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

Skip to content

Commit 80321a3

Browse files
committed
Deal with Unicode and non-ASCII characters correctly when outputting
Postscript with ps.useafm == True. svn path=/trunk/matplotlib/; revision=3758
1 parent 37470b0 commit 80321a3

File tree

2 files changed

+118
-50
lines changed

2 files changed

+118
-50
lines changed

lib/matplotlib/afm.py

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
John D. Hunter <[email protected]>
3535
"""
3636

37-
3837
import sys, os
38+
from _mathtext_data import uni2type1
3939

4040
#Convert string the a python type
4141
_to_int = int
@@ -152,12 +152,13 @@ def _parse_char_metrics(fh):
152152
all the sample afm files I have
153153
"""
154154

155-
d = {}
155+
ascii_d = {}
156+
name_d = {}
156157
while 1:
157158
line = fh.readline()
158159
if not line: break
159160
line = line.rstrip()
160-
if line.startswith('EndCharMetrics'): return d
161+
if line.startswith('EndCharMetrics'): return ascii_d, name_d
161162
vals = line.split(';')[:4]
162163
if len(vals) !=4 : raise RuntimeError('Bad char metrics line: %s' % line)
163164
num = _to_int(vals[0].split()[1])
@@ -169,7 +170,8 @@ def _parse_char_metrics(fh):
169170
if name == 'Euro':
170171
num = 128
171172
if num != -1:
172-
d[num] = (wx, name, bbox)
173+
ascii_d[num] = (wx, name, bbox)
174+
name_d[name] = (wx, bbox)
173175
raise RuntimeError('Bad parse')
174176

175177
def _parse_kern_pairs(fh):
@@ -277,9 +279,9 @@ def parse_afm(fh):
277279
"""
278280
_sanity_check(fh)
279281
dhead = _parse_header(fh)
280-
dcmetrics = _parse_char_metrics(fh)
282+
dcmetrics_ascii, dcmetrics_name = _parse_char_metrics(fh)
281283
doptional = _parse_optional(fh)
282-
return dhead, dcmetrics, doptional[0], doptional[1]
284+
return dhead, dcmetrics_ascii, dcmetrics_name, doptional[0], doptional[1]
283285

284286

285287
class AFM:
@@ -288,10 +290,12 @@ def __init__(self, fh):
288290
"""
289291
Parse the AFM file in file object fh
290292
"""
291-
(dhead, dcmetrics, dkernpairs, dcomposite) = parse_afm(fh)
293+
(dhead, dcmetrics_ascii, dcmetrics_name, dkernpairs, dcomposite) = \
294+
parse_afm(fh)
292295
self._header = dhead
293296
self._kern = dkernpairs
294-
self._metrics = dcmetrics
297+
self._metrics = dcmetrics_ascii
298+
self._metrics_by_name = dcmetrics_name
295299
self._composite = dcomposite
296300

297301

@@ -340,9 +344,16 @@ def get_str_bbox(self, s):
340344
miny = 1e9
341345
maxy = 0
342346
left = 0
347+
if not isinstance(s, unicode):
348+
s = s.decode()
343349
for c in s:
344350
if c == '\n': continue
345-
wx, name, bbox = self._metrics[ord(c)]
351+
name = uni2type1.get(ord(c), 'question')
352+
try:
353+
wx, bbox = self._metrics_by_name[name]
354+
except KeyError:
355+
name = 'question'
356+
wx, bbox = self._metrics_by_name[name]
346357
l,b,w,h = bbox
347358
if l<left: left = l
348359
# find the width with kerning
@@ -377,6 +388,13 @@ def get_width_char(self, c, isord=False):
377388
wx, name, bbox = self._metrics[c]
378389
return wx
379390

391+
def get_width_from_char_name(self, name):
392+
"""
393+
Get the width of the character from a type1 character name
394+
"""
395+
wx, bbox = self._metrics_by_name[name]
396+
return wx
397+
380398
def get_height_char(self, c, isord=False):
381399
"""
382400
Get the height of character c from the bounding box. This is
@@ -392,9 +410,16 @@ def get_kern_dist(self, c1, c2):
392410
c2
393411
"""
394412
name1, name2 = self.get_name_char(c1), self.get_name_char(c2)
413+
return self.get_kern_dist_from_name(name1, name2)
414+
415+
def get_kern_dist_from_name(self, name1, name2):
416+
"""
417+
Return the kerning pair distance (possibly 0) for chars c1 and
418+
c2
419+
"""
395420
try: return self._kern[ (name1, name2) ]
396421
except: return 0
397-
422+
398423
def get_fontname(self):
399424
"Return the font name, eg, Times-Roman"
400425
return self._header['FontName']

lib/matplotlib/backends/backend_ps.py

Lines changed: 83 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def _fn_name(): return sys._getframe(1).f_code.co_name
2222
from matplotlib.ft2font import FT2Font, KERNING_DEFAULT, LOAD_NO_HINTING
2323
from matplotlib.ttconv import convert_ttf_to_ps
2424
from matplotlib.mathtext import MathTextParser
25+
from matplotlib._mathtext_data import uni2type1
2526
from matplotlib.text import Text
2627

2728
from matplotlib.transforms import get_vec6_scales
@@ -700,8 +701,10 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath):
700701
elif ismath:
701702
return self.draw_mathtext(gc, x, y, s, prop, angle)
702703

704+
elif isinstance(s, unicode):
705+
return self.draw_unicode(gc, x, y, s, prop, angle)
706+
703707
elif rcParams['ps.useafm']:
704-
if ismath: s = s[1:-1]
705708
font = self._get_font_afm(prop)
706709

707710
l,b,w,h = font.get_str_bbox(s)
@@ -735,8 +738,6 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath):
735738
""" % locals()
736739
self._draw_ps(ps, gc, None)
737740

738-
elif isinstance(s, unicode):
739-
return self.draw_unicode(gc, x, y, s, prop, angle)
740741
else:
741742
font = self._get_font_ttf(prop)
742743
font.set_text(s, 0, flags=LOAD_NO_HINTING)
@@ -762,48 +763,90 @@ def draw_unicode(self, gc, x, y, s, prop, angle):
762763
"""draw a unicode string. ps doesn't have unicode support, so
763764
we have to do this the hard way
764765
"""
766+
if rcParams['ps.useafm']:
767+
self.set_color(*gc.get_rgb())
765768

766-
font = self._get_font_ttf(prop)
767-
768-
self.set_color(*gc.get_rgb())
769-
self.set_font(font.get_sfnt()[(1,0,0,6)], prop.get_size_in_points())
770-
self.track_characters(font, s)
771-
772-
cmap = font.get_charmap()
773-
lastgind = None
774-
#print 'text', s
775-
lines = []
776-
thisx, thisy = 0,0
777-
for c in s:
778-
ccode = ord(c)
779-
gind = cmap.get(ccode)
780-
if gind is None:
781-
ccode = ord('?')
782-
name = '.notdef'
783-
gind = 0
784-
else:
785-
name = font.get_glyph_name(gind)
786-
glyph = font.load_char(ccode, flags=LOAD_NO_HINTING)
787-
788-
if lastgind is not None:
789-
kern = font.get_kerning(lastgind, gind, KERNING_DEFAULT)
790-
else:
791-
kern = 0
792-
lastgind = gind
793-
thisx += kern/64.0
794-
795-
lines.append('%f %f m /%s glyphshow'%(thisx, thisy, name))
796-
thisx += glyph.linearHoriAdvance/65536.0
797-
798-
799-
thetext = '\n'.join(lines)
800-
ps = """gsave
769+
font = self._get_font_afm(prop)
770+
fontname = font.get_fontname()
771+
fontsize = prop.get_size_in_points()
772+
scale = 0.001*fontsize
773+
774+
thisx, thisy = 0, 0
775+
last_name = None
776+
lines = []
777+
for c in s:
778+
name = uni2type1.get(ord(c), 'question')
779+
try:
780+
width = font.get_width_from_char_name(name)
781+
except KeyError:
782+
name = 'question'
783+
width = font.get_width_char('?')
784+
if last_name is not None:
785+
kern = font.get_kern_dist_from_name(last_name, name)
786+
else:
787+
kern = 0
788+
last_name = name
789+
thisx += kern * scale
790+
791+
lines.append('%f %f m /%s glyphshow'%(thisx, thisy, name))
792+
793+
thisx += width * scale
794+
795+
thetext = "\n".join(lines)
796+
ps = """\
797+
gsave
798+
/%(fontname)s findfont
799+
%(fontsize)s scalefont
800+
setfont
801801
%(x)f %(y)f translate
802802
%(angle)f rotate
803803
%(thetext)s
804804
grestore
805-
""" % locals()
806-
self._pswriter.write(ps)
805+
""" % locals()
806+
self._pswriter.write(ps)
807+
808+
else:
809+
font = self._get_font_ttf(prop)
810+
811+
self.set_color(*gc.get_rgb())
812+
self.set_font(font.get_sfnt()[(1,0,0,6)], prop.get_size_in_points())
813+
self.track_characters(font, s)
814+
815+
cmap = font.get_charmap()
816+
lastgind = None
817+
#print 'text', s
818+
lines = []
819+
thisx, thisy = 0,0
820+
for c in s:
821+
ccode = ord(c)
822+
gind = cmap.get(ccode)
823+
if gind is None:
824+
ccode = ord('?')
825+
name = '.notdef'
826+
gind = 0
827+
else:
828+
name = font.get_glyph_name(gind)
829+
glyph = font.load_char(ccode, flags=LOAD_NO_HINTING)
830+
831+
if lastgind is not None:
832+
kern = font.get_kerning(lastgind, gind, KERNING_DEFAULT)
833+
else:
834+
kern = 0
835+
lastgind = gind
836+
thisx += kern/64.0
837+
838+
lines.append('%f %f m /%s glyphshow'%(thisx, thisy, name))
839+
thisx += glyph.linearHoriAdvance/65536.0
840+
841+
842+
thetext = '\n'.join(lines)
843+
ps = """gsave
844+
%(x)f %(y)f translate
845+
%(angle)f rotate
846+
%(thetext)s
847+
grestore
848+
""" % locals()
849+
self._pswriter.write(ps)
807850

808851

809852
def draw_mathtext(self, gc,

0 commit comments

Comments
 (0)