def testSubsetting(self): "Tests TTFontFile and TTF parsing code" ttf = TTFontFile("Vera.ttf") subset = ttf.makeSubset([0x41, 0x42]) subset = TTFontFile(StringIO(subset), 0) for tag in ('cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'OS/2', 'post', 'cvt ', 'fpgm', 'glyf', 'loca', 'prep'): self.assert_(subset.get_table(tag)) subset.seek_table('loca') for n in range(4): pos = subset.read_ushort() # this is actually offset / 2 self.failIf( pos % 2 != 0, "glyph %d at +%d should be long aligned" % (n, pos * 2)) self.assertEquals(subset.name, "BitstreamVeraSans-Roman") self.assertEquals(subset.flags, FF_SYMBOLIC) self.assertEquals(subset.italicAngle, 0.0) self.assertNear(subset.ascent, 759.765625) self.assertNear(subset.descent, -240.234375) self.assertEquals(subset.capHeight, 759.765625) self.assertNear( subset.bbox, [-183.10546875, -235.83984375, 1287.109375, 928.22265625]) self.assertEquals(subset.stemV, 87)
def testSubsetting(self): "Tests TTFontFile and TTF parsing code" ttf = TTFontFile("luxiserif.ttf") subset = ttf.makeSubset([0x41, 0x42]) subset = TTFontFile(StringIO(subset), 0) for tag in ('cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'OS/2', 'post', 'cvt ', 'fpgm', 'glyf', 'loca', 'prep'): self.assert_(subset.get_table(tag)) subset.seek_table('loca') for n in range(4): pos = subset.read_ushort() # this is actually offset / 2 self.failIf( pos % 2 != 0, "glyph %d at +%d should be long aligned" % (n, pos * 2)) self.assertEquals(subset.name, "LuxiSerif") self.assertEquals(subset.flags, FF_SYMBOLIC) self.assertEquals(subset.italicAngle, 0.0) self.assertNear(subset.ascent, 783.203125) # FIXME: or 992? self.assertNear(subset.descent, -205.078125) # FIXME: or -210? self.assertEquals(subset.capHeight, 0) self.assertNear(subset.bbox, [-203.125, -210.9375, 983.3984375, 992.67578125]) self.assertEquals(subset.stemV, 87)
def testFontFileChecksum(self): "Tests TTFontFile and TTF parsing code" file = TTFOpenFile("Vera.ttf")[1].read() TTFontFile(StringIO(file), validate=1) # should not fail file1 = file[:12345] + "\xFF" + file[12346:] # change one byte self.assertRaises(TTFError, TTFontFile, StringIO(file1), validate=1) file1 = file[:8] + "\xFF" + file[9:] # change one byte self.assertRaises(TTFError, TTFontFile, StringIO(file1), validate=1)
def testFontFileChecksum(self): "Tests TTFontFile and TTF parsing code" F = TTFOpenFile("Vera.ttf")[1].read() TTFontFile(getBytesIO(F), validate=1) # should not fail F1 = F[:12345] + b"\xFF" + F[12346:] # change one byte self.assertRaises(TTFError, TTFontFile, getBytesIO(F1), validate=1) F1 = F[:8] + b"\xFF" + F[9:] # change one byte self.assertRaises(TTFError, TTFontFile, getBytesIO(F1), validate=1)
def testSubsetting(self): "Tests TTFontFile and TTF parsing code" ttf = TTFontFile("luxiserif.ttf") subset = ttf.makeSubset([0x41, 0x42]) subset = TTFontFile(StringIO(subset), 0) for tag in ( "cmap", "head", "hhea", "hmtx", "maxp", "name", "OS/2", "post", "cvt ", "fpgm", "glyf", "loca", "prep", ): self.assert_(subset.get_table(tag)) subset.seek_table("loca") for n in range(4): pos = subset.read_ushort() # this is actually offset / 2 self.failIf(pos % 2 != 0, "glyph %d at +%d should be long aligned" % (n, pos * 2)) self.assertEquals(subset.name, "LuxiSerif") self.assertEquals(subset.flags, FF_SYMBOLIC) self.assertEquals(subset.italicAngle, 0.0) self.assertEquals(subset.ascent, 783) # FIXME: or 992? self.assertEquals(subset.descent, -206) # FIXME: or -210? self.assertEquals(subset.capHeight, 0) self.assertEquals(subset.bbox, [-204, -211, 983, 992]) self.assertEquals(subset.stemV, 87)
def testFontFile(self): "Tests TTFontFile and TTF parsing code" ttf = TTFontFile("Vera.ttf") self.assertEquals(ttf.name, "BitstreamVeraSans-Roman") self.assertEquals(ttf.flags, FF_SYMBOLIC) self.assertEquals(ttf.italicAngle, 0.0) self.assertNear(ttf.ascent,759.765625) self.assertNear(ttf.descent,-240.234375) self.assertEquals(ttf.capHeight, 759.765625) self.assertNear(ttf.bbox, [-183.10546875, -235.83984375, 1287.109375, 928.22265625]) self.assertEquals(ttf.stemV, 87) self.assertEquals(ttf.defaultWidth, 600.09765625)
def testFontFile(self): "Tests TTFontFile and TTF parsing code" ttf = TTFontFile("luxiserif.ttf") self.assertEquals(ttf.name, "LuxiSerif") self.assertEquals(ttf.flags, FF_SYMBOLIC) self.assertEquals(ttf.italicAngle, 0.0) self.assertNear(ttf.ascent, 783.203125) # FIXME: or 992? self.assertNear(ttf.descent, -205.078125) # FIXME: or -210? self.assertEquals(ttf.capHeight, 0) self.assertNear(ttf.bbox, [-203.125, -210.9375, 983.3984375, 992.67578125]) self.assertEquals(ttf.stemV, 87) self.assertEquals(ttf.defaultWidth, 250)
def testSubsetting(self): "Tests TTFontFile and TTF parsing code" ttf = TTFontFile("Vera.ttf") subset = ttf.makeSubset([0x41, 0x42]) subset = TTFontFile(getBytesIO(subset), 0) for tag in ('cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'OS/2', 'post', 'cvt ', 'fpgm', 'glyf', 'loca', 'prep'): self.assert_(subset.get_table(tag)) subset.seek_table('loca') for n in range(4): pos = subset.read_ushort() # this is actually offset / 2 self.failIf(pos % 2 != 0, "glyph %d at +%d should be long aligned" % (n, pos * 2)) self.assertEquals(subset.name, b"BitstreamVeraSans-Roman") self.assertEquals(subset.flags, FF_SYMBOLIC) self.assertEquals(subset.italicAngle, 0.0) self.assertNear(subset.ascent,759.765625) self.assertNear(subset.descent,-240.234375) self.assertEquals(subset.capHeight, 759.765625) self.assertNear(subset.bbox, [-183.10546875, -235.83984375, 1287.109375, 928.22265625]) self.assertEquals(subset.stemV, 87)
def testSubsetting(self): "Tests TTFontFile and TTF parsing code" ttf = TTFontFile("luxiserif.ttf") subset = ttf.makeSubset([0x41, 0x42]) subset = TTFontFile(StringIO(subset), 0) for tag in ('cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'OS/2', 'post', 'cvt ', 'fpgm', 'glyf', 'loca', 'prep'): self.assert_(subset.get_table(tag)) subset.seek_table('loca') for n in range(4): pos = subset.read_ushort() # this is actually offset / 2 self.failIf(pos % 2 != 0, "glyph %d at +%d should be long aligned" % (n, pos * 2)) self.assertEquals(subset.name, "LuxiSerif") self.assertEquals(subset.flags, FF_SYMBOLIC) self.assertEquals(subset.italicAngle, 0.0) self.assertNear(subset.ascent,783.203125) # FIXME: or 992? self.assertNear(subset.descent,-205.078125) # FIXME: or -210? self.assertEquals(subset.capHeight, 0) self.assertNear(subset.bbox, [-203.125, -210.9375, 983.3984375, 992.67578125]) self.assertEquals(subset.stemV, 87)
def loadFonts(): """ Search the system and build lists of available fonts. """ if not any([afmList, pfbList, ttfList]): # FIXME: When we drop support por py2, use recursive globs for folder in flist: for root, _, files in os.walk(folder): for f in files: if fnmatch(f, "*.ttf") or fnmatch(f, "*.ttc"): ttfList.append(os.path.join(root, f)) elif fnmatch(f, "*.afm"): afmList.append(os.path.join(root, f)) elif fnmatch(f, "*.pfb"): pfbList[f[:-4]] = os.path.join(root, f) for ttf in ttfList: try: font = TTFontFile(ttf) except TTFError: log.warning("Error processing %s", ttf) continue family = make_string(font.familyName.lower()) fontName = make_string(font.name).lower() baseName = os.path.basename(ttf)[:-4] fullName = make_string(font.fullName).lower() for k in (fontName, fullName, fullName.replace("italic", "oblique")): fonts[k] = (ttf, ttf, family) bold = FF_FORCEBOLD == FF_FORCEBOLD & font.flags italic = FF_ITALIC == FF_ITALIC & font.flags # And we can try to build/fill the family mapping if family not in families: families[family] = [fontName, fontName, fontName, fontName] if bold and italic: families[family][3] = fontName elif bold: families[family][1] = fontName elif italic: families[family][2] = fontName # FIXME: what happens if there are Demi and Medium # weights? We get a random one. else: families[family][0] = fontName # Now we have full afm and pbf lists, process the # afm list to figure out family name, weight and if # it's italic or not, as well as where the # matching pfb file is for afm in afmList: family = None fontName = None italic = False bold = False for line in open(afm, "r"): line = line.strip() if line.startswith("StartCharMetrics"): break elif line.startswith("FamilyName"): family = line.split(" ", 1)[1].lower() elif line.startswith("FontName"): fontName = line.split(" ")[1] elif line.startswith("FullName"): fullName = line.split(" ", 1)[1] elif line.startswith("Weight"): bold = line.split(" ")[1] == "Bold" elif line.startswith("ItalicAngle"): italic = line.split(" ")[1] != "0.0" baseName = os.path.basename(afm)[:-4] if family in Ignored or family in Alias: continue if baseName not in pfbList: log.info("afm file without matching pfb file: %s" % baseName) continue # So now we have a font we know we can embed. for n in ( fontName.lower(), fullName.lower(), fullName.lower().replace("italic", "oblique"), ): fonts[n] = (afm, pfbList[baseName], family) # And we can try to build/fill the family mapping if family not in families: families[family] = [fontName, fontName, fontName, fontName] if bold and italic: families[family][3] = fontName elif bold: families[family][1] = fontName elif italic: families[family][2] = fontName # FIXME: what happens if there are Demi and Medium # weights? We get a random one. else: families[family][0] = fontName
def search(self): started = time.clock() if not self._dirs: raise ValueError( "Font search path is empty! Please specify search directories using addDirectory or addDirectories" ) if self.useCache: cfn = self._getCacheFileName() if rl_isfile(cfn): try: self.load(cfn) #print "loaded cached file with %d fonts (%s)" % (len(self._fonts), cfn) return except: pass #pickle load failed. Ho hum, maybe it's an old pickle. Better rebuild it. from stat import ST_MTIME for dirName in self._dirs: fileNames = rl_listdir(dirName) for fileName in fileNames: root, ext = os.path.splitext(fileName) if ext.lower() in EXTENSIONS: #it's a font f = FontDescriptor() f.fileName = os.path.normpath( os.path.join(dirName, fileName)) f.timeModified = rl_getmtime(f.fileName) ext = ext.lower() if ext[0] == '.': ext = ext[1:] f.typeCode = ext #strip the dot #what to do depends on type. We only accept .pfb if we #have .afm to go with it, and don't handle .otf now. if ext in ('otf', 'pfa'): self._skippedFiles.append(fileName) elif ext in ('ttf', 'ttc'): #parsing should check it for us from reportlab.pdfbase.ttfonts import TTFontFile, TTFError try: font = TTFontFile(fileName, validate=self.validate) except TTFError: self._badFiles.append(fileName) continue f.name = font.name f.fullName = font.fullName f.styleName = font.styleName f.familyName = font.familyName f.isBold = (FF_FORCEBOLD == FF_FORCEBOLD & font.flags) f.isItalic = (FF_ITALIC == FF_ITALIC & font.flags) elif ext == 'pfb': # type 1; we need an AFM file or have to skip. if rl_isfile(os.path.join(dirName, root + '.afm')): f.metricsFileName = os.path.normpath( os.path.join(dirName, root + '.afm')) elif rl_isfile(os.path.join(dirName, root + '.AFM')): f.metricsFileName = os.path.normpath( os.path.join(dirName, root + '.AFM')) else: self._skippedFiles.append(fileName) continue from reportlab.pdfbase.pdfmetrics import parseAFMFile (info, glyphs) = parseAFMFile(f.metricsFileName) f.name = info['FontName'] f.fullName = info.get('FullName', f.name) f.familyName = info.get('FamilyName', None) f.isItalic = (float(info.get('ItalicAngle', 0)) > 0.0) #if the weight has the word bold, deem it bold f.isBold = ('bold' in info.get('Weight', '').lower()) self._fonts.append(f) if self.useCache: self.save(cfn) finished = time.clock()
def loadFonts(): """ Search the system and build lists of available fonts. """ if not afmList and not pfbList and not ttfList: # Find all ".afm" and ".pfb" files files def findFontFiles(_, folder, names): for f in os.listdir(folder): ext = os.path.splitext(f)[-1] if ext in ['.ttf', '.ttc']: ttfList.append(os.path.join(folder, f)) if ext == '.afm': afmList.append(os.path.join(folder, f)) if ext == '.pfb': pfbList[f[:-4]] = os.path.join(folder, f) for folder in flist: os.path.walk(folder, findFontFiles, None) for ttf in ttfList: '''Find out how to process these''' try: font = TTFontFile(ttf) except TTFError: continue #print ttf, font.name, font.fullName, font.styleName, font.familyName family = font.familyName.lower() fontName = font.name baseName = os.path.basename(ttf)[:-4] fullName = font.fullName fonts[fontName.lower()] = (ttf, ttf, family) fonts[fullName.lower()] = (ttf, ttf, family) fonts[fullName.lower().replace('italic', 'oblique')] = (ttf, ttf, family) bold = (FF_FORCEBOLD == FF_FORCEBOLD & font.flags) italic = (FF_ITALIC == FF_ITALIC & font.flags) # And we can try to build/fill the family mapping if family not in families: families[family] = [fontName, fontName, fontName, fontName] if bold and italic: families[family][3] = fontName elif bold: families[family][1] = fontName elif italic: families[family][2] = fontName # FIXME: what happens if there are Demi and Medium # weights? We get a random one. else: families[family][0] = fontName # Now we have full afm and pbf lists, process the # afm list to figure out family name, weight and if # it's italic or not, as well as where the # matching pfb file is for afm in afmList: family = None fontName = None italic = False bold = False for line in open(afm, 'r'): line = line.strip() if line.startswith('StartCharMetrics'): break elif line.startswith('FamilyName'): family = ' '.join(line.split(' ')[1:]).lower() elif line.startswith('FontName'): fontName = line.split(' ')[1] # TODO: find a way to alias the fullname to this font # so you can use names like "Bitstream Charter Italic" elif line.startswith('FullName'): fullName = ' '.join(line.split(' ')[1:]) elif line.startswith('Weight'): w = line.split(' ')[1] if w == 'Bold': bold = True elif line.startswith('ItalicAngle'): if line.split(' ')[1] != '0.0': italic = True baseName = os.path.basename(afm)[:-4] if family in Ignored: continue if family in Alias: continue if baseName not in pfbList: log.info("afm file without matching pfb file: %s" % baseName) continue # So now we have a font we know we can embed. fonts[fontName.lower()] = (afm, pfbList[baseName], family) fonts[fullName.lower()] = (afm, pfbList[baseName], family) fonts[fullName.lower().replace( 'italic', 'oblique')] = (afm, pfbList[baseName], family) # And we can try to build/fill the family mapping if family not in families: families[family] = [fontName, fontName, fontName, fontName] if bold and italic: families[family][3] = fontName elif bold: families[family][1] = fontName elif italic: families[family][2] = fontName # FIXME: what happens if there are Demi and Medium # weights? We get a random one. else: families[family][0] = fontName