From: <jo...@us...> - 2007-09-06 20:13:12
|
Revision: 3802 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3802&view=rev Author: jouni Date: 2007-09-06 13:13:11 -0700 (Thu, 06 Sep 2007) Log Message: ----------- A more careful reading of the pdf spec reveals that Type 1 fonts embedded in pdf files are not actually supposed to be in pfa format, but a similar format where the encrypted part is not encoded in hexadecimal, and where the "fixed-content" part at the end may be omitted. This fixes the problems with Preview.app. Modified Paths: -------------- trunk/matplotlib/API_CHANGES trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py trunk/matplotlib/lib/matplotlib/type1font.py Modified: trunk/matplotlib/API_CHANGES =================================================================== --- trunk/matplotlib/API_CHANGES 2007-09-06 19:36:34 UTC (rev 3801) +++ trunk/matplotlib/API_CHANGES 2007-09-06 20:13:11 UTC (rev 3802) @@ -7,9 +7,9 @@ The file type1font.py contains a new class for Type 1 fonts. Currently it simply reads pfa and pfb format files and stores the - data in pfa format, which is the format for embedding Type 1 fonts - in postscript and pdf files. In the future the class might - actually parse the font to allow e.g. subsetting. + data in a way that is suitable for embedding in pdf files. In the + future the class might actually parse the font to allow e.g. + subsetting. FT2Font now supports FT_Attach_File. In practice this can be used to read an afm file in addition to a pfa/pfb file, to get metrics Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2007-09-06 19:36:34 UTC (rev 3801) +++ trunk/matplotlib/CHANGELOG 2007-09-06 20:13:11 UTC (rev 3802) @@ -1,3 +1,6 @@ +2007-09-06 Fixed a bug in the embedding of Type 1 fonts in PDF. + Now it doesn't crash Preview.app. - JKS + 2007-09-06 Refactored image saving code so that all GUI backends can save most image types. See FILETYPES for a matrix of backends and their supported file types. Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-09-06 19:36:34 UTC (rev 3801) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-09-06 20:13:11 UTC (rev 3802) @@ -607,13 +607,13 @@ self.writeObject(widthsObject, widths) self.writeObject(fontdescObject, descriptor) - fontdata = type1font.Type1Font(filename) - len1, len2, len3 = fontdata.lengths() + t1font = type1font.Type1Font(filename) self.beginStream(fontfileObject.id, None, - { 'Length1': len1, - 'Length2': len2, - 'Length3': len3 }) - self.currentstream.write(fontdata.data) + { 'Length1': len(t1font.parts[0]), + 'Length2': len(t1font.parts[1]), + 'Length3': 0 }) + self.currentstream.write(t1font.parts[0]) + self.currentstream.write(t1font.parts[1]) self.endStream() return fontdictObject Modified: trunk/matplotlib/lib/matplotlib/type1font.py =================================================================== --- trunk/matplotlib/lib/matplotlib/type1font.py 2007-09-06 19:36:34 UTC (rev 3801) +++ trunk/matplotlib/lib/matplotlib/type1font.py 2007-09-06 20:13:11 UTC (rev 3802) @@ -1,9 +1,10 @@ """ A class representing a Type 1 font. -This version merely allows reading in pfa and pfb files, and stores -the data in pfa format (which can be embedded in PostScript or PDF -files). A more complete class might support subsetting. +This version merely allows reading in pfa and pfb files, stores the +data in pfa format, and allows reading the parts of the data in a +format suitable for embedding in pdf files. A more complete class +might support subsetting. Usage: font = Type1Font(filename) somefile.write(font.data) # writes out font in pfa format @@ -23,9 +24,10 @@ def __init__(self, filename): file = open(filename, 'rb') try: - self._read(file) + data = self._read(file) finally: file.close() + self.parts = self._split(data) def _read(self, file): rawdata = file.read() @@ -33,7 +35,7 @@ self.data = rawdata return - self.data = '' + data = '' while len(rawdata) > 0: if not rawdata.startswith(chr(128)): raise RuntimeError, \ @@ -46,9 +48,9 @@ rawdata = rawdata[6+length:] if type == 1: # ASCII text: include verbatim - self.data += segment + data += segment elif type == 2: # binary data: encode in hexadecimal - self.data += ''.join(['%02x' % ord(char) + data += ''.join(['%02x' % ord(char) for char in segment]) elif type == 3: # end of file break @@ -56,9 +58,11 @@ raise RuntimeError, \ 'Unknown segment type %d in pfb file' % type - def lengths(self): + return data + + def _split(self, data): """ - Compute the lengths of the three parts of a Type 1 font. + Split the Type 1 font into its three main parts. The three parts are: (1) the cleartext part, which ends in a eexec operator; (2) the encrypted part; (3) the fixed part, @@ -66,28 +70,33 @@ lines, a cleartomark operator, and possibly something else. """ - # Cleartext part: just find the eexec and skip the eol char(s) - idx = self.data.index('eexec') + # Cleartext part: just find the eexec and skip whitespace + idx = data.index('eexec') idx += len('eexec') - while self.data[idx] in ('\n', '\r'): + while data[idx] in ' \t\r\n': idx += 1 len1 = idx # Encrypted part: find the cleartomark operator and count # zeros backward - idx = self.data.rindex('cleartomark') - 1 + idx = data.rindex('cleartomark') - 1 zeros = 512 - while zeros and self.data[idx] in ('0', '\n', '\r'): - if self.data[idx] == '0': + while zeros and data[idx] in ('0', '\n', '\r'): + if data[idx] == '0': zeros -= 1 idx -= 1 if zeros: raise RuntimeError, 'Insufficiently many zeros in Type 1 font' - len2 = idx - len1 - len3 = len(self.data) - idx + # Convert encrypted part to binary (if we read a pfb file, we + # may end up converting binary to hexadecimal to binary again; + # but if we read a pfa file, this part is already in hex, and + # I am not quite sure if even the pfb format guarantees that + # it will be in binary). + binary = ''.join([chr(int(data[i:i+2], 16)) + for i in range(len1, idx, 2)]) - return len1, len2, len3 + return data[:len1], binary, data[idx:] if __name__ == '__main__': import sys This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |