From: <jo...@us...> - 2007-09-12 07:16:51
|
Revision: 3834 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3834&view=rev Author: jouni Date: 2007-09-12 00:04:38 -0700 (Wed, 12 Sep 2007) Log Message: ----------- Further development of dviread. It now attempts to read virtual fonts, but the positioning is still somewhat off. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py trunk/matplotlib/lib/matplotlib/dviread.py Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-09-11 19:40:27 UTC (rev 3833) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-09-12 07:04:38 UTC (rev 3834) @@ -491,6 +491,7 @@ return fontdictObject def embedType1(self, filename, fontinfo): + # TODO: font effects such as SlantFont fh = open(filename, 'rb') matplotlib.verbose.report( 'Embedding Type 1 font ' + filename, 'debug') @@ -520,8 +521,15 @@ if fontinfo.encodingfile is not None: enc = dviread.Encoding(fontinfo.encodingfile) - widths = [ afmdata.get_width_from_char_name(ch) - for ch in enc ] + widths = [] + for ch in enc: + try: + widths.append(afmdata.get_width_from_char_name(ch)) + except KeyError: + matplotlib.verbose.report( + 'No width for %s in %s' % (ch, fullname), 'debug') + widths.append(0) + differencesArray = [ Name(ch) for ch in enc ] differencesArray = [ 0 ] + differencesArray firstchar = 0 @@ -538,11 +546,24 @@ firstchar = not_None.next() lastchar = max(not_None) widths = widths[firstchar:lastchar+1] + for i,w in enumerate(widths): + if w is None: widths[i] = 0 - differencesArray = [ firstchar ] + differencesArray = [ ] + need_idx = True for ch in range(firstchar, lastchar+1): - differencesArray.append(Name( - afmdata.get_name_char(ch, isord=True))) + try: + name = afmdata.get_name_char(ch, isord=True) + if need_idx: + differencesArray.append(ch) + need_idx = False + differencesArray.append(Name(name)) + except KeyError: + matplotlib.verbose.report( + 'No name for glyph %d in %s' % (ch, fullname), + 'debug') + need_idx = True + fontdict = { 'Type': Name('Font'), @@ -1448,17 +1469,16 @@ # Gather font information and do some setup for combining # characters into strings. - oldfontnum, seq = None, [] - for x1, y1, fontnum, glyph, width in page.text: - if fontnum != oldfontnum: - texname, fontsize = dvi.fontinfo(fontnum) - fontinfo = self.tex_font_mapping(texname) + oldfont, seq = None, [] + for x1, y1, dvifont, glyph, width in page.text: + if dvifont != oldfont: + fontinfo = self.tex_font_mapping(dvifont.texname) pdfname = self.file.fontName(fontinfo.filename) self.file.fontInfo[pdfname] = Bunch( encodingfile=fontinfo.encoding, afmfile=fontinfo.afm) - seq += [['font', pdfname, fontsize]] - oldfontnum = fontnum + seq += [['font', pdfname, dvifont.size]] + oldfont = dvifont seq += [['text', x1, y1, [chr(glyph)], x1+width]] seq += [('end',)] Modified: trunk/matplotlib/lib/matplotlib/dviread.py =================================================================== --- trunk/matplotlib/lib/matplotlib/dviread.py 2007-09-11 19:40:27 UTC (rev 3833) +++ trunk/matplotlib/lib/matplotlib/dviread.py 2007-09-12 07:04:38 UTC (rev 3834) @@ -9,15 +9,13 @@ for page in dvi: # iterate over pages w, h, d = page.width, page.height, page.descent for x,y,font,glyph,width in page.text: - fontname, pointsize = dvi.fontinfo(font) + fontname = font.texname + pointsize = font.size ... for x,y,height,width in page.boxes: ... """ -# TODO: support TeX virtual fonts (*.vf) which are a sort of -# subroutine collections for dvi files - import matplotlib import matplotlib.cbook as mpl_cbook import numpy as npy @@ -85,8 +83,7 @@ x,y,h,w = elt e = 0 # zero depth else: # glyph - x,y,f,g,w = elt - font = self.fonts[f] + x,y,font,g,w = elt h = (font.scale * font.tfm.height[g]) >> 20 e = (font.scale * font.tfm.depth[g]) >> 20 minx = min(minx, x) @@ -96,7 +93,8 @@ maxy_pure = max(maxy_pure, y) d = self.dpi / (72.27 * 2**16) # from TeX's "scaled points" to dpi units - text = [ ((x-minx)*d, (maxy-y)*d, f, g, w*d) for (x,y,f,g,w) in self.text ] + text = [ ((x-minx)*d, (maxy-y)*d, DviFont(f), g, w*d) + for (x,y,f,g,w) in self.text ] boxes = [ ((x-minx)*d, (maxy-y)*d, h*d, w*d) for (x,y,h,w) in self.boxes ] return mpl_cbook.Bunch(text=text, boxes=boxes, @@ -104,14 +102,6 @@ height=(maxy_pure-miny)*d, descent=(maxy-maxy_pure)*d) - def fontinfo(self, f): - """ - texname, pointsize = dvi.fontinfo(fontnum) - - Name and size in points (Adobe points, not TeX points). - """ - return self.fonts[f].name, self.fonts[f].scale * (72.0 / (72.27 * 2**16)) - def _read(self): """ Read one page from the file. Return True if successful, @@ -235,17 +225,21 @@ # I think we can assume this is constant self.state = _dvistate.outer - def _width_of(self, char): - font = self.fonts[self.f] - width = font.tfm.width[char] - width = (width * font.scale) >> 20 - return width + def _width_of(self, char, font): + width = font.tfm.width.get(char, None) + if width is not None: + return (width * font.scale) >> 20 + matplotlib.verbose.report( + 'No width for char %d in font %s' % (char, font.name), + 'debug') + return 0 + def _set_char(self, char): if self.state != _dvistate.inpage: raise ValueError, "misplaced set_char in dvi file" self._put_char(char) - self.h += self._width_of(char) + self.h += self._width_of(char, self.fonts[self.f]) def _set_rule(self, a, b): if self.state != _dvistate.inpage: @@ -256,7 +250,15 @@ def _put_char(self, char): if self.state != _dvistate.inpage: raise ValueError, "misplaced put_char in dvi file" - self.text.append((self.h, self.v, self.f, char, self._width_of(char))) + font = self.fonts[self.f] + if font.vf is None: + self.text.append((self.h, self.v, font, char, + self._width_of(char, font))) + else: + self.text.extend([(self.h + x, self.v + y, f, g, w) + for x, y, f, g, w in font.vf[char].text]) + self.boxes.extend([(self.h + x, self.v + y, a, b) + for x, y, a, b in font.vf[char].boxes]) def _put_rule(self, a, b): if self.state != _dvistate.inpage: @@ -269,8 +271,8 @@ def _bop(self, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, p): if self.state != _dvistate.outer: - print '+++', self.state - raise ValueError, "misplaced bop in dvi file" + raise ValueError, \ + "misplaced bop in dvi file (state %d)" % self.state self.state = _dvistate.inpage self.h, self.v, self.w, self.x, self.y, self.z = 0, 0, 0, 0, 0, 0 self.stack = [] @@ -345,16 +347,18 @@ 'debug') def _fnt_def(self, k, c, s, d, a, l, n): - filename = find_tex_file(n[-l:] + '.tfm') - tfm = Tfm(filename) + tfm = _tfmfile(n[-l:]) if c != 0 and tfm.checksum != 0 and c != tfm.checksum: raise ValueError, 'tfm checksum mismatch: %s'%n # It seems that the assumption behind the following check is incorrect: #if d != tfm.design_size: # raise ValueError, 'tfm design size mismatch: %d in dvi, %d in %s'%\ # (d, tfm.design_size, n) - self.fonts[k] = mpl_cbook.Bunch(scale=s, tfm=tfm, name=n) + vf = _vffile(n[-l:]) + + self.fonts[k] = mpl_cbook.Bunch(scale=s, tfm=tfm, name=n, vf=vf) + def _post(self): if self.state != _dvistate.outer: raise ValueError, "misplaced post in dvi file" @@ -365,6 +369,121 @@ def _post_post(self): raise NotImplementedError +class DviFont(object): + __slots__ = ('texname', 'size') + + def __init__(self, f): + """ + Object that holds a font's texname and size and supports comparison. + + The size is in Adobe points (converted from TeX points). + """ + # TODO: would it make more sense to have the size in dpi units? + self.texname = f.name + self.size = f.scale * (72.0 / (72.27 * 2**16)) + + def __eq__(self, other): + return self.__class__ == other.__class__ and \ + self.texname == other.texname and self.size == other.size + + def __ne__(self, other): + return not self.__eq__(other) + +class Vf(Dvi): + """ + A virtual font (*.vf file) containing subroutines for dvi files. + + Usage: + vf = Vf(filename) + glyph = vf[code] + glyph.text, glyph.boxes, glyph.width + """ + + def __init__(self, filename): + Dvi.__init__(self, filename, 0) + self._first_font = None + self._chars = {} + self._packet_ends = None + self._read() + self.close() + + def __getitem__(self, code): + return self._chars[code] + + def _dispatch(self, byte): + # If we are in a packet, execute the dvi instructions + if self.state == _dvistate.inpage: + byte_at = self.file.tell()-1 + if byte_at == self._packet_ends: + self._finalize_packet() + # fall through + elif byte_at > self._packet_ends: + raise ValueError, "Packet length mismatch in vf file" + else: + if byte in (139, 140) or byte >= 243: + raise ValueError, "Inappropriate opcode %d in vf file" % byte + Dvi._dispatch(self, byte) + return + + # We are outside a packet + if byte < 242: # a short packet (length given by byte) + cc, tfm = self._arg(1), self._arg(3) + self._init_packet(byte, cc, tfm) + elif byte == 242: # a long packet + pl, cc, tfm = [ self._arg(x) for x in (4, 4, 4) ] + self._init_packet(pl, cc, tfm) + elif 243 <= byte <= 246: + Dvi._dispatch(self, byte) + elif byte == 247: # preamble + i, k = self._arg(1), self._arg(1) + x = self.file.read(k) + cs, ds = self._arg(4), self._arg(4) + self._pre(i, x, cs, ds) + elif byte == 248: # postamble (just some number of 248s) + self.state = _dvistate.post_post + else: + raise ValueError, "unknown vf opcode %d" % byte + + def _init_packet(self, pl, cc, tfm): + if self.state != _dvistate.outer: + raise ValueError, "Misplaced packet in vf file" + self.state = _dvistate.inpage + self._packet_ends = self.file.tell() + pl + self._packet_char = cc + self._packet_width = tfm + self.h, self.v, self.w, self.x, self.y, self.z = 0, 0, 0, 0, 0, 0 + self.stack, self.text, self.boxes = [], [], [] + self.f = self._first_font + + def _finalize_packet(self): + self._chars[self._packet_char] = mpl_cbook.Bunch( + text=self.text, boxes=self.boxes, width = self._packet_width) + self.state = _dvistate.outer + + def _pre(self, i, x, cs, ds): + if self.state != _dvistate.pre: + raise ValueError, "pre command in middle of vf file" + if i != 202: + raise ValueError, "Unknown vf format %d" % i + matplotlib.verbose.report('vf file comment: ' + x, 'debug') + self.state = _dvistate.outer + # cs = checksum, ds = design size + + def _fnt_def(self, k, *args): + Dvi._fnt_def(self, k, *args) + if self._first_font is None: + self._first_font = k + +def fix2comp(num): + """ + Convert from two's complement to negative. + """ + assert 0 <= num < 2**32 + if num & 2**31: + return num - 2**32 + else: + return num + class Tfm(object): """ A TeX Font Metric file. This implementation covers only the bare @@ -380,12 +499,16 @@ """ def __init__(self, filename): + matplotlib.verbose.report('opening tfm file ' + filename, 'debug') file = open(filename, 'rb') try: header1 = file.read(24) lh, bc, ec, nw, nh, nd = \ struct.unpack('!6H', header1[2:14]) + matplotlib.verbose.report( + 'lh=%d, bc=%d, ec=%d, nw=%d, nh=%d, nd=%d' % ( + lh, bc, ec, nw, nh, nd), 'debug') header2 = file.read(4*lh) self.checksum, self.design_size = \ struct.unpack('!2I', header2[:8]) @@ -399,12 +522,12 @@ self.width, self.height, self.depth = {}, {}, {} widths, heights, depths = \ - [ struct.unpack('!%dI' % n, x) - for n,x in [(nw, widths), (nh, heights), (nd, depths)] ] + [ struct.unpack('!%dI' % (len(x)/4), x) + for x in (widths, heights, depths) ] for i in range(ec-bc): - self.width[bc+i] = widths[ord(char_info[4*i])] - self.height[bc+i] = heights[ord(char_info[4*i+1]) >> 4] - self.depth[bc+i] = depths[ord(char_info[4*i+1]) & 0xf] + self.width[bc+i] = fix2comp(widths[ord(char_info[4*i])]) + self.height[bc+i] = fix2comp(heights[ord(char_info[4*i+1]) >> 4]) + self.depth[bc+i] = fix2comp(depths[ord(char_info[4*i+1]) & 0xf]) class PsfontsMap(object): @@ -530,9 +653,12 @@ # Expecting something like /FooEncoding [ if '[' in line: state = 1 - line = line[line.index('[')+1].strip() + line = line[line.index('[')+1:].strip() if state == 1: + if ']' in line: # ] def + line = line[:line.index(']')] + state = 2 words = line.split() for w in words: if w.startswith('/'): @@ -541,10 +667,6 @@ result.extend(subwords[1:]) else: raise ValueError, "Broken name in encoding file: " + w - - # Expecting ] def - if ']' in line: - break return result @@ -572,6 +694,32 @@ return result +_tfmcache = {} +_vfcache = {} + +def _fontfile(texname, class_, suffix, cache): + try: + return cache[texname] + except KeyError: + pass + + filename = find_tex_file(texname + suffix) + if filename: + result = class_(filename) + else: + result = None + + cache[texname] = result + return result + +def _tfmfile(texname): + return _fontfile(texname, Tfm, '.tfm', _tfmcache) + +def _vffile(texname): + return _fontfile(texname, Vf, '.vf', _vfcache) + + + if __name__ == '__main__': matplotlib.verbose.set_level('debug') dvi = Dvi('foo.dvi', 72) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |