Exemple #1
0
    def path2ScaledImagePath(self,
                             path,
                             w,
                             h,
                             index=None,
                             exportExtension=None):
        """Answers the path to the scaled image.

        >>> context = DrawBotContext()
        >>> context.path2ScaledImagePath('/xxx/yyy/zzz/This.Is.An.Image.jpg', 110, 120)
        ('/xxx/yyy/zzz/_scaled/', 'This.Is.An.Image.110x120.0.jpg')

        >>> path, fileName = context.path2ScaledImagePath('/xxx/yyy/zzz/This.Is.An.Image.jpg', 110, 120)
        >>> path in ('/xxx/yyy/zzz/scaled/', '/xxx/yyy/zzz/_scaled/')
        True
        >>> fileName
        'This.Is.An.Image.110x120.0.jpg'
        """
        # /_scaled will be ignored with default .gitignore settings.
        # If docs/images/_scaled need to be committed into Git repo,
        # then remove _scaled from .gitignore.
        cachePath = '%s/%s/' % (path2Dir(path), self.SCALED_PATH)
        fileNameParts = path2Name(path).split('.')
        if not exportExtension:  # If undefined, take the original extension for exporting the cache.
            exportExtension = fileNameParts[-1].lower()
        cachedFileName = '%s.%dx%d.%d.%s' % ('.'.join(
            fileNameParts[:-1]), w, h, index or 0, exportExtension)
        return cachePath, cachedFileName
Exemple #2
0
    def _get_parametricAxisFonts(self):
        """Generate the dictionary with parametric axis fonts. Key is the parametric axis name,
        value is the font instance (there can only be one font per axis). If the fonts don't
        exist as cached files, they are created. The font currently under self.defaultFont is
        used a neutral, for which all delta's are 0."""
        origin = self.defaultFont
        if origin is None:
            return None

        # Create directory for the parametric axis fonts, if it does not exist.
        paFontDir = path2ParentPath(origin.path) + '/_export/@axes'
        if not os.path.exists(paFontDir):
            os.makedirs(paFontDir)
        for axisName in self.PARAMETRIC_AXES:
            self._parametricAxisFonts[axisName] = []
            for extreme in ('_min', '_max'):
                fileNameParts = path2Name(origin.path).split('.')
                paFontPath = paFontDir + '/' + '.'.join(
                    fileNameParts[:-1]
                ) + '@' + axisName + extreme + '.' + fileNameParts[-1]
                if os.path.exists(paFontPath):
                    os.remove(paFontPath)
                shutil.copy(origin.path, paFontPath)
                self._parametricAxisFonts[axisName].append(Font(paFontPath))
            self._parametricAxisMetrics[axisName] = origin.analyzer.stems
        return self._parametricAxisFonts
Exemple #3
0
 def addFont(self, font, fontKey=None, fontStyle=None):
     if fontKey is None:
         fontKey = path2Name(font.path) # This must be unique in the family, used as key in self.fonts.
     assert fontKey not in self.fonts, ('Font "%s" already in family "%s"' % (fontKey, self.fonts.keys()))
     if fontStyle is None:
         fontStyle = font.info.styleName # It is allowed to have multiple fonts with the same style name.
     # Store the font under unique fontKey.
     self.fonts[fontKey] = font
     # Create list entry for fontStyle, if it does not exist.
     if not fontStyle in self.fontStyles:
         self.fontStyles[fontStyle] = [] # Keep list, there may be fonts with the same style name.
     self.fontStyles[fontStyle].append(font)
Exemple #4
0
    def path2ScaledImagePath(self, path, w, h, index=None, exportExtension=None):
        """Answers the path to the scaled image.

        >>> context = PdfLibContext()
        >>> context.path2ScaledImagePath('/xxx/yyy/zzz/This.Is.An.Image.jpg', 110, 120)
        ('/xxx/yyy/zzz/_scaled/', 'This.Is.An.Image.110x120.0.jpg')
        """
        cachePath = '%s/_scaled/' % path2Dir(path)
        fileNameParts = path2Name(path).split('.')
        if not exportExtension: # If undefined, take the original extension for exporting the cache.
            exportExtension = fileNameParts[-1].lower()
        cachedFileName = '%s.%dx%d.%d.%s' % ('.'.join(fileNameParts[:-1]), w, h, index or 0, exportExtension)
        return cachePath, cachedFileName
