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
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins