Esempio n. 1
0
    def interpolate(self, factor, minLayer, maxLayer, round=True, suppressError=True):
        """
        Interpolate all possible data in the layer. ::

            >>> layer.interpolate(0.5, otherLayer1, otherLayer2)
            >>> layer.interpolate((0.5, 2.0), otherLayer1, otherLayer2, round=False)

        The interpolation occurs on a 0 to 1.0 range where **minLayer**
        is located at 0 and **maxLayer** is located at 1.0. **factor**
        is the interpolation value. It may be less than 0 and greater
        than 1.0. It may be a :ref:`type-int-float` or a tuple of
        two :ref:`type-int-float`. If it is a tuple, the first
        number indicates the x factor and the second number indicates
        the y factor. **round** indicates if the result should be
        rounded to integers. **suppressError** indicates if incompatible
        data should be ignored or if an error should be raised when
        such incompatibilities are found.
        """
        factor = normalizers.normalizeInterpolationFactor(factor)
        if not isinstance(minLayer, BaseLayer):
            raise FontPartsError("Interpolation to an instance of %r can not be performed from an instance of %r." % (self.__class__.__name__, minLayer.__class__.__name__))
        if not isinstance(maxLayer, BaseLayer):
            raise FontPartsError("Interpolation to an instance of %r can not be performed from an instance of %r." % (self.__class__.__name__, maxLayer.__class__.__name__))
        round = normalizers.normalizeBoolean(round)
        suppressError = normalizers.normalizeBoolean(suppressError)
        self._interpolate(factor, minLayer, maxLayer, round=round, suppressError=suppressError)
Esempio n. 2
0
    def interpolate(self, factor, minInfo, maxInfo, round=True, suppressError=True):
        """
        Interpolate all pairs between minInfo and maxInfo.
        The interpolation occurs on a 0 to 1.0 range where minInfo
        is located at 0 and maxInfo is located at 1.0.

        factor is the interpolation value. It may be less than 0
        and greater than 1.0. It may be a number (integer, float)
        or a tuple of two numbers. If it is a tuple, the first
        number indicates the x factor and the second number
        indicates the y factor.

        round indicates if the result should be rounded to integers.

        suppressError indicates if incompatible data should be ignored
        or if an error should be raised when such incompatibilities are found.
        """
        factor = normalizers.normalizeInterpolationFactor(factor)
        if not isinstance(minInfo, BaseInfo):
            raise FontPartsError("Interpolation to an instance of %r can not be performed from an instance of %r." % (self.__class__.__name__, minInfo.__class__.__name__))
        if not isinstance(maxInfo, BaseInfo):
            raise FontPartsError("Interpolation to an instance of %r can not be performed from an instance of %r." % (self.__class__.__name__, maxInfo.__class__.__name__))
        round = normalizers.normalizeBoolean(round)
        suppressError = normalizers.normalizeBoolean(suppressError)
        self._interpolate(factor, minInfo, maxInfo, round=round, suppressError=suppressError)
Esempio n. 3
0
 def _set_bcpOut(self, value):
     """
     Subclasses may override this method.
     """
     x, y = absoluteBCPOut(self.anchor, value)
     segment = self._segment
     nextSegment = self._nextSegment
     if nextSegment.type == "move" and value != (0, 0):
         raise FontPartsError(("Cannot set the bcpOut for the last "
                               "point in an open contour."))
     else:
         offCurves = nextSegment.offCurve
         if offCurves:
             # if the off curves are located at the anchor coordinates
             # we can switch to a "line" segment type
             if value == (0, 0) and self.bcpIn == (0, 0):
                 segment.type = "line"
                 segment.smooth = False
             else:
                 offCurves[0].x = x
                 offCurves[0].y = y
         elif value != (0, 0):
             nextSegment.type = "curve"
             offCurves = nextSegment.offCurve
             offCurves[0].x = x
             offCurves[0].y = y
