1from fontTools.ttLib.ttFont import TTFont 2from fontTools.ttLib.sfnt import readTTCHeader, writeTTCHeader 3from io import BytesIO 4import struct 5import logging 6 7log = logging.getLogger(__name__) 8 9 10class TTCollection(object): 11 12 """Object representing a TrueType Collection / OpenType Collection. 13 The main API is self.fonts being a list of TTFont instances. 14 15 If shareTables is True, then different fonts in the collection 16 might point to the same table object if the data for the table was 17 the same in the font file. Note, however, that this might result 18 in suprises and incorrect behavior if the different fonts involved 19 have different GlyphOrder. Use only if you know what you are doing. 20 """ 21 22 def __init__(self, file=None, shareTables=False, **kwargs): 23 fonts = self.fonts = [] 24 if file is None: 25 return 26 27 assert 'fontNumber' not in kwargs, kwargs 28 29 if not hasattr(file, "read"): 30 file = open(file, "rb") 31 32 tableCache = {} if shareTables else None 33 34 header = readTTCHeader(file) 35 for i in range(header.numFonts): 36 font = TTFont(file, fontNumber=i, _tableCache=tableCache, **kwargs) 37 fonts.append(font) 38 39 def __enter__(self): 40 return self 41 42 def __exit__(self, type, value, traceback): 43 self.close() 44 45 def close(self): 46 for font in self.fonts: 47 font.close() 48 49 def save(self, file, shareTables=True): 50 """Save the font to disk. Similarly to the constructor, 51 the 'file' argument can be either a pathname or a writable 52 file object. 53 """ 54 if not hasattr(file, "write"): 55 final = None 56 file = open(file, "wb") 57 else: 58 # assume "file" is a writable file object 59 # write to a temporary stream to allow saving to unseekable streams 60 final = file 61 file = BytesIO() 62 63 tableCache = {} if shareTables else None 64 65 offsets_offset = writeTTCHeader(file, len(self.fonts)) 66 offsets = [] 67 for font in self.fonts: 68 offsets.append(file.tell()) 69 font._save(file, tableCache=tableCache) 70 file.seek(0,2) 71 72 file.seek(offsets_offset) 73 file.write(struct.pack(">%dL" % len(self.fonts), *offsets)) 74 75 if final: 76 final.write(file.getvalue()) 77 file.close() 78 79 def saveXML(self, fileOrPath, newlinestr=None, writeVersion=True, **kwargs): 80 81 from fontTools.misc import xmlWriter 82 writer = xmlWriter.XMLWriter(fileOrPath, newlinestr=newlinestr) 83 84 if writeVersion: 85 from fontTools import version 86 version = ".".join(version.split('.')[:2]) 87 writer.begintag("ttCollection", ttLibVersion=version) 88 else: 89 writer.begintag("ttCollection") 90 writer.newline() 91 writer.newline() 92 93 for font in self.fonts: 94 font._saveXML(writer, writeVersion=False, **kwargs) 95 writer.newline() 96 97 writer.endtag("ttCollection") 98 writer.newline() 99 100 writer.close() 101 102 103 def __getitem__(self, item): 104 return self.fonts[item] 105 106 def __setitem__(self, item, value): 107 self.fonts[item] = value 108 109 def __delitem__(self, item): 110 return self.fonts[item] 111 112 def __len__(self): 113 return len(self.fonts) 114 115 def __iter__(self): 116 return iter(self.fonts) 117