Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0f19207

Browse files
committed
Improve pdf usetex by adding support for font effects
svn path=/trunk/matplotlib/; revision=6718
1 parent fabab8c commit 0f19207

8 files changed

Lines changed: 366 additions & 134 deletions

File tree

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2008-12-31 Improve pdf usetex by adding support for font effects
2+
(slanting and extending). - JKS
3+
14
2008-12-29 Fix a bug in pdf usetex support, which occurred if the same
25
Type-1 font was used with different encodings, e.g. with
36
Minion Pro and MnSymbol. - JKS

doc/api/dviread.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
:mod:`matplotlib.dviread`
3+
=========================
4+
5+
.. automodule:: matplotlib.dviread
6+
:members:
7+
:undoc-members:
8+
:show-inheritance:

doc/api/index_backend_api.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ matplotlib backends
88
backend_gtkagg_api.rst
99
backend_qt4agg_api.rst
1010
backend_wxagg_api.rst
11+
dviread.rst
12+
type1font.rst

doc/api/type1font.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
:mod:`matplotlib.type1font`
3+
===========================
4+
5+
.. automodule:: matplotlib.type1font
6+
:members:
7+
:undoc-members:
8+
:show-inheritance:
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# This script demonstrates that font effects specified in your pdftex.map
2+
# are now supported in pdf usetex.
3+
4+
import matplotlib
5+
matplotlib.rc('text', usetex=True)
6+
import pylab
7+
8+
def setfont(font):
9+
return r'\font\a %s at 14pt\a ' % font
10+
11+
for y, font, text in zip(range(5),
12+
['ptmr8r', 'ptmri8r', 'ptmro8r', 'ptmr8rn', 'ptmrr8re'],
13+
['Nimbus Roman No9 L ' + x for x in
14+
['', 'Italics (real italics for comparison)',
15+
'(slanted)', '(condensed)', '(extended)']]):
16+
pylab.text(0, y, setfont(font) + text)
17+
18+
pylab.ylim(-1, 5)
19+
pylab.xlim(-0.2, 0.6)
20+
pylab.setp(pylab.gca(), frame_on=False, xticks=(), yticks=())
21+
pylab.title('Usetex font effects')
22+
pylab.savefig('usetex_fonteffects.pdf')

lib/matplotlib/backends/backend_pdf.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ def writeFonts(self):
500500
# from pdf.use14corefonts
501501
fontdictObject = self._write_afm_font(filename)
502502
elif self.dviFontInfo.has_key(filename):
503-
# a Type 1 font from a dvi file
503+
# a Type 1 font from a dvi file; the filename is really the TeX name
504504
fontdictObject = self.embedType1(filename, self.dviFontInfo[filename])
505505
else:
506506
# a normal TrueType font
@@ -525,22 +525,25 @@ def _write_afm_font(self, filename):
525525
return fontdictObject
526526

527527
def embedType1(self, texname, fontinfo):
528-
# TODO: font effects such as SlantFont
529528
matplotlib.verbose.report(
530-
'Embedding Type 1 font ' + fontinfo.fontfile +
531-
' with encoding ' + (fontinfo.encodingfile or '(none)'),
529+
'Embedding ' + texname +
530+
' which is the Type 1 font ' + fontinfo.fontfile +
531+
' with encoding ' + (fontinfo.encodingfile or '(none)') +
532+
' and effects ' + `fontinfo.effects`,
532533
'debug')
533534

534-
# Use FT2Font to get several font properties
535-
font = FT2Font(fontinfo.fontfile)
535+
t1font = type1font.Type1Font(fontinfo.fontfile)
536+
if fontinfo.effects:
537+
t1font = t1font.transform(fontinfo.effects)
536538