def drawFontLabel(p, varFamily, f, fIndex=None, fAxis=None):
    x, y = p
    print f.info.styleName, f.info.weightClass, f.info.widthClass

    glyphH = f[GLYPH]
    if not glyphH.width:
        print glyphH, 'No width'
        return

    s = 0.05 * 1000 / f.info.unitsPerEm
    leading = 2048 / f.info.unitsPerEm
    stroke(None)
    fill(0)
    save()
    translate(x - glyphH.width / 2 * s, y - leading - 50)
    scale(s)
    drawPath(glyphH.path)
    restore()
    y -= leading + 50

    save()
    pathLabel = '-'.join(path2Name(f.path).split('-')[1:])
    #label = path2Name(f.path)
    if fAxis is not None:
        label = '@' + fAxis
    elif fIndex is None:
        label = ''
    else:
        label = '#%d ' % fIndex
    label += '%s\n(%s)\n%d' % (pathLabel.replace('.ttf', '').replace(
        '_', '\n').replace('-', '\n'), f.info.styleName, f.info.weightClass)
    fs = FormattedString(label, fontSize=10, align='center')
    tw, th = textSize(fs)
    text(fs, (x - tw / 2, y - 14))
    restore()
    y -= leading + th - 22

    # Draw marker on actual position of H.stem and H.weight as green dot
    stemValues = f.analyzer.stems.keys()
    if stemValues:  # Cannot find H-stem, skip this marker
        stem = min(stemValues)
        # XOPQ (counter) + H.stem == H.width - H.stem - H.lsb - H.rsb
        width = glyphH.width - stem - glyphH.leftMargin - glyphH.rightMargin

        c.fill((0, 0.5, 0))
        c.stroke(None)
        R = 16
        weightLoc, widthLoc = stem, width / 2
        c.oval(weightLoc - R / 2, widthLoc - R / 2, R, R)
        if fAxis is not None:
            label = '@' + fAxis
        elif fIndex is None:
            label = ''
        else:
            label = '#%d\n' % fIndex
        bs = c.newString(label + ('S:%d\nW:%d\n%d' %
                                  (weightLoc, widthLoc, f.info.weightClass)),
                         style=dict(fontSize=10,
                                    xTextAlign='center',
                                    textFill=0))
        tw, th = c.textSize(bs)
        c.text(bs, (weightLoc - tw / 2, widthLoc - 24))

        if varFamily.originFont is f:
            # If one of these is the guessed origin font, then draw marker
            c.fill(None)
            c.stroke((0, 0.5, 0), 2)  # Stroke color and width
            R = 23
            c.oval(weightLoc - R / 2, widthLoc - R / 2, R, R)

    else:
        pass
def guessVarFamilyFromPaths(basePath, name=None):
    u"""Initialize by guessing the self._font axis locations. 
    """
    paths = findFontPaths(basePath)
    name = name or path2Name(basePath)
    return VarFamily(name, paths)
