8000 Move from FT2Font to freetypy for all uses · matplotlib/matplotlib@796d58a · GitHub
[go: up one dir, main page]

Skip to content

Commit 796d58a

Browse files
committed
Move from FT2Font to freetypy for all uses
1 parent 6cdf405 commit 796d58a

File tree

8 files changed

+230
-218
lines changed

8 files changed

+230
-218
lines changed

lib/matplotlib/backends/backend_agg.py

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
* alpha blending
1313
* DPI scaling properly - everything scales properly (dashes, linewidths, etc)
1414
* draw polygon
15-
* freetype2 w/ ft2font
1615
1716
TODO:
1817
@@ -24,17 +23,17 @@
2423

2524
from matplotlib.externals import six
2625

26+
import freetypy as ft
27+
2728
import threading
2829
import numpy as np
2930
from math import radians, cos, sin
3031
from matplotlib import verbose, rcParams
3132
from matplotlib.backend_bases import (RendererBase, FigureManagerBase,
3233
FigureCanvasBase)
33-
from matplotlib.cbook import is_string_like, maxdict, restrict_dict
34+
from matplotlib.cbook import is_string_like, restrict_dict
3435
from matplotlib.figure import Figure
3536
from matplotlib.font_manager import findfont, get_font
36-
from matplotlib.ft2font import (LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING,
37-
LOAD_DEFAULT, LOAD_NO_AUTOHINT)
3837
from matplotlib.mathtext import MathTextParser
3938
from matplotlib.path import Path
4039
from matplotlib.transforms import Bbox, BboxBase
@@ -43,6 +42,8 @@
4342
from matplotlib.backends._backend_agg import RendererAgg as _RendererAgg
4443
from matplotlib import _png
4544

45+
from matplotlib import font_util
46+
4647
try:
4748
from PIL import Image
4849
_has_pil = True
@@ -51,14 +52,15 @@
5152

5253
backend_version = 'v2.2'
5354

55+
5456
def get_hinting_flag():
5557
mapping = {
56-
True: LOAD_FORCE_AUTOHINT,
57-
False: LOAD_NO_HINTING,
58-
'either': LOAD_DEFAULT,
59-
'native': LOAD_NO_AUTOHINT,
60-
'auto': LOAD_FORCE_AUTOHINT,
61-
'none': LOAD_NO_HINTING
58+
True: ft.LOAD.FORCE_AUTOHINT,
59+
False: ft.LOAD.NO_HINTING,
60+
'either': ft.LOAD.DEFAULT,
61+
'native': ft.LOAD.NO_AUTOHINT,
62+
'auto': ft.LOAD.FORCE_AUTOHINT,
63+
'none': ft.LOAD.NO_HINTING
6264
}
6365
return mapping[rcParams['text.hinting']]
6466

@@ -193,24 +195,19 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
193195
font = self._get_agg_font(prop)
194196

195197
if font is None: return None
196-
if len(s) == 1 and ord(s) > 127:
197-
font.load_char(ord(s), flags=flags)
198-
else:
199-
# We pass '0' for angle here, since it will be rotated (in raster
200-
# space) in the following call to draw_text_image).
201-
font.set_text(s, 0, flags=flags)
202-
font.draw_glyphs_to_bitmap(antialiased=rcParams['text.antialiased'])
203-
d = font.get_descent() / 64.0
198+
199+
layout = ft.Layout(font, s)
200+
bm = font_util.draw_layout_to_bitmap(layout, font)
201+
d = layout.ink_bbox.y_min
204202
# The descent needs to be adjusted for the angle
205-
xo, yo = font.get_bitmap_offset()
206-
xo /= 64.0
207-
yo /= 64.0
203+
xo = layout.ink_bbox.x_min
204+
yo = 0
208205
xd = -d * sin(radians(angle))
209206
yd = d * cos(radians(angle))
210207

211208
#print x, y, int(x), int(y), s
212209
self._renderer.draw_text_image(
213-
font, round(x - xd + xo), round(y + yd + yo) + 1, angle, gc)
210+
bm, int(round(x - xd + xo)), int(round(y + yd + yo)) + 1, angle, gc)
214211