537539
# Font descriptors may be shared between differently encoded
538540
# Type-1 fonts, so only create a new descriptor if there is no
539541
# existing descriptor for this font.
540-
fontdesc = self.type1Descriptors.get(fontinfo.fontfile)
542+
effects = (fontinfo.effects.get('slant', 0.0), fontinfo.effects.get('extend', 1.0))
543+
fontdesc = self.type1Descriptors.get((fontinfo.fontfile, effects))
541544
if fontdesc is None:
542-
fontdesc = self.createType1Descriptor(font, fontinfo.fontfile)
543-
self.type1Descriptors[fontinfo.fontfile] = fontdesc
545+
fontdesc = self.createType1Descriptor(t1font, fontinfo.fontfile)
546+
self.type1Descriptors[(fontinfo.fontfile, effects)] = fontdesc
544547

545548
# Widths
546549
widthsObject = self.reserveObject('font widths')
@@ -551,7 +554,7 @@ def embedType1(self, texname, fontinfo):
551554
fontdict = {
552555
'Type': Name('Font'),
553556
'Subtype': Name('Type1'),
554-
'BaseFont': Name(font.postscript_name),
557+
'BaseFont': Name(t1font.prop['FontName']),
555558
'FirstChar': 0,
556559
'LastChar': len(fontinfo.widths) - 1,
557560
'Widths': widthsObject,
@@ -571,14 +574,14 @@ def embedType1(self, texname, fontinfo):
571574
self.writeObject(fontdictObject, fontdict)
572575
return fontdictObject
573576

574-
def createType1Descriptor(self, font, fontfile):
577+
def createType1Descriptor(self, t1font, fontfile):
575578
# Create and write the font descriptor and the font file
576579
# of a Type-1 font
577580
fontdescObject = self.reserveObject('font descriptor')
578581
fontfileObject = self.reserveObject('font file')
579582

580-
_, _, fullname, familyname, weight, italic_angle, fixed_pitch, \
581-
ul_position, ul_thickness = font.get_ps_font_info()
583+
italic_angle = t1font.prop['ItalicAngle']
584+
fixed_pitch = t1font.prop['isFixedPitch']
582585

583586
flags = 0
584587
if fixed_pitch: flags |= 1 << 0 # fixed width
@@ -590,26 +593,27 @@ def createType1Descriptor(self, font, fontfile):
590593
if 0: flags |= 1 << 17 # TODO: small caps
591594
if 0: flags |= 1 << 18 # TODO: force bold
592595

596+
ft2font = FT2Font(fontfile)
597+
593598
descriptor = {
594599
'Type': Name('FontDescriptor'),
595-
'FontName': Name(font.postscript_name),
600+
'FontName': Name(t1font.prop['FontName']),
596601
'Flags': flags,
597-
'FontBBox': font.bbox,
602+
'FontBBox': ft2font.bbox,
598603
'ItalicAngle': italic_angle,
599-
'Ascent': font.ascender,
600-
'Descent': font.descender,
604+
'Ascent': ft2font.ascender,
605+
'Descent': ft2font.descender,
601606
'CapHeight': 1000, # TODO: find this out
602607
'XHeight': 500, # TODO: this one too
603608
'FontFile': fontfileObject,
604-
'FontFamily': familyname,
609+
'FontFamily': t1font.prop['FamilyName'],
605610
'StemV': 50, # TODO
606611
# (see also revision 3874; but not all TeX distros have AFM files!)
607612
#'FontWeight': a number where 400 = Regular, 700 = Bold
608613
}
609614

610615
self.writeObject(fontdescObject, descriptor)
611616

612-
t1font = type1font.Type1Font(fontfile)
613617
self.beginStream(fontfileObject.id, None,
614618
{ 'Length1': len(t1font.parts[0]),
615619
'Length2': len(t1font.parts[1]),
@@ -1369,14 +1373,14 @@ def draw_tex(self, gc, x, y, s, prop, angle):
13691373
self.file.dviFontInfo[dvifont.texname] = Bunch(
13701374
fontfile=psfont.filename,
13711375
encodingfile=psfont.encoding,
1376+
effects=psfont.effects,
13721377
widths=dvifont.widths,
13731378
dvifont=dvifont)
1374-
# TODO: font effects
13751379
seq += [['font', pdfname, dvifont.size]]
13761380
oldfont = dvifont
13771381
seq += [['text', x1, y1, [chr(glyph)], x1+width]]
13781382

1379-
# Find consecutive text strings with constant x coordinate and
1383+
# Find consecutive text strings with constant y coordinate and
13801384
# combine into a sequence of strings and kerns, or just one
13811385
# string (if any kerns would be less than 0.1 points).
13821386
i, curx = 0, 0

lib/matplotlib/dviread.py

Lines changed: 84 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
"""
22
An experimental module for reading dvi files output by TeX. Several
33
limitations make this not (currently) useful as a general-purpose dvi
4-
preprocessor.
4+
preprocessor, but it is currently used by the pdf backend for
5+
processing usetex text.
56
67
Interface::
78
89
dvi = Dvi(filename, 72)
9-
for page in dvi: # iterate over pages
10+
# iterate over pages (but only one page is supported for now):
11+
for page in dvi:
1012
w, h, d = page.width, page.height, page.descent
1113
for x,y,font,glyph,width in page.text:
1214
fontname = font.texname
@@ -49,7 +51,7 @@ def __iter__(self):
4951
"""
5052
Iterate through the pages of the file.
5153
52-
Returns (text, pages) pairs, where:
54+
Returns (text, boxes) pairs, where:
5355
text is a list of (x, y, fontnum, glyphnum, width) tuples
5456
boxes is a list of (x, y, height, width) tuples
5557
@@ -131,8 +133,8 @@ def _read(self):
131133

132134
def _arg(self, nbytes, signed=False):
133135
"""
134-
Read and return an integer argument "nbytes" long.
135-
Signedness is determined by the "signed" keyword.
136+
Read and return an integer argument *nbytes* long.
137+
Signedness is determined by the *signed* keyword.
136138
"""
137139
str = self.file.read(nbytes)
138140
value = ord(str[0])
@@ -144,7 +146,7 @@ def _arg(self, nbytes, signed=False):
144146

145147
def _dispatch(self, byte):
146148
"""
147-
Based on the opcode "byte", read the correct kinds of
149+
Based on the opcode *byte*, read the correct kinds of
148150
arguments from the dvi file and call the method implementing
149151
that opcode with those arguments.
150152
"""
@@ -385,9 +387,27 @@ class DviFont(object):
385387
Object that holds a font's texname and size, supports comparison,
386388
and knows the widths of glyphs in the same units as the AFM file.
387389
There are also internal attributes (for use by dviread.py) that
388-
are _not_ used for comparison.
390+
are *not* used for comparison.
389391
390392
The size is in Adobe points (converted from TeX points).
393+
394+
.. attribute:: texname
395+
396+
Name of the font as used internally by TeX and friends. This
397+
is usually very different from any external font names, and
398+
:class:`dviread.PsfontsMap` can be used to find the external
399+
name of the font.
400+
401+
.. attribute:: size
402+
403+
Size of the font in Adobe points, converted from the slightly
404+
smaller TeX points.
405+
406+
.. attribute:: widths
407+
408+
Widths of glyphs in glyph-space units, typically 1/1000ths of
409+
the point size.
410+
391411
"""
392412
__slots__ = ('texname', 'size', 'widths', '_scale', '_vf', '_tfm')
393413

@@ -532,17 +552,27 @@ class Tfm(object):
532552
A TeX Font Metric file. This implementation covers only the bare
533553
minimum needed by the Dvi class.
534554
535-
Attributes:
555+
.. attribute:: checksum
556+
557+
Used for verifying against the dvi file.
558+
559+
.. attribute:: design_size
536560
537-
checksum: for verifying against dvi file
561+
Design size of the font (in what units?)
538562
539-
design_size: design size of the font (in what units?)
563+
.. attribute:: width
540564
541-
width[i]: width of character \#i, needs to be scaled
542-
by the factor specified in the dvi file
543-
(this is a dict because indexing may not start from 0)
565+
Width of each character, needs to be scaled by the factor
566+
specified in the dvi file. This is a dict because indexing may
567+
not start from 0.
544568
545-
height[i], depth[i]: height and depth of character \#i
569+
.. attribute:: height
570+
571+
Height of each character.
572+
573+
.. attribute:: depth
574+
575+
Depth of each character.
546576
"""
547577
__slots__ = ('checksum', 'design_size', 'width', 'height', 'depth')
548578

@@ -581,7 +611,19 @@ def __init__(self, filename):
581611
class PsfontsMap(object):
582612
"""
583613
A psfonts.map formatted file, mapping TeX fonts to PS fonts.
584-
Usage: map = PsfontsMap('.../psfonts.map'); map['cmr10']
614+
Usage::
615+
616+
>>> map = PsfontsMap(find_tex_file('pdftex.map'))
617+
>>> entry = map['ptmbo8r']
618+
>>> entry.texname
619+
'ptmbo8r'
620+
>>> entry.psname
621+
'Times-Bold'
622+
>>> entry.encoding
623+
'/usr/local/texlive/2008/texmf-dist/fonts/enc/dvips/base/8r.enc'
624+
>>> entry.effects
625+
{'slant': 0.16700000000000001}
626+
>>> entry.filename
585627
586628
For historical reasons, TeX knows many Type-1 fonts by different
587629
names than the outside world. (For one thing, the names have to
@@ -594,11 +636,12 @@ class PsfontsMap(object):
594636
file names.
595637
596638
A texmf tree typically includes mapping files called e.g.
597-
psfonts.map, pdftex.map, dvipdfm.map. psfonts.map is used by
639+
psfonts.map, pdftex.map, dvipdfm.map. psfonts.map is used by
598640
dvips, pdftex.map by pdfTeX, and dvipdfm.map by dvipdfm.
599-
psfonts.map might avoid embedding the 35 PostScript fonts, while
600-
the pdf-related files perhaps only avoid the "Base 14" pdf fonts.
601-
But the user may have configured these files differently.
641+
psfonts.map might avoid embedding the 35 PostScript fonts (i.e.,
642+
have no filename for them, as in the Times-Bold example above),
643+
while the pdf-related files perhaps only avoid the "Base 14" pdf
644+
fonts. But the user may have configured these files differently.
602645
"""
603646
__slots__ = ('_font',)
604647

@@ -655,10 +698,10 @@ def _register(self, words):
655698
subsetting, but I have no example of << in my TeX installation.
656699
"""
657700
texname, psname = words[:2]
658-
effects, encoding, filename = [], None, None
701+
effects, encoding, filename = '', None, None
659702
for word in words[2:]:
660703
if not word.startswith('<'):
661-
effects.append(word)
704+
effects = word
662705
else:
663706
word = word.lstrip('<')
664707
if word.startswith('['):
@@ -670,6 +713,18 @@ def _register(self, words):
670713
else:
671714
assert filename is None
672715
filename = word
716+
717+
eff = effects.split()
718+
effects = {}
719+
try:
720+
effects['slant'] = float(eff[eff.index('SlantFont')-1])
721+
except ValueError:
722+
pass
723+
try:
724+
effects['extend'] = float(eff[eff.index('ExtendFont')-1])
725+
except ValueError:
726+
pass
727+
673728
self._font[texname] = mpl_cbook.Bunch(
674729
texname=texname, psname=psname, effects=effects,
675730
encoding=encoding, filename=filename)
@@ -733,13 +788,18 @@ def _parse(self, file):
733788

734789
def find_tex_file(filename, format=None):
735790
"""
736-
Call kpsewhich to find a file in the texmf tree.
737-
If format is not None, it is used as the value for the --format option.
738-
See the kpathsea documentation for more information.
791+
Call :program:`kpsewhich` to find a file in the texmf tree. If
792+
*format* is not None, it is used as the value for the
793+
:option:`--format` option.
739794
740795
Apparently most existing TeX distributions on Unix-like systems
741796
use kpathsea. I hear MikTeX (a popular distribution on Windows)
742797
doesn't use kpathsea, so what do we do? (TODO)
798+
799+
.. seealso::
800+
801+
`Kpathsea documentation <http://www.tug.org/kpathsea/>`_
802+
The library that :program:`kpsewhich` is part of.
743803
"""
744804

745805
cmd = ['kpsewhich']

0 commit comments

Comments
 (0)