def validateDesignspaceDoc(dsDoc, dsoptions, **kwArgs): """ Validate the dsDoc DesignSpaceDocument object, using supplied dsoptions and kwArgs. Raises Exceptions if certain criteria are not met. These are above and beyond the basic validations in fontTools.designspaceLib and are specific to makeinstancesufo. """ if dsDoc.sources: for src in dsDoc.sources: if not os.path.exists(src.path): raise DesignSpaceDocumentError( f"Source file {src.path} does not exist") else: raise DesignSpaceDocumentError("Designspace file contains no sources.") if dsDoc.instances: if dsoptions.indexList: # bounds check on indexList maxinstidx = max(dsoptions.indexList) if maxinstidx >= len(dsDoc.instances): raise IndexError(f"Instance index {maxinstidx} out-of-range") else: raise DesignSpaceDocumentError( "Designspace file contains no instances.") for i, inst in enumerate(dsDoc.instances): if dsoptions.indexList and i not in dsoptions.indexList: continue for attr_name in ('familyName', 'postScriptFontName', 'styleName'): if getattr(inst, attr_name, None) is None: logger.warning( f"Instance at index {i} has no '{attr_name}' attribute.") if inst.path is None: raise DesignSpaceDocumentError( f"Instance at index {i} has no 'filename' attribute.")
def validateDesignspaceDoc(dsDoc, dsoptions, **kwArgs): """ Validate the dsDoc DesignSpaceDocument object, using supplied dsoptions and kwArgs. Raises Exceptions if certain criteria are not met. These are above and beyond the basic validations in fontTools.designspaceLib and are specific to makeinstancesufo. """ if dsDoc.sources: for src in dsDoc.sources: if not os.path.exists(src.path): raise DesignSpaceDocumentError( "Source file {} does not exist".format(src.path, )) else: raise DesignSpaceDocumentError("Designspace file contains no sources.") if dsDoc.instances: if dsoptions.indexList: # bounds check on indexList maxinstidx = max(dsoptions.indexList) if maxinstidx >= len(dsDoc.instances): raise IndexError("Instance index {} out-of-range".format( maxinstidx, )) else: raise DesignSpaceDocumentError( "Designspace file contains no instances.")
def validateDesignspaceDoc(dsDoc, dsoptions, **kwArgs): """ Validate the dsDoc DesignSpaceDocument object, using supplied dsoptions and kwArgs. Raises Exceptions if certain criteria are not met. These are above and beyond the basic validations in fontTools.designspaceLib and are specific to makeinstancesufo. """ if dsDoc.sources: for src in dsDoc.sources: if not os.path.exists(src.path): raise DesignSpaceDocumentError( f"Source file {src.path} does not exist") else: raise DesignSpaceDocumentError("Designspace file contains no sources.") if dsDoc.instances: if dsoptions.indexList: # bounds check on indexList maxinstidx = max(dsoptions.indexList) if maxinstidx >= len(dsDoc.instances): raise IndexError(f"Instance index {maxinstidx} out-of-range") else: raise DesignSpaceDocumentError( "Designspace file contains no instances.") if dsoptions.useVarlib: axis_map = dict() for axis in dsDoc.axes: ad = axis.asdict() axis_map[ad['name']] = axis for i, inst in enumerate(dsDoc.instances): if dsoptions.indexList and i not in dsoptions.indexList: continue for attr_name in ('familyname', 'postscriptfontname', 'stylename'): if getattr(inst, attr_name, None) is None: logger.warning( f"Instance at index {i} has no '{attr_name}' attribute.") if inst.path is None: raise DesignSpaceDocumentError( f"Instance at index {i} has no 'filename' attribute.") if dsoptions.useVarlib: # check for extrapolation, which is not supported by varLib # for these only warn, do not raise exception for dim, val in inst.location.items(): axis = axis_map[dim] mval = axis.map_backward(val) if mval < axis.minimum or mval > axis.maximum: logger.warning("Extrapolation is not supported with varlib" f" ({inst.familyName} {inst.styleName} " f"{dim}: {val})")
def validateDesignspaceDoc(dsDoc, **kwArgs): """ Validate the dsDoc DesignSpaceDocument object. Raises Exceptions if certain criteria are not met. These are above and beyond the basic validations in fontTools.designspaceLib and are specific to buildmasterotfs. """ if dsDoc.sources: for src in dsDoc.sources: if not os.path.exists(src.path): raise DesignSpaceDocumentError( f"Source file {src.path} does not exist") else: raise DesignSpaceDocumentError("Designspace file contains no sources.")
def buildVFStatTable(ttFont: TTFont, doc: DesignSpaceDocument, vfName: str) -> None: """Build the STAT table for the variable font identified by its name in the given document. Knowing which variable we're building STAT data for is needed to subset the STAT locations to only include what the variable font actually ships. .. versionadded:: 5.0 .. seealso:: - :func:`getStatAxes()` - :func:`getStatLocations()` - :func:`fontTools.otlLib.builder.buildStatTable()` """ for vf in doc.getVariableFonts(): if vf.name == vfName: break else: raise DesignSpaceDocumentError( f"Cannot find the variable font by name {vfName}") region = getVFUserRegion(doc, vf) return fontTools.otlLib.builder.buildStatTable( ttFont, getStatAxes(doc, region), getStatLocations(doc, region), doc.elidedFallbackName if doc.elidedFallbackName is not None else 2, )
def getVFUserRegion(doc: DesignSpaceDocument, vf: VariableFontDescriptor) -> Region: vfUserRegion: Region = {} # For each axis, 2 cases: # - it has a range = it's an axis in the VF DS # - it's a single location = use it to know which rules should apply in the VF for axisSubset in vf.axisSubsets: axis = doc.getAxis(axisSubset.name) if axis is None: raise DesignSpaceDocumentError( f"Cannot find axis named '{axisSubset.name}' for variable font '{vf.name}'." ) if hasattr(axisSubset, "userMinimum"): # Mypy doesn't support narrowing union types via hasattr() # TODO(Python 3.10): use TypeGuard # https://mypy.readthedocs.io/en/stable/type_narrowing.html axisSubset = cast(RangeAxisSubsetDescriptor, axisSubset) if not hasattr(axis, "minimum"): raise DesignSpaceDocumentError( f"Cannot select a range over '{axis.name}' for variable font '{vf.name}' " "because it's a discrete axis, use only 'userValue' instead." ) axis = cast(AxisDescriptor, axis) vfUserRegion[axis.name] = Range( max(axisSubset.userMinimum, axis.minimum), min(axisSubset.userMaximum, axis.maximum), axisSubset.userDefault or axis.default, ) else: axisSubset = cast(ValueAxisSubsetDescriptor, axisSubset) vfUserRegion[axis.name] = axisSubset.userValue # Any axis not mentioned explicitly has a single location = default value for axis in doc.axes: if axis.name not in vfUserRegion: assert isinstance( axis.default, (int, float)), f"Axis '{axis.name}' has no valid default value." vfUserRegion[axis.name] = axis.default return vfUserRegion
def userRegionToDesignRegion(doc: DesignSpaceDocument, userRegion: Region) -> Region: designRegion = {} for name, value in userRegion.items(): axis = doc.getAxis(name) if axis is None: raise DesignSpaceDocumentError( f"Cannot find axis named '{name}' for region.") if isinstance(value, (float, int)): designRegion[name] = axis.map_forward(value) else: designRegion[name] = Range( axis.map_forward(value.minimum), axis.map_forward(value.maximum), axis.map_forward(value.default), ) return designRegion