215212
def get_text_width_height_descent(self, s, prop, ismath):
216213
"""
@@ -237,12 +234,10 @@ def get_text_width_height_descent(self, s, prop, ismath):
237234

238235
flags = get_hinting_flag()
239236
font = self._get_agg_font(prop)
240-
font.set_text(s, 0.0, flags=flags) # the width and height of unrotated string
241-
w, h = font.get_width_height()
242-
d = font.get_descent()
243-
w /= 64.0 # convert from subpixels
244-
h /= 64.0
245-
d /= 64.0
237+
layout = ft.Layout(font, s, load_flags=flags) # the width and height of unrotated string
238+
w = layout.ink_bbox.width
239+
h = layout.ink_bbox.height
240+
d = layout.ink_bbox.y_min
246241
return w, h, d
247242

248243
def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None):
@@ -274,13 +269,10 @@ def _get_agg_font(self, prop):
274269
'debug-annoying')
275270

276271
fname = findfont(prop)
277-
font = get_font(
278-
fname,
279-
hinting_factor=rcParams['text.hinting_factor'])
272+
font = get_font(fname)
280273

281-
font.clear()
282274
size = prop.get_size_in_points()
283-
font.set_size(size, self.dpi)
275+
font.set_char_size(float(size), float(size), int(self.dpi), int(self.dpi))
284276

285277
return font
286278

lib/matplotlib/backends/backend_pdf.py

Lines changed: 53 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
from io import BytesIO
2121

2222
import numpy as np
23+
24+
import freetypy as ft
25+
2326
from matplotlib.externals.six import unichr
2427

2528

@@ -39,8 +42,6 @@
3942
from matplotlib.afm import AFM
4043
import matplotlib.type1font as type1font
4144
import matplotlib.dviread as dviread
42-
from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE,
43-
LOAD_NO_HINTING, KERNING_UNFITTED)
4445
from matplotlib.mathtext import MathTextParser
4546
from matplotlib.transforms import Affine2D, BboxBase
4647
from matplotlib.path import Path
@@ -757,16 +758,16 @@ def createType1Descriptor(self, t1font, fontfile):
757758
if 0:
758759
flags |= 1 << 18
759760

760-
ft2font = get_font(fontfile)
761+
font = get_font(fontfile)
761762

762763
descriptor = {
763764
'Type': Name('FontDescriptor'),
764765
'FontName': Name(t1font.prop['FontName']),
765766
'Flags': flags,
766-
'FontBBox': ft2font.bbox,
767+
'FontBBox': font.bbox,
767768
'ItalicAngle': italic_angle,
768-
'Ascent': ft2font.ascender,
769-
'Descent': ft2font.descender,
769+
'Ascent': font.ascender,
770+
'Descent': font.descender,
770771
'CapHeight': 1000, # TODO: find this out
771772
'XHeight': 500, # TODO: this one too
772773
'FontFile': fontfileObject,
@@ -820,7 +821,7 @@ def embedTTF(self, filename, characters):
820821
font = get_font(filename)
821822
fonttype = rcParams['pdf.fonttype']
822823

823-
def cvt(length, upe=font.units_per_EM, nearest=True):
824+
def cvt(length, upe=font.units_per_em, nearest=True):
824825
"Convert font coordinates to PDF glyph coordinates"
825826
value = length / upe * 1000
826827
if nearest:
@@ -840,7 +841,7 @@ def embedTTFType3(font, characters, descriptor):
840841
charprocsObject = self.reserveObject('character procs')
841842
differencesArray = []
842843
firstchar, lastchar = 0, 255
843-
bbox = [cvt(x, nearest=False) for x in font.bbox]
844+
bbox = [cvt(x * 64.0, nearest=False) for x in font.bbox]
844845

845846
fontdict = {
846847
'Type': Name('Font'),
@@ -861,20 +862,16 @@ def embedTTFType3(font, characters, descriptor):
861862

862863
# Make the "Widths" array
863864
from encodings import cp1252
864-
# The "decoding_map" was changed
865-
# to a "decoding_table" as of Python 2.5.
866-
if hasattr(cp1252, 'decoding_map'):
867-
def decode_char(charcode):
868-
return cp1252.decoding_map[charcode] or 0
869-
else:
870-
def decode_char(charcode):
871-
return ord(cp1252.decoding_table[charcode])
865+
866+
def decode_char(charcode):
867+
return ord(cp1252.decoding_table[charcode])
872868

873869
def get_char_width(charcode):
874870
s = decode_char(charcode)
875-
width = font.load_char(
876-
s, flags=LOAD_NO_SCALE | LOAD_NO_HINTING).horiAdvance
877-
return cvt(width)
871+
glyph = font.load_char_unicode(
872+
s, load_flags=ft.LOAD.NO_SCALE | ft.LOAD.NO_HINTING)
873+
width = glyph.linear_hori_advance
874+
return cvt(width * 64.0 * 2)
878875

879876
widths = [get_char_width(charcode)
880877
for charcode in range(firstchar, lastchar+1)]
@@ -886,11 +883,10 @@ def get_char_width(charcode):
886883
glyph_ids = []
887884
differences = []
888885
multi_byte_chars = set()
889-
for c in characters:
890-
ccode = c
891-
gind = font.get_char_index(ccode)
886+
for ccode in characters:
887+
gind = font.get_char_index_unicode(ccode)
892888
glyph_ids.append(gind)
893-
glyph_name = font.get_glyph_name(gind)
889+
glyph_name = font.get_char_name(ccode)
894890
if ccode <= 255:
895891
differences.append((ccode, glyph_name))
896892
else:
@@ -1000,11 +996,10 @@ def embedTTFType42(font, characters, descriptor):
1000996
cid_to_gid_map = ['\u0000'] * 65536
1001997
widths = []
1002998
max_ccode = 0
1003-
for c in characters:
1004-
ccode = c
1005-
gind = font.get_char_index(ccode)
1006-
glyph = font.load_char(ccode, flags=LOAD_NO_HINTING)
1007-
widths.append((ccode, glyph.horiAdvance / 6))
999+
for ccode in characters:
1000+
gind = font.get_char_index_unicode(ccode)
1001+
glyph = font.load_char_unicode(ccode, load_flags=ft.LOAD.NO_HINTING)
1002+
widths.append((ccode, glyph.linear_hori_advance * 64.))
10081003
if ccode < 65536:
10091004
cid_to_gid_map[ccode] = unichr(gind)
10101005
max_ccode = max(ccode, max_ccode)
@@ -1064,31 +1059,30 @@ def embedTTFType42(font, characters, descriptor):
10641059
# Beginning of main embedTTF function...
10651060

10661061
# You are lost in a maze of TrueType tables, all different...
1067-
sfnt = font.get_sfnt()
1068-
try:
1069-
ps_name = sfnt[(1, 0, 0, 6)].decode('macroman') # Macintosh scheme
1070-
except KeyError:
1071-
# Microsoft scheme:
1072-
ps_name = sfnt[(3, 1, 0x0409, 6)].decode('utf-16be')
1073-
# (see freetype/ttnameid.h)
1074-
ps_name = ps_name.encode('ascii', 'replace')
1062+
ps_name = font.get_postscript_name()
10751063
ps_name = Name(ps_name)
1076-
pclt = font.get_sfnt_table('pclt') or {'capHeight': 0, 'xHeight': 0}
1077-
post = font.get_sfnt_table('post') or {'italicAngle': (0, 0)}
1064+
if hasattr(font, 'tt_pclt'):
1065+
pclt = font.tt_pclt
1066+
else:
1067+
pclt = Bunch(cap_height=0, x_height=0)
1068+
if hasattr(font, 'tt_postscript'):
1069+
post = font.tt_postscript
1070+
else:
1071+
post = Bunch(italicAngle=0)
10781072
ff = font.face_flags
10791073
sf = font.style_flags
10801074

10811075
flags = 0
10821076
symbolic = False # ps_name.name in ('Cmsy10', 'Cmmi10', 'Cmex10')
1083-
if ff & FIXED_WIDTH:
1077+
if ff & ft.FACE_FLAG.FIXED_WIDTH:
10841078
flags |= 1 << 0
10851079
if 0: # TODO: serif
10861080
flags |= 1 << 1
10871081
if symbolic:
10881082
flags |= 1 << 2
10891083
else:
10901084
flags |= 1 << 5
1091-
if sf & ITALIC:
1085+
if sf & ft.STYLE_FLAG.ITALIC:
10921086
flags |= 1 << 6
10931087
if 0: # TODO: all caps
10941088
flags |= 1 << 16
@@ -1104,9 +1098,9 @@ def embedTTFType42(font, characters, descriptor):
11041098
'FontBBox': [cvt(x, nearest=False) for x in font.bbox],
11051099
'Ascent': cvt(font.ascender, nearest=False),
11061100
'Descent': cvt(font.descender, nearest=False),
1107-
'CapHeight': cvt(pclt['capHeight'], nearest=False),
1108-
'XHeight': cvt(pclt['xHeight']),
1109-
'ItalicAngle': post['italicAngle'][1], # ???
1101+
'CapHeight': cvt(pclt.cap_height, nearest=False),
1102+
'XHeight': cvt(pclt.x_height),
1103+
'ItalicAngle': post.italic_angle, # ???
11101104
'StemV': 0 # ???
11111105
}
11121106

@@ -1570,7 +1564,7 @@ def track_characters(self, font, s):
15701564
if isinstance(font, six.string_types):
15711565
fname = font
15721566
else:
1573-
fname = font.fname
1567+
fname = font.filename
15741568
realpath, stat_key = get_realpath_and_stat(fname)
15751569
used_characters = self.file.used_characters.setdefault(
15761570
stat_key, (realpath, set()))
@@ -1959,13 +1953,13 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
19591953
else:
19601954
font = self._get_font_ttf(prop)
19611955
self.track_characters(font, s)
1962-
font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
1956+
layout = ft.Layout(font, s, load_flags=ft.LOAD.NO_HINTING)
19631957

19641958
fonttype = rcParams['pdf.fonttype']
19651959

19661960
# We can't subset all OpenType fonts, so switch to Type 42
19671961
# in that case.
1968-
if is_opentype_cff_font(font.fname):
1962+
if is_opentype_cff_font(font.filename):
19691963
fonttype = 42
19701964

19711965
def check_simple_method(s):
@@ -2037,30 +2031,30 @@ def draw_text_woven(chunks):
20372031
lastgind = None
20382032
for c in chunk:
20392033
ccode = ord(c)
2040-
gind = font.get_char_index(ccode)
2034+
gind = font.get_char_index_unicode(ccode)
20412035
if gind is not None:
20422036
if mode == 2 and chunk_type == 2:
2043-
glyph_name = font.get_glyph_name(gind)
2037+
glyph_name = font.get_char_name(ccode)
20442038
self.file.output(Op.gsave)
20452039
self.file.output(0.001 * fontsize, 0,
20462040
0, 0.001 * fontsize,
20472041
newx, 0, Op.concat_matrix)
20482042
name = self.file._get_xobject_symbol_name(
2049-
font.fname, glyph_name)
2043+
font.filename, glyph_name)
20502044
self.file.output(Name(name), Op.use_xobject)
20512045
self.file.output(Op.grestore)
20522046

20532047
# Move the pointer based on the character width
20542048
# and kerning
2055-
glyph = font.load_char(ccode,
2056-
flags=LOAD_NO_HINTING)
2049+
# glyph = font.load_char_unicode(ccode, flags=ft.LOAD.NO_HINTING)
20572050
if lastgind is not None:
20582051
kern = font.get_kerning(
2059-
lastgind, gind, KERNING_UNFITTED)
2052+
lastgind, gind, ft.KERNING.UNFITTED).x
20602053
else:
20612054
kern = 0
20622055
lastgind = gind
2063-
newx += kern/64.0 + glyph.linearHoriAdvance/65536.0
2056+
glyph = font.load_glyph(gind, load_flags=ft.LOAD.NO_HINTING)
2057+
newx += kern + glyph.linear_hori_advance
20642058

20652059
if mode == 1:
20662060
self.file.output(Op.end_text)
@@ -2094,13 +2088,10 @@ def get_text_width_height_descent(self, s, prop, ismath):
20942088
d *= scale / 1000
20952089
else:
20962090
font = self._get_font_ttf(prop)
2097-
font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
2098-
w, h = font.get_width_height()
2099-
scale = (1.0 / 64.0)
2100-
w *= scale
2101-
h *= scale
2102-
d = font.get_descent()
2103-
d *= scale
2091+
layout = ft.Layout(font, s, load_flags=ft.LOAD.NO_HINTING)
2092+
w = layout.ink_bbox.width
2093+
h = layout.ink_bbox.height
2094+
d = layout.ink_bbox.y_min
21042095
return w, h, d
21052096

21062097
def _get_font_afm(self, prop):
@@ -2124,8 +2115,8 @@ def _get_font_afm(self, prop):
21242115
def _get_font_ttf(self, prop):
21252116
filename = findfont(prop)
21262117
font = get_font(filename)
2127-
font.clear()
2128-
font.set_size(prop.get_size_in_points(), 72)
2118+
font.set_char_size(prop.get_size_in_points(), prop.get_size_in_points(),
2119+
72, 72)
21292120
return font
21302121

21312122
def flipy(self):

0 commit comments

Comments
 (0)
0