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.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to