Esempio n. 4
0
 def _set_type(self, newType):
     """
     Subclasses may override this method.
     """
     oldType = self.type
     if oldType == newType:
         return
     contour = self.contour
     if contour is None:
         raise FontPartsError("The segment does not belong to a contour.")
     # converting line <-> move
     if newType in ("move", "line") and oldType in ("move", "line"):
         pass
     # converting to a move or line
     elif newType not in ("curve", "qcurve"):
         offCurves = self.offCurve
         for point in offCurves:
             contour.removePoint(point)
     # converting a line/move to a curve/qcurve
     else:
         segments = contour.segments
         i = segments.index(self)
         prev = segments[i - 1].onCurve
         on = self.onCurve
         x = on.x
         y = on.y
         points = contour.points
         i = points.index(on)
         contour.insertPoint(i, (x, y), "offcurve")
         off2 = contour.points[i]
         contour.insertPoint(i, (prev.x, prev.y), "offcurve")
         off1 = contour.points[i]
         del self._points
         self._setPoints((off1, off2, on))
     self.onCurve.type = newType
Esempio n. 5
0
 def _set_bcpIn(self, value):
     """
     Subclasses may override this method.
     """
     x, y = absoluteBCPIn(self.anchor, value)
     segment = self._segment
     if segment.type == "move" and value != (0, 0):
         raise FontPartsError(
             "Cannot set the bcpIn for the first point in an open contour.")
     else:
         offCurves = segment.offCurve
         if offCurves:
             # if the two off curves are located at the anchor
             # coordinates we can switch to a line segment type.
             if value == (0, 0) and self.bcpOut == (0, 0):
                 segment.type = "line"
                 segment.smooth = False
             else:
                 offCurves[-1].x = x
                 offCurves[-1].y = y
         elif value != (0, 0):
             segment.type = "curve"
             offCurves = segment.offCurve
             offCurves[-1].x = x
             offCurves[-1].y = y
Esempio n. 6
0
 def _getitem__points(self, index):
     index = normalizers.normalizeIndex(index)
     if index >= self._len__points():
         raise FontPartsError("No point located at index %d." % index)
     point = self._getPoint(index)
     self._setContourInPoint(point)
     return point
Esempio n. 7
0
 def _loadFromGLIF(self, glifData):
     try:
         readGlyphFromString(aString=glifData,
                             glyphObject=self.naked(),
                             pointPen=self.getPointPen())
     except GlifLibError:
         raise FontPartsError("Not valid glif data")
Esempio n. 8
0
 def _getitem__guidelines(self, index):
     index = normalizers.normalizeGuidelineIndex(index)
     if index >= self._len__guidelines():
         raise FontPartsError("No guideline located at index %d." % index)
     guideline = self._getGuideline(index)
     self._setFontInGuideline(guideline)
     return guideline
Esempio n. 9
0
    def generate(self, format, path=None, **kwargs):
        """
        Generate the font to another format.

            >>> font.generate("otfcff")
            >>> font.generate("otfcff", "/path/to/my/font.otf")

        **format** defines the file format to output. These are the
        standard format identifiers:

        %s

        Environments are not required to support all of these
        and environments may define their own format types.
        **path** defines the location where the new file should
        be created. If a file already exists at that location,
        it will be overwritten by the new file. If **path** defines
        a directory, the file will be output as the current
        file name, with the appropriate suffix for the format,
        into the given directory. If no **path** is given, the
        file will be output into the same directory as the source
        font with the file named with the current file name,
        with the appropriate suffix for the format.
        """

        if format is None:
            raise FontPartsError("The format must be defined when generating.")
        elif not isinstance(format, basestring):
            raise FontPartsError("The format must be defined as a string.")
        ext = self.generateFormatToExtension(format, "." + format)
        if path is None and self.path is None:
            raise FontPartsError(
                "The file cannot be generated because an output path was not defined."
            )
        elif path is None:
            path = os.path.splitext(self.path)[0]
            path += ext
        elif os.path.isdir(path):
            if self.path is None:
                raise FontPartsError(
                    "The file cannot be generated because the file does not have a path."
                )
            fileName = os.path.basename(self.path)
            fileName += ext
            path = os.path.join(path, fileName)
        path = normalizers.normalizeFilePath(path)
        return self._generate(format=format, path=path, **kwargs)