Exemple #7
0
    def checkOutlineCompatibility(self, refFont, font, glyphName):

        report = []

        if refFont is None or not glyphName in refFont:
            report.append('Glyph "%s" not in reference master "%s"' %
                          (glyphName, path2Name(refFont.path)))

        if font is None or not glyphName in font:
            report.append('Glyph "%s" not in master "%s"' %
                          (glyphName, path2Name(font.path)))

        if report:  # Errors here already, then they are fatal. No need for firther checking.
            return report

        rg = refFont[glyphName]
        g = font[glyphName]

        # Compare number of contours
        if len(g) > len(rg):
            report.append(
                u'Glyph "%s" has more contours (%d) in "%s" than in "%s" (%d).'
                % (glyphName, len(g), refFont.info.styleName,
                   font.info.styleName, len(rg)))
        elif len(g) < len(rg):
            report.append(
                u'Glyph "%s" has fewer contours (%d) in "%s" than in "%s" (%d).'
                % (glyphName, len(g), refFont.info.styleName,
                   font.info.styleName, len(rg)))
        else:  # Same amount of contours, we can compare them.
            for cIndex, contour in enumerate(g.contours):
                rContour = rg.contours[cIndex]
                if len(contour) > len(rContour):
                    report.append(
                        u'Glyph "%s" contour #%d has more points (%d) than “%s” (%d).'
                        % (glyphName, cIndex, len(contour),
                           refFont.info.styleName, len(rContour)))
                elif len(contour) < len(rContour):
                    report.append(
                        u'Glyph "%s" contour #%d has fewer points (%d) than “%s” (%d).'
                        % (glyphName, cIndex, len(contour),
                           refFont.info.styleName, len(rContour)))
                else:  # Contour lengths are equal, now we can test on point types.
                    # Test on contour clockwise directions.
                    if g.isClockwise(contour) != rg.isClockwise(rContour):
                        report.append(
                            u'Glyph "%s" contour #%d has reversed direction of “%s”.'
                            % (glyphName, cIndex, refFont.info.styleName))
                    else:  # Now the length and directions are the same, we can test on the type of points.
                        pIndex = 0
                        for p in contour:
                            if p.onCurve != rContour[pIndex].onCurve:
                                report.append(
                                    u'"%s" Glyph "%s" point #%d (%d,%d,%s) is not same type as point #%d (%d,%d,%s) in “%s”.'
                                    % (font.info.styleName, glyphName, pIndex,
                                       p.x, p.y, p.onCurve, pIndex,
                                       rContour[pIndex].x, rContour[pIndex].y,
                                       rContour[pIndex].onCurve,
                                       refFont.info.styleName))
                            pIndex += 1

        components = g.components
        rComponents = rg.components

        if len(components) > len(rComponents):
            baseGlyphs = []
            for component in components:
                baseName = component.baseGlyph
                if not baseName in font:
                    baseName += self.DOES_NOT_EXIST
                baseGlyphs.append(baseName)
            rBaseGlyphs = []
            for component in rComponents:
                baseName = component.baseGlyph
                if not baseName in refFont:
                    baseName += self.DOES_NOT_EXIST
                rBaseGlyphs.append(baseName)
            report.append(
                u'Glyph "%s" has %d more components (%s) than %s (%s) in "%s".'
                % (glyphName, len(baseGlyphs) - len(rBaseGlyphs),
                   ','.join(baseGlyphs), refFont.info.styleName,
                   ','.join(rBaseGlyphs), refFont.info.styleName))
        elif len(components) < len(rComponents):
            baseGlyphs = []
            for component in components:
                baseName = component.baseGlyph
                if not baseName in font:
                    baseName += self.DOES_NOT_EXIST
                baseGlyphs.append(baseName)
            rBaseGlyphs = []
            for component in rComponents:
                baseName = component.baseGlyph
                if not baseName in refFont:
                    baseName += self.DOES_NOT_EXIST
                rBaseGlyphs.append(baseName)
            report.append(
                u'Glyph "%s" has %d fewer components (%d) than %s (%s) in "%s".'
                % (glyphName, len(rBaseGlyphs) - len(baseGlyphs),
                   len(components), refFont.info.styleName, len(rComponents),
                   refFont.info.styleName))
        return report
Exemple #8
0
def drawFontLabel(p, varFamily, f, fIndex=None, fAxis=None):
    x, y = p
    #print f.info.styleName, f.info.weightClass, f.info.widthClass

    if not GLYPH in f:
        print('###', GLYPH, 'not in font', f.path)
        return

    glyphH = f[GLYPH]
    if not glyphH.width:
        print('###', GLYPH, 'No width', f.path)
        return

    stems = glyphH.analyzer.stems
    # Draw marker on actual position of H.stem and H.weight as green dot
    stemValues = stems.keys()

    s = 0.05 * 1000 / f.info.unitsPerEm
    leading = 2048 / f.info.unitsPerEm
    c.stroke(noColor)
    c.fill(Color(0))
    save()
    translate(x - glyphH.width / 2 * s, y - leading - 50)
    scale(s)
    drawPath(glyphH.path)
    restore()
    y -= leading + 50

    save()
    pathLabel = '-'.join(path2Name(f.path).split('-')[1:])
    #label = path2Name(f.path)
    if fAxis is not None:
        label = '@' + fAxis
    elif fIndex is None:
        label = ''
    else:
        label = '#%d ' % fIndex
    label += '%s\n(%s)\n%d' % (pathLabel.replace('.ttf', '').replace(
        '_', '\n').replace('-', '\n'), f.info.styleName, f.info.weightClass)
    fs = FormattedString(label, style=dict(fontSize=10, align=CENTER))
    tw, th = textSize(fs)
    text(fs, (x - tw / 2, y - 14))
    restore()
    y -= leading + th - 22

    if stemValues:  # Cannot find H-stem, skip this marker
        stem = min(stemValues)
        stemS = '%d' % stem
        normalizedStemS = '%0.2f' % (stem * 1000 / f.info.unitsPerEm)

        # XOPQ (counter) + H.stem == H.width - H.stem - H.lsb - H.rsb
        width = glyphH.width - stem - glyphH.leftMargin - glyphH.rightMargin

        c.fill(Color(0, 0.5, 0))
        c.stroke(noColor)
        R = 16
        weightLoc, widthLoc = stem, width / 2
        c.oval(weightLoc - R / 2, widthLoc - R / 2, R, R)
        if fAxis is not None:
            label = '@' + fAxis
        elif fIndex is None:
            label = ''
        else:
            label = '#%d\n' % fIndex
        bs = c.newString(label + ('S:%d\nW:%d\n%d' %
                                  (weightLoc, widthLoc, f.info.weightClass)),
                         style=dict(fontSize=10,
                                    xTextAlign='center',
                                    textFill=Color(0)))
        tw, th = c.textSize(bs)
        c.text(bs, (weightLoc - tw / 2, widthLoc - 24))

        if varFamily.originFont is f:
            # If one of these is the guessed origin font, then draw marker
            c.fill(noColor)
            c.stroke(Color(0, 0.5, 0), 2)  # Stroke color and width
            R = 23
            c.oval(weightLoc - R / 2, widthLoc - R / 2, R, R)

        # Find distance between left side of 2 stems of the H
        eqStems = glyphH.analyzer.stems.values()
        x1 = 10000000000
        x2 = 0
        for stems in eqStems:
            for stem in stems:
                x1 = min(x1, stem.parent[0])
                x2 = max(x2, stem.parent[0])
    else:
        stemS = 'No stem'
        normalizedStemS = '-'
        x1 = x2 = 0
        #print 'No stem for', glyphH.font

    exportCSV.write('%s,%d,%s,%s,%s,%d,%d,%d,%d,%d\n' % (
        f.path.split('/')[-1],
        f.info.unitsPerEm,
        stemS,
        normalizedStemS,
        x2 - x1,
        (x2 - x1) * 1000 / f.info.unitsPerEm,
        #normalizedWidth,
        #normalizedWeight,
        #guessedOS2Width,
        #guessedOS2Weight,
        f.info.widthClass,
        f.info.weightClass,
        f.info.capHeight,
        f.info.capHeight * 1000 / f.info.unitsPerEm))
