• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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		closeStream = False
30		if not hasattr(file, "read"):
31			file = open(file, "rb")
32			closeStream = True
33
34		tableCache = {} if shareTables else None
35
36		header = readTTCHeader(file)
37		for i in range(header.numFonts):
38			font = TTFont(file, fontNumber=i, _tableCache=tableCache, **kwargs)
39			fonts.append(font)
40
41		# don't close file if lazy=True, as the TTFont hold a reference to the original
42		# file; the file will be closed once the TTFonts are closed in the
43		# TTCollection.close(). We still want to close the file if lazy is None or
44		# False, because in that case the TTFont no longer need the original file
45		# and we want to avoid 'ResourceWarning: unclosed file'.
46		if not kwargs.get("lazy") and closeStream:
47			file.close()
48
49	def __enter__(self):
50		return self
51
52	def __exit__(self, type, value, traceback):
53		self.close()
54
55	def close(self):
56		for font in self.fonts:
57			font.close()
58
59	def save(self, file, shareTables=True):
60		"""Save the font to disk. Similarly to the constructor,
61		the 'file' argument can be either a pathname or a writable
62		file object.
63		"""
64		if not hasattr(file, "write"):
65			final = None
66			file = open(file, "wb")
67		else:
68			# assume "file" is a writable file object
69			# write to a temporary stream to allow saving to unseekable streams
70			final = file
71			file = BytesIO()
72
73		tableCache = {} if shareTables else None
74
75		offsets_offset = writeTTCHeader(file, len(self.fonts))
76		offsets = []
77		for font in self.fonts:
78			offsets.append(file.tell())
79			font._save(file, tableCache=tableCache)
80			file.seek(0,2)
81
82		file.seek(offsets_offset)
83		file.write(struct.pack(">%dL" % len(self.fonts), *offsets))
84
85		if final:
86			final.write(file.getvalue())
87		file.close()
88
89	def saveXML(self, fileOrPath, newlinestr="\n", writeVersion=True, **kwargs):
90
91		from fontTools.misc import xmlWriter
92		writer = xmlWriter.XMLWriter(fileOrPath, newlinestr=newlinestr)
93
94		if writeVersion:
95			from fontTools import version
96			version = ".".join(version.split('.')[:2])
97			writer.begintag("ttCollection", ttLibVersion=version)
98		else:
99			writer.begintag("ttCollection")
100		writer.newline()
101		writer.newline()
102
103		for font in self.fonts:
104			font._saveXML(writer, writeVersion=False, **kwargs)
105			writer.newline()
106
107		writer.endtag("ttCollection")
108		writer.newline()
109
110		writer.close()
111
112
113	def __getitem__(self, item):
114		return self.fonts[item]
115
116	def __setitem__(self, item, value):
117		self.fonts[item] = value
118
119	def __delitem__(self, item):
120		return self.fonts[item]
121
122	def __len__(self):
123		return len(self.fonts)
124
125	def __iter__(self):
126		return iter(self.fonts)
127