Esempio n. 10
0
 def _set_base_name(self, value):
     if value == self.name:
         return
     value = normalizers.normalizeLayerName(value)
     existing = self.font.layerOrder
     if value in existing:
         raise FontPartsError("A layer with the name '%s' already exists." % value)
     self._set_name(value)
Esempio n. 11
0
 def decompose(self):
     """
     Decompose the component.
     """
     glyph = self.glyph
     if glyph is None:
         raise FontPartsError("The component does not belong to a glyph.")
     self._decompose()
Esempio n. 12
0
 def isCompatible(self, other, cls):
     """
     Evaluate interpolation compatibility with other.
     """
     if not isinstance(other, cls):
         raise FontPartsError("Compatibility between an instance of %r and an instance of %r can not be checked." % (cls.__name__, other.__class__.__name__))
     reporter = self.compatibilityReporterClass(self, other)
     self._isCompatible(other, reporter)
     return not reporter.fatal, reporter
Esempio n. 13
0
    def removeGlyph(self, name):
        """
        Remove the glyph with name from the layer. ::

            >>> layer.removeGlyph("A")
        """
        name = normalizers.normalizeGlyphName(name)
        if name not in self:
            raise FontPartsError("No glyph with the name '%s' exists." % name)
        self._removeGlyph(name)
Esempio n. 14
0
    def interpolate(self,
                    factor,
                    minKerning,
                    maxKerning,
                    round=True,
                    suppressError=True):
        """
        Interpolates all pairs between two :class:`BaseKerning` objects:

        **minKerning** and **maxKerning**. The interpolation occurs on a
        0 to 1.0 range where **minKerning** is located at 0 and
        **maxKerning** is located at 1.0. The kerning data is replaced by
        the interpolated kerning.

        * **factor** is the interpolation value. It may be less than 0
          and greater than 1.0. It may be an :ref:`type-int-float`,
          ``tuple`` or ``list``. If it is a ``tuple`` or ``list``,
          the first number indicates the x factor and the second number
          indicates the y factor.
        * **round** is a ``bool`` indicating if the result should be rounded to
          ``int``\s. The default behavior is to round interpolated kerning.
        * **suppressError** is a ``bool`` indicating if incompatible data should
          be ignored or if an error should be raised when such incompatibilities
          are found. The default behavior is to ignore incompatible data.

            >>> myKerning.interpolate(kerningOne, kerningTwo)
        """
        factor = normalizers.normalizeInterpolationFactor(factor)
        if not isinstance(minKerning, BaseKerning):
            raise FontPartsError(
                "Interpolation to an instance of %r can not be performed from an instance of %r."
                % (self.__class__.__name__, minKerning.__class__.__name__))
        if not isinstance(maxKerning, BaseKerning):
            raise FontPartsError(
                "Interpolation to an instance of %r can not be performed from an instance of %r."
                % (self.__class__.__name__, maxKerning.__class__.__name__))
        round = normalizers.normalizeBoolean(round)
        suppressError = normalizers.normalizeBoolean(suppressError)
        self._interpolate(factor,
                          minKerning,
                          maxKerning,
                          round=round,
                          suppressError=suppressError)
Esempio n. 15
0
 def __get__(self, obj, cls):
     getter = getattr(obj, self.getterName, None)
     if getter is not None:
         return getter()
     else:
         # obj is None when the property is accessed
         # via the class instead of an instance
         if obj is None:
             return self
         raise FontPartsError("no getter for %r" % self.name)
Esempio n. 16
0
 def removeSegment(self, segment, **kwargs):
     """
     Remove segment from the contour.
     """
     if not isinstance(segment, int):
         segment = self.segments.index(segment)
     segment = normalizers.normalizeIndex(segment)
     if segment >= self._len__segments():
         raise FontPartsError("No segment located at index %d." % segment)
     self._removeSegment(segment, **kwargs)