Exemple #9
0
 def _get_name(self):
     return path2Name(self.font.path)
Exemple #10
0
    def findFont(self, name=None, weight=None, width=None, italic=None):
        u"""Answer the font that is the closest match on name, weight as name or weight as number,
        width as name or width as number and italic angle as name or number, if any of these are defined.
        In case there is one or more fonts in the family then there always is a closest match.
        If the family is empty, None is anwere.

        >>> familyName = 'Roboto' # We know this exists in the PageBot repository
        >>> families = guessFamiliesByPatterns(familyName)
        >>> familyName in families.keys()
        True
        >>> family = families[familyName]
        >>> len(family)
        18
        >>> sorted(family.keys())[1]
        'Roboto-BlackItalic.ttf'
        >>> regularName = 'Roboto-Regular.ttf'
        >>> family._matchWeights('Regular', family[regularName]) # Match on name
        70
        >>> family._matchWeights('Normal', family[regularName]) # Match on alternative name
        70
        >>> family._matchWeights('Bold', family[regularName]) # No match on full name
        0
        >>> family._matchWidths(5, family[regularName]) # Full match in width 5
        1000
        >>> family._matchWidths('Normal', family[regularName]) # Full match in normal width
        1000
        >>> family._matchWidths(5, family[regularName]) # Full match in width 5
        1000
        >>> family._matchWidths(500, family[regularName]) # Full match in width 500
        1000
        >>> family._matchWidths(100, family[regularName]) # Closest match (but not exact) for width 100
        600
        >>> boldName = 'Roboto-Bold.ttf'
        >>> family._matchWeights('Normal', family[boldName]) # No match on alternative name with Bold
        0
        >>> family._matchWeights('Bold', family[boldName]) # Match on full style name
        40
        >>> family._matchWeights('Bd', family[boldName]) # Match on partial style name
        40
        >>> font1 = family.findFont(weight='Regular')
        >>> font2 = family.findFont(weight='Normal')
        >>> font1 is font2, font1.info.styleName # Found by different style  names.
        (True, u'Regular')
        >>> font3 = family.findFont(weight=800, italic=False)
        >>> font3.info.styleName

        """
        matchingFont = None
        match = 0 # Matching value for the current matchingFont
        for font in self.fonts.values():
            thisMatch = 0
            if name is not None and name in path2Name(font.path):
                thisMatch += len(name)**4 # Longer names have better matching
            thisMatch += self._matchWeights(weight or 'Regular', font)**4
            thisMatch += (self._matchWidths(width or 500, font)/2)**4
            thisMatch += (self._matchItalics(italic or 0, font)/4)**4
            if thisMatch > match or matchingFont is None:
                matchingFont = font
                match = thisMatch

        return matchingFont
Exemple #11
0
 def installFont(self, fontPath):
     self._installedFonts.append(fontPath)
     if os.path.exists(fontPath):
         return path2Name(fontPath)
     return None
Exemple #12
0
 def installFont(self, fontPath):
     u"""Install the font in the context and answer the font (file)name."""
     # TODO: To be implemented later, if there is a real need for cached fonts.
     return path2Name(fontPath)