From: <jo...@us...> - 2008-12-31 13:20:56
|
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. |