Revision: 6718
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6718&view=rev
Author:   jouni
Date:     2008-12-31 13:20:50 +0000 (Wed, 31 Dec 2008)

Log Message:
-----------
Improve pdf usetex by adding support for font effects

Modified Paths:
--------------
    trunk/matplotlib/CHANGELOG
    trunk/matplotlib/doc/api/index_backend_api.rst
    trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py
    trunk/matplotlib/lib/matplotlib/dviread.py
    trunk/matplotlib/lib/matplotlib/type1font.py

Added Paths:
-----------
    trunk/matplotlib/doc/api/dviread.rst
    trunk/matplotlib/doc/api/type1font.rst
    trunk/matplotlib/examples/pylab_examples/usetex_fonteffects.py

Modified: trunk/matplotlib/CHANGELOG
===================================================================
--- trunk/matplotlib/CHANGELOG  2008-12-30 16:06:59 UTC (rev 6717)
+++ trunk/matplotlib/CHANGELOG  2008-12-31 13:20:50 UTC (rev 6718)
@@ -1,3 +1,6 @@
+2008-12-31 Improve pdf usetex by adding support for font effects
+           (slanting and extending). - JKS
+
 2008-12-29 Fix a bug in pdf usetex support, which occurred if the same
            Type-1 font was used with different encodings, e.g. with
            Minion Pro and MnSymbol. - JKS

Added: trunk/matplotlib/doc/api/dviread.rst
===================================================================
--- trunk/matplotlib/doc/api/dviread.rst                                (rev 0)
+++ trunk/matplotlib/doc/api/dviread.rst        2008-12-31 13:20:50 UTC (rev 
6718)
@@ -0,0 +1,8 @@
+
+:mod:`matplotlib.dviread`
+=========================
+
+.. automodule:: matplotlib.dviread
+   :members:
+   :undoc-members:
+   :show-inheritance:

Modified: trunk/matplotlib/doc/api/index_backend_api.rst
===================================================================
--- trunk/matplotlib/doc/api/index_backend_api.rst      2008-12-30 16:06:59 UTC 
(rev 6717)
+++ trunk/matplotlib/doc/api/index_backend_api.rst      2008-12-31 13:20:50 UTC 
(rev 6718)
@@ -8,3 +8,5 @@
    backend_gtkagg_api.rst
    backend_qt4agg_api.rst
    backend_wxagg_api.rst
+   dviread.rst
+   type1font.rst

Added: trunk/matplotlib/doc/api/type1font.rst
===================================================================
--- trunk/matplotlib/doc/api/type1font.rst                              (rev 0)
+++ trunk/matplotlib/doc/api/type1font.rst      2008-12-31 13:20:50 UTC (rev 
6718)
@@ -0,0 +1,8 @@
+
+:mod:`matplotlib.type1font`
+===========================
+
+.. automodule:: matplotlib.type1font
+   :members:
+   :undoc-members:
+   :show-inheritance:

Added: trunk/matplotlib/examples/pylab_examples/usetex_fonteffects.py
===================================================================
--- trunk/matplotlib/examples/pylab_examples/usetex_fonteffects.py              
                (rev 0)
+++ trunk/matplotlib/examples/pylab_examples/usetex_fonteffects.py      
2008-12-31 13:20:50 UTC (rev 6718)
@@ -0,0 +1,22 @@
+# This script demonstrates that font effects specified in your pdftex.map
+# are now supported in pdf usetex.
+
+import matplotlib
+matplotlib.rc('text', usetex=True)
+import pylab
+
+def setfont(font):
+    return r'\font\a %s at 14pt\a ' % font
+
+for y, font, text in zip(range(5),
+                         ['ptmr8r', 'ptmri8r', 'ptmro8r', 'ptmr8rn', 
'ptmrr8re'],
+                         ['Nimbus Roman No9 L ' + x for x in
+                          ['', 'Italics (real italics for comparison)',
+                           '(slanted)', '(condensed)', '(extended)']]):
+    pylab.text(0, y, setfont(font) + text)
+
+pylab.ylim(-1, 5)
+pylab.xlim(-0.2, 0.6)
+pylab.setp(pylab.gca(), frame_on=False, xticks=(), yticks=())
+pylab.title('Usetex font effects')
+pylab.savefig('usetex_fonteffects.pdf')

Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py     2008-12-30 
16:06:59 UTC (rev 6717)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py     2008-12-31 
13:20:50 UTC (rev 6718)
@@ -500,7 +500,7 @@
                 # from pdf.use14corefonts
                 fontdictObject = self._write_afm_font(filename)
             elif self.dviFontInfo.has_key(filename):