Esempio n. 17
0
    def removeLayer(self, name):
        """
        Remove the layer with **name** from the font.

            >>> font.removeLayer("My Layer 3")
        """
        name = normalizers.normalizeLayerName(name)
        if name not in self.layerOrder:
            raise FontPartsError("No layer with the name '%s' exists." % name)
        self._removeLayer(name)
Esempio n. 18
0
    def round(self, multiple=1):
        """
        Rounds the kerning values to increments of **multiple**,
        which will be an ``int``.

        The default behavior is to round to increments of 1.
        """
        if not isinstance(multiple, int):
            raise FontPartsError("The round multiple must be an int not %s." %
                                 multiple.__class__.__name__)
        self._round(multiple)
Esempio n. 19
0
 def removePoint(self, point, **kwargs):
     """
     Remove the point from the contour.
     point can be a point object or an index.
     """
     if not isinstance(point, int):
         point = self.points.index(point)
     point = normalizers.normalizeIndex(point)
     if point >= self._len__points():
         raise FontPartsError("No point located at index %d." % point)
     self._removePoint(point, **kwargs)
Esempio n. 20
0
 def removeBPoint(self, bPoint, **kwargs):
     """
     Remove the bpoint from the contour.
     bpoint can be a point object or an index.
     """
     if not isinstance(bPoint, int):
         bPoint = bPoint.index
     bPoint = normalizers.normalizeIndex(bPoint)
     if bPoint >= self._len__points():
         raise FontPartsError("No bPoint located at index %d." % bPoint)
     self._removeBPoint(bPoint, **kwargs)
Esempio n. 21
0
 def _set_base_index(self, value):
     glyph = self.glyph
     if glyph is None:
         raise FontPartsError("The contour does not belong to a glyph.")
     value = normalizers.normalizeIndex(value)
     contourCount = len(glyph.contours)
     if value < 0:
         value = -(value % contourCount)
     if value >= contourCount:
         value = contourCount
     self._set_index(value)
Esempio n. 22
0
    def __getitem__(self, name):
        """
        Get the :class:`BaseGlyph` with name from the layer. ::

            >>> glyph = layer["A"]
        """
        name = normalizers.normalizeGlyphName(name)
        if name not in self:
            raise FontPartsError("No glyph named '%s'." % name)
        glyph = self._getItem(name)
        self._setLayerInGlyph(glyph)
        return glyph
Esempio n. 23
0
    def getLayer(self, name):
        """
        Get the :class:`BaseLayer` with **name**.

            >>> layer = font.getLayer("My Layer 2")
        """
        name = normalizers.normalizeLayerName(name)
        if name not in self.layerOrder:
            raise FontPartsError("No layer with the name '%s' exists." % name)

        layer = self._getLayer(name)
        self._setFontInLayer(layer)
        return layer
Esempio n. 24
0
 def _get_type(self):
     """
     Subclasses may override this method.
     """
     point = self._point
     typ = point.type
     if typ == "curve" and point.smooth:
         bType = "curve"
     elif typ in ("move", "line", "curve"):
         bType = "corner"
     else:
         raise FontPartsError(
             "A %s point can not be converted to a bPoint." % typ)
     return bType
Esempio n. 25
0
 def _interpolate(self, factor, minInfo, maxInfo, round=True, suppressError=True):
     """
     Subclasses may override this method.
     """
     minInfo = minInfo._toMathInfo()
     maxInfo = maxInfo._toMathInfo()
     result = interpolate(minInfo, maxInfo, factor)
     if result is None and not suppressError:
         raise FontPartsError(("Info from font '%s' and font '%s' could not be "
                               "interpolated.")
                              % (minInfo.font.name, maxInfo.font.name))
     if round:
         result = result.round()
     self._fromMathInfo(result)
Esempio n. 26
0
    def save(self, path=None, showProgress=False, formatVersion=None):
        """
        Save the font to **path**.

            >>> font.save()
            >>> font.save("/path/to/my/font-2.ufo")

        If **path** is None, use the font's original location.
        The file type must be inferred from the file extension
        of the given path. If no file extension is given, the
        environment may fall back to the format of its choice.
        **showProgress** indicates if a progress indicator should
        be displayed during the operation. Environments may or may
        not implement this behavior. **formatVersion** indicates
        the format version that should be used for writing the given
        file type. For example, if 2 is given for formatVersion
        and the file type being written if UFO, the file is to
        be written in UFO 2 format. This value is not limited
        to UFO format versions. If no format version is given,
        the original format version of the file should be preserved.
        If there is no original format version it is implied that
        the format version is the latest version for the file
        type as supported by the environment.

        .. note::

           Environments may define their own rules governing when
           a file should be saved into its original location and
           when it should not. For example, a font opened from a
           compiled OpenType font may not be written back into
           the original OpenType font.
        """
        if path is None and self.path is None:
            raise FontPartsError(
                "The font cannot be saved because no file location has been given."
            )
        if path is not None:
            path = normalizers.normalizeFilePath(path)
        showProgress = bool(showProgress)
        if formatVersion is not None:
            formatVersion = normalizers.normalizeFileFormatVersion(
                formatVersion)
        self._save(path=path,
                   showProgress=showProgress,
                   formatVersion=formatVersion)
Esempio n. 27
0
    def removeGuideline(self, guideline):
        """
        Remove **guideline** from the font.

            >>> font.removeGuideline(guideline)
            >>> font.removeGuideline(2)

        **guideline** can be a guideline object or
        an integer representing the guideline index.
        """
        if isinstance(guideline, int):
            index = guideline
        else:
            index = self._getGuidelineIndex(guideline)
        index = normalizers.normalizeGuidelineIndex(index)
        if index >= self._len__guidelines():
            raise FontPartsError("No guideline located at index %d." % index)
        self._removeGuideline(index)
Esempio n. 28
0
 def setStartSegment(self, segment, **kwargs):
     """
     Set the first segment on the contour.
     segment can be a segment object or an index.
     """
     segments = self.segments
     if not isinstance(segment, int):
         segmentIndex = segments.index(segment)
     else:
         segmentIndex = segment
     if len(self.segments) < 2:
         return
     if segmentIndex == 0:
         return
     if segmentIndex >= len(segments):
         raise FontPartsError(
             "The contour does not contain a segment at index %d" %
             segmentIndex)
     self._setStartSegment(segmentIndex, **kwargs)
Esempio n. 29
0
    def _interpolate(self,
                     factor,
                     minInfo,
                     maxInfo,
                     round=True,
                     suppressError=True):
        """
        Subclasses may override this method.
        """
        from fontMath.mathFunctions import setRoundIntegerFunction

        setRoundIntegerFunction(normalizers.normalizeVisualRounding)

        minInfo = minInfo._toMathInfo()
        maxInfo = maxInfo._toMathInfo()
        result = interpolate(minInfo, maxInfo, factor)
        if result is None and not suppressError:
            raise FontPartsError(
                ("Info from font '%s' and font '%s' could not be "
                 "interpolated.") % (minInfo.font.name, maxInfo.font.name))
        if round:
            result = result.round()
        self._fromMathInfo(result)
Esempio n. 30
0
    def _get_type(self):
        """
        Subclasses may override this method.
        """
        point = self._point
        typ = point.type
        bType = None
        if point.smooth:
            if typ == "curve":
                bType = "curve"
            elif typ == "line":
                nextSegment = self._nextSegment
                if nextSegment is not None and nextSegment.type == "curve":
                    bType = "curve"
                else:
                    bType = "corner"
        elif typ in ("move", "line", "curve"):
            bType = "corner"

        if bType is None:
            raise FontPartsError("A %s point can not be converted to a bPoint."
                                         % typ)
        return bType