-                # a Type 1 font from a dvi file
+                # a Type 1 font from a dvi file; the filename is really the 
TeX name
                 fontdictObject = self.embedType1(filename, 
self.dviFontInfo[filename])
             else:
                 # a normal TrueType font
@@ -525,22 +525,25 @@
         return fontdictObject
 
     def embedType1(self, texname, fontinfo):
-        # TODO: font effects such as SlantFont
         matplotlib.verbose.report(
-            'Embedding Type 1 font ' + fontinfo.fontfile +
-            ' with encoding ' + (fontinfo.encodingfile or '(none)'),
+            'Embedding ' + texname +
+            ' which is the Type 1 font ' + fontinfo.fontfile +
+            ' with encoding ' + (fontinfo.encodingfile or '(none)') +
+            ' and effects ' + `fontinfo.effects`,
             'debug')
 
-        # Use FT2Font to get several font properties
-        font = FT2Font(fontinfo.fontfile)
+        t1font = type1font.Type1Font(fontinfo.fontfile)
+        if fontinfo.effects:
+            t1font = t1font.transform(fontinfo.effects)
 
         # Font descriptors may be shared between differently encoded
         # Type-1 fonts, so only create a new descriptor if there is no
         # existing descriptor for this font.
-        fontdesc = self.type1Descriptors.get(fontinfo.fontfile)
+        effects = (fontinfo.effects.get('slant', 0.0), 
fontinfo.effects.get('extend', 1.0))
+        fontdesc = self.type1Descriptors.get((fontinfo.fontfile, effects))
         if fontdesc is None:
-            fontdesc = self.createType1Descriptor(font, fontinfo.fontfile)
-            self.type1Descriptors[fontinfo.fontfile] = fontdesc
+            fontdesc = self.createType1Descriptor(t1font, fontinfo.fontfile)
+            self.type1Descriptors[(fontinfo.fontfile, effects)] = fontdesc
 
         # Widths
         widthsObject = self.reserveObject('font widths')
@@ -551,7 +554,7 @@
         fontdict = {
             'Type':           Name('Font'),
             'Subtype':        Name('Type1'),
-            'BaseFont':       Name(font.postscript_name),
+            'BaseFont':       Name(t1font.prop['FontName']),
             'FirstChar':      0,
             'LastChar':       len(fontinfo.widths) - 1,
             'Widths':         widthsObject,
@@ -571,14 +574,14 @@
         self.writeObject(fontdictObject, fontdict)
         return fontdictObject
 
-    def createType1Descriptor(self, font, fontfile):
+    def createType1Descriptor(self, t1font, fontfile):
         # Create and write the font descriptor and the font file
         # of a Type-1 font
         fontdescObject = self.reserveObject('font descriptor')
         fontfileObject = self.reserveObject('font file')
 
-        _, _, fullname, familyname, weight, italic_angle, fixed_pitch, \
-            ul_position, ul_thickness = font.get_ps_font_info()
+        italic_angle = t1font.prop['ItalicAngle']
+        fixed_pitch = t1font.prop['isFixedPitch']
 
         flags = 0
         if fixed_pitch:   flags |= 1 << 0  # fixed width
@@ -590,18 +593,20 @@
         if 0:             flags |= 1 << 17 # TODO: small caps
         if 0:             flags |= 1 << 18 # TODO: force bold
 
+        ft2font = FT2Font(fontfile)
+        
         descriptor = {
             'Type':        Name('FontDescriptor'),
-            'FontName':    Name(font.postscript_name),
+            'FontName':    Name(t1font.prop['FontName']),
             'Flags':       flags,
-            'FontBBox':    font.bbox,
+            'FontBBox':    ft2font.bbox,
             'ItalicAngle': italic_angle,
-            'Ascent':      font.ascender,
-            'Descent':     font.descender,
+            'Ascent':      ft2font.ascender,
+            'Descent':     ft2font.descender,
             'CapHeight':   1000, # TODO: find this out
             'XHeight':     500, # TODO: this one too
             'FontFile':    fontfileObject,
-            'FontFamily':  familyname,
+            'FontFamily':  t1font.prop['FamilyName'],
             'StemV':       50, # TODO
             # (see also revision 3874; but not all TeX distros have AFM files!)
             #'FontWeight': a number where 400 = Regular, 700 = Bold
@@ -609,7 +614,6 @@
 
         self.writeObject(fontdescObject, descriptor)
 
-        t1font = type1font.Type1Font(fontfile)
         self.beginStream(fontfileObject.id, None,
                          { 'Length1': len(t1font.parts[0]),
                            'Length2': len(t1font.parts[1]),
@@ -1369,14 +1373,14 @@
                     self.file.dviFontInfo[dvifont.texname] = Bunch(
                         fontfile=psfont.filename,
                         encodingfile=psfont.encoding,
+                        effects=psfont.effects,
                         widths=dvifont.widths,
                         dvifont=dvifont)
-                    # TODO: font effects
                 seq += [['font', pdfname, dvifont.size]]
                 oldfont = dvifont
             seq += [['text', x1, y1, [chr(glyph)], x1+width]]
 
-        # Find consecutive text strings with constant x coordinate and
+        # Find consecutive text strings with constant y coordinate and
         # combine into a sequence of strings and kerns, or just one
         # string (if any kerns would be less than 0.1 points).
         i, curx = 0, 0

Modified: trunk/matplotlib/lib/matplotlib/dviread.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/dviread.py  2008-12-30 16:06:59 UTC (rev 
6717)
+++ trunk/matplotlib/lib/matplotlib/dviread.py  2008-12-31 13:20:50 UTC (rev 
6718)
@@ -1,12 +1,14 @@
 """
 An experimental module for reading dvi files output by TeX. Several
 limitations make this not (currently) useful as a general-purpose dvi
-preprocessor.
+preprocessor, but it is currently used by the pdf backend for
+processing usetex text.
 
 Interface::
 
   dvi = Dvi(filename, 72)
-  for page in dvi:          # iterate over pages
+  # iterate over pages (but only one page is supported for now):
+  for page in dvi:
       w, h, d = page.width, page.height, page.descent
       for x,y,font,glyph,width in page.text:
           fontname = font.texname
@@ -49,7 +51,7 @@
         """
         Iterate through the pages of the file.
 
-        Returns (text, pages) pairs, where:
+        Returns (text, boxes) pairs, where:
           text is a list of (x, y, fontnum, glyphnum, width) tuples
           boxes is a list of (x, y, height, width) tuples
 
@@ -131,8 +133,8 @@
 
     def _arg(self, nbytes, signed=False):
         """
-        Read and return an integer argument "nbytes" long.
-        Signedness is determined by the "signed" keyword.
+        Read and return an integer argument *nbytes* long.
+        Signedness is determined by the *signed* keyword.
         """
         str = self.file.read(nbytes)
         value = ord(str[0])
@@ -144,7 +146,7 @@
 
     def _dispatch(self, byte):
         """
-        Based on the opcode "byte", read the correct kinds of
+        Based on the opcode *byte*, read the correct kinds of
         arguments from the dvi file and call the method implementing
         that opcode with those arguments.
         """
@@ -385,9 +387,27 @@
     Object that holds a font's texname and size, supports comparison,
     and knows the widths of glyphs in the same units as the AFM file.
     There are also internal attributes (for use by dviread.py) that
-    are _not_ used for comparison.
+    are *not* used for comparison.
 
     The size is in Adobe points (converted from TeX points).
+
+    .. attribute:: texname
+    
+       Name of the font as used internally by TeX and friends. This
+       is usually very different from any external font names, and
+       :class:`dviread.PsfontsMap` can be used to find the external
+       name of the font.
+
+    .. attribute:: size
+    
+       Size of the font in Adobe points, converted from the slightly
+       smaller TeX points.
+
+    .. attribute:: widths
+    
+       Widths of glyphs in glyph-space units, typically 1/1000ths of
+       the point size.
+    
     """
     __slots__ = ('texname', 'size', 'widths', '_scale', '_vf', '_tfm')
 
@@ -532,17 +552,27 @@
     A TeX Font Metric file. This implementation covers only the bare
     minimum needed by the Dvi class.
 
-    Attributes:
+    .. attribute:: checksum
 
-      checksum: for verifying against dvi file
+       Used for verifying against the dvi file.
 
-      design_size: design size of the font (in what units?)
+    .. attribute:: design_size
 
-      width[i]: width of character \#i, needs to be scaled
-        by the factor specified in the dvi file
-        (this is a dict because indexing may not start from 0)
+       Design size of the font (in what units?)
 
-      height[i], depth[i]: height and depth of character \#i
+    .. attribute::  width
+
+       Width of each character, needs to be scaled by the factor
+       specified in the dvi file. This is a dict because indexing may
+       not start from 0.
+
+    .. attribute:: height
+
+       Height of each character.
+    
+    .. attribute:: depth
+        
+       Depth of each character.
     """
     __slots__ = ('checksum', 'design_size', 'width', 'height', 'depth')
 
@@ -581,8 +611,20 @@
 class PsfontsMap(object):
     """
     A psfonts.map formatted file, mapping TeX fonts to PS fonts.
-    Usage: map = PsfontsMap('.../psfonts.map'); map['cmr10']
+    Usage::
 
+     >>> map = PsfontsMap(find_tex_file('pdftex.map'))
+     >>> entry = map['ptmbo8r']
+     >>> entry.texname
+     'ptmbo8r'
+     >>> entry.psname
+     'Times-Bold'
+     >>> entry.encoding
+     '/usr/local/texlive/2008/texmf-dist/fonts/enc/dvips/base/8r.enc'
+     >>> entry.effects
+     {'slant': 0.16700000000000001}
+     >>> entry.filename
+
     For historical reasons, TeX knows many Type-1 fonts by different
     names than the outside world. (For one thing, the names have to
     fit in eight characters.) Also, TeX's native fonts are not Type-1
@@ -594,11 +636,12 @@
     file names.
 
     A texmf tree typically includes mapping files called e.g.
-    psfonts.map, pdftex.map,  dvipdfm.map. psfonts.map is used by
+    psfonts.map, pdftex.map, dvipdfm.map. psfonts.map is used by
     dvips, pdftex.map by pdfTeX, and dvipdfm.map by dvipdfm.
-    psfonts.map might avoid embedding the 35 PostScript fonts, while
-    the pdf-related files perhaps only avoid the "Base 14" pdf fonts.
-    But the user may have configured these files differently.
+    psfonts.map might avoid embedding the 35 PostScript fonts (i.e.,
+    have no filename for them, as in the Times-Bold example above),
+    while the pdf-related files perhaps only avoid the "Base 14" pdf
+    fonts. But the user may have configured these files differently.
     """
     __slots__ = ('_font',)
 
@@ -655,10 +698,10 @@
         subsetting, but I have no example of << in my TeX installation.
         """
         texname, psname = words[:2]
-        effects, encoding, filename = [], None, None
+        effects, encoding, filename = '', None, None
         for word in words[2:]:
             if not word.startswith('<'):
-                effects.append(word)
+                effects = word
             else:
                 word = word.lstrip('<')
                 if word.startswith('['):
@@ -670,6 +713,18 @@
                 else:
                     assert filename is None
                     filename = word
+
+        eff = effects.split()
+        effects = {}
+        try:
+            effects['slant'] = float(eff[eff.index('SlantFont')-1])
+        except ValueError:
+            pass
+        try:
+            effects['extend'] = float(eff[eff.index('ExtendFont')-1])
+        except ValueError:
+            pass
+
         self._font[texname] = mpl_cbook.Bunch(
             texname=texname, psname=psname, effects=effects,
             encoding=encoding, filename=filename)
@@ -733,13 +788,18 @@
 
 def find_tex_file(filename, format=None):
     """
-    Call kpsewhich to find a file in the texmf tree.
-    If format is not None, it is used as the value for the --format option.
-    See the kpathsea documentation for more information.
+    Call :program:`kpsewhich` to find a file in the texmf tree. If
+    *format* is not None, it is used as the value for the
+    :option:`--format` option.
 
     Apparently most existing TeX distributions on Unix-like systems
     use kpathsea. I hear MikTeX (a popular distribution on Windows)
     doesn't use kpathsea, so what do we do? (TODO)
+
+    .. seealso::
+
+      `Kpathsea documentation <http://www.tug.org/kpathsea/>`_
+        The library that :program:`kpsewhich` is part of.
     """
 
     cmd = ['kpsewhich']

Modified: trunk/matplotlib/lib/matplotlib/type1font.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/type1font.py        2008-12-30 16:06:59 UTC 
(rev 6717)
+++ trunk/matplotlib/lib/matplotlib/type1font.py        2008-12-31 13:20:50 UTC 
(rev 6718)
@@ -1,37 +1,70 @@
 """
-A class representing a Type 1 font.
+This module contains a class representing a Type 1 font.
 
-This version merely reads pfa and pfb files and splits them for
-embedding in pdf files. There is no support yet for subsetting or
-anything like that.
+This version reads pfa and pfb files and splits them for embedding in
+pdf files. It also supports SlantFont and ExtendFont transformations,
+similarly to pdfTeX and friends. There is no support yet for
+subsetting.
 
-Usage (subject to change):
+Usage::
 
-   font = Type1Font(filename)
-   clear_part, encrypted_part, finale = font.parts
+   >>> font = Type1Font(filename)
+   >>> clear_part, encrypted_part, finale = font.parts
+   >>> slanted_font = font.transform({'slant': 0.167})
+   >>> extended_font = font.transform({'extend': 1.2})
 
-Source: Adobe Technical Note #5040, Supporting Downloadable PostScript
-Language Fonts.
+Sources:
 
-If extending this class, see also: Adobe Type 1 Font Format, Adobe
-Systems Incorporated, third printing, v1.1, 1993. ISBN 0-201-57044-0.
+* Adobe Technical Note #5040, Supporting Downloadable PostScript
+  Language Fonts.
+
+* Adobe Type 1 Font Format, Adobe Systems Incorporated, third printing,
+  v1.1, 1993. ISBN 0-201-57044-0.
 """
 
+import matplotlib.cbook as cbook
+import cStringIO
+import itertools
+import numpy as np
 import re
 import struct
 
 class Type1Font(object):
+    """
+    A class representing a Type-1 font, for use by backends.
 
-    def __init__(self, filename):
-        file = open(filename, 'rb')
-        try:
-            data = self._read(file)
-        finally:
-            file.close()
-        self.parts = self._split(data)
-        #self._parse()
+    .. attribute:: parts
 
+       A 3-tuple of the cleartext part, the encrypted part, and the
+       finale of zeros.
+
+    .. attribute:: prop
+
+       A dictionary of font properties.
+    """
+    __slots__ = ('parts', 'prop')
+
+    def __init__(self, input):
+        """
+        Initialize a Type-1 font. *input* can be either the file name of
+        a pfb file or a 3-tuple of already-decoded Type-1 font parts.
+        """
+        if isinstance(input, tuple) and len(input) == 3:
+            self.parts = input
+        else:
+            file = open(input, 'rb')
+            try:
+                data = self._read(file)
+            finally:
+                file.close()
+            self.parts = self._split(data)
+            
+        self._parse()
+
     def _read(self, file):
+        """
+        Read the font from a file, decoding into usable parts.
+        """
         rawdata = file.read()
         if not rawdata.startswith(chr(128)):
             return rawdata
@@ -100,85 +133,177 @@
         return data[:len1], binary, data[idx:]
 
     _whitespace = re.compile(r'[\0\t\r\014\n ]+')
-    _delim = re.compile(r'[()<>[]{}/%]')
     _token = re.compile(r'/{0,2}[^]\0\t\r\v\n ()<>{}/%[]+')
     _comment = re.compile(r'%[^\r\n\v]*')
     _instring = re.compile(r'[()\\]')
-    def _parse(self):
+    @classmethod
+    def _tokens(cls, text):
         """
-        A very limited kind of parsing to find the Encoding of the
-        font.
+        A PostScript tokenizer. Yield (token, value) pairs such as
+        ('whitespace', '   ') or ('name', '/Foobar').
         """
-        def tokens(text):
-            """
-            Yield pairs (position, token), ignoring comments and
-            whitespace. Numbers count as tokens.
-            """
-            pos = 0
-            while pos < len(text):
-                match = self._comment.match(text[pos:]) or 
self._whitespace.match(text[pos:])
+        pos = 0
+        while pos < len(text):
+            match = cls._comment.match(text[pos:]) or 
cls._whitespace.match(text[pos:])
+            if match:
+                yield ('whitespace', match.group())
+                pos += match.end()
+            elif text[pos] == '(':
+                start = pos
+                pos += 1
+                depth = 1
+                while depth:
+                    match = cls._instring.search(text[pos:])
+                    if match is None: return
+                    pos += match.end()
+                    if match.group() == '(':
+                        depth += 1
+                    elif match.group() == ')':
+                        depth -= 1
+                    else: # a backslash - skip the next character
+                        pos += 1
+                yield ('string', text[start:pos])
+            elif text[pos:pos+2] in ('<<', '>>'):
+                yield ('delimiter', text[pos:pos+2])
+                pos += 2
+            elif text[pos] == '<':
+                start = pos
+                pos += text[pos:].index('>')
+                yield ('string', text[start:pos])
+            else:
+                match = cls._token.match(text[pos:])
                 if match:
+                    try:
+                        float(match.group())
+                        yield ('number', match.group())
+                    except ValueError:
+                        yield ('name', match.group())
                     pos += match.end()
-                elif text[pos] == '(':
-                    start = pos
+                else:
+                    yield ('delimiter', text[pos])
                     pos += 1
-                    depth = 1
-                    while depth:
-                        match = self._instring.search(text[pos:])
-                        if match is None: return
-                        if match.group() == '(':
-                            depth += 1
-                            pos += 1
-                        elif match.group() == ')':
-                            depth -= 1
-                            pos += 1
-                        else:
-                            pos += 2
-                    yield (start, text[start:pos])
-                elif text[pos:pos+2] in ('<<', '>>'):
-                    yield (pos, text[pos:pos+2])
-                    pos += 2
-                elif text[pos] == '<':
-                    start = pos
-                    pos += text[pos:].index('>')
-                    yield (start, text[start:pos])
-                else:
-                    match = self._token.match(text[pos:])
-                    if match:
-                        yield (pos, match.group())
-                        pos += match.end()
+
+    def _parse(self):
+        """
+        Find the values of various font properties. This limited kind
+        of parsing is described in Chapter 10 "Adobe Type Manager
+        Compatibility" of the Type-1 spec.
+        """
+        # Start with reasonable defaults
+        prop = { 'weight': 'Regular', 'ItalicAngle': 0.0, 'isFixedPitch': 
False,
+                 'UnderlinePosition': -100, 'UnderlineThickness': 50 }
+        tokenizer = self._tokens(self.parts[0])
+        filtered = itertools.ifilter(lambda x: x[0] != 'whitespace', tokenizer)
+        for token, value in filtered:
+            if token == 'name' and value.startswith('/'):
+                key = value[1:]
+                token, value = filtered.next()
+                if token == 'name':
+                    if value in ('true', 'false'):
+                        value = value == 'true'
                     else:
-                        yield (pos, text[pos])
-                        pos += 1
+                        value = value.lstrip('/')
+                elif token == 'string':
+                    value = value.lstrip('(').rstrip(')')
+                elif token == 'number':
+                    if '.' in value: value = float(value)
+                    else: value = int(value)
+                else: # more complicated value such as an array
+                    value = None
+                if key != 'FontInfo' and value is not None:
+                    prop[key] = value
 
-        enc_starts, enc_ends = None, None
-        state = 0
-        # State transitions:
-        # 0 -> /Encoding -> 1
-        # 1 -> StandardEncoding -> 2 -> def -> (ends)
-        # 1 -> dup -> 4 -> put -> 5
-        # 5 -> dup -> 4 -> put -> 5
-        # 5 -> def -> (ends)
-        for pos,token in tokens(self.parts[0]):
-            if state == 0 and token == '/Encoding':
-                enc_starts = pos
-                state = 1
-            elif state == 1 and token == 'StandardEncoding':
-                state = 2
-            elif state in (2,5) and token == 'def':
-                enc_ends = pos+3
-                break
-            elif state in (1,5) and token == 'dup':
-                state = 4
-            elif state == 4 and token == 'put':
-                state = 5
-        self.enc_starts, self.enc_ends = enc_starts, enc_ends
+        # Fill in the various *Name properties
+        if not prop.has_key('FontName'):
+            prop['FontName'] = prop.get('FullName') or prop.get('FamilyName') 
or 'Unknown'
+        if not prop.has_key('FullName'):
+            prop['FullName'] = prop['FontName']
+        if not prop.has_key('FamilyName'):
+            extras = r'(?i)([ 
-](regular|plain|italic|oblique|(semi)?bold|(ultra)?light|extra|condensed))+$'
+            prop['FamilyName'] = re.sub(extras, '', prop['FullName'])
                 
+        self.prop = prop
+                        
+    @classmethod
+    def _transformer(cls, tokens, slant, extend):
+        def fontname(name):
+            result = name
+            if slant: result += '_Slant_' + str(int(1000*slant))
+            if extend != 1.0: result += '_Extend_' + str(int(1000*extend))
+            return result
+
+        def italicangle(angle):
+            return str(float(angle) - np.arctan(slant)/np.pi*180)
+
+        def fontmatrix(array):
+            array = array.lstrip('[').rstrip(']').strip().split()
+            array = [ float(x) for x in array ]
+            oldmatrix = np.eye(3,3)
+            oldmatrix[0:3,0] = array[::2]
+            oldmatrix[0:3,1] = array[1::2]
+            modifier = np.array([[extend, 0, 0],
+                                 [slant, 1, 0],
+                                 [0, 0, 1]])
+            newmatrix = np.dot(modifier, oldmatrix)
+            array[::2] = newmatrix[0:3,0]
+            array[1::2] = newmatrix[0:3,1]
+            return '[' + ' '.join(str(x) for x in array) + ']'
+
+        def replace(fun):
+            def replacer(tokens):
+                token, value = tokens.next()      # name, e.g. /FontMatrix
+                yield value
+                token, value = tokens.next()      # possible whitespace
+                while token == 'whitespace':
+                    yield value
+                    token, value = tokens.next()
+                if value != '[':                  # name/number/etc.
+                    yield fun(value)
+                else:                             # array, e.g. [1 2 3]
+                    array = []
+                    while value != ']':
+                        array += value
+                        token, value = tokens.next()
+                    array += value
+                    yield fun(''.join(array))
+            return replacer
+
+        def suppress(tokens):
+            for x in itertools.takewhile(lambda x: x[1] != 'def', tokens):
+                pass
+            yield ''
+        
+        table = { '/FontName': replace(fontname),
+                  '/ItalicAngle': replace(italicangle),
+                  '/FontMatrix': replace(fontmatrix),
+                  '/UniqueID': suppress }
+
+        while True:
+            token, value = tokens.next()
+            if token == 'name' and value in table:
+                for value in table[value](itertools.chain([(token, value)], 
tokens)):
+                    yield value
+            else:
+                yield value
+                        
+    def transform(self, effects):
+        """
+        Transform the font by slanting or extending. *effects* should
+        be a dict where ``effects['slant']`` is the tangent of the
+        angle that the font is to be slanted to the right (so negative
+        values slant to the left) and ``effects['extend']`` is the
+        multiplier by which the font is to be extended (so values less
+        than 1.0 condense). Returns a new :class:`Type1Font` object.
+        """
+
+        buffer = cStringIO.StringIO()
+        tokenizer = self._tokens(self.parts[0])
+        for value in self._transformer(tokenizer,
+                                       slant=effects.get('slant', 0.0),
+                                       extend=effects.get('extend', 1.0)):
+            buffer.write(value)
+        result = buffer.getvalue()
+        buffer.close()
+
+        return Type1Font((result, self.parts[1], self.parts[2]))
     
-if __name__ == '__main__':
-    import sys
-    font = Type1Font(sys.argv[1])
-    parts = font.parts
-    print len(parts[0]), len(parts[1]), len(parts[2])
-    #print parts[0][font.enc_starts:font.enc_ends]
-


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
_______________________________________________
Matplotlib-checkins mailing list
Matplotlib-checkins@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to