8000 WIP: New FreeType wrappers by mdboom · Pull Request #5414 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

WIP: New FreeType wrappers #5414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
038cd9c
Remove ft2font
mdboom Nov 5, 2015
5e4c36e
Install freetypy from git
mdboom Nov 5, 2015
0cc61e9
Remove tests and examples that are now obsolete
mdboom Nov 5, 2015
d97def9
Use freetypy in font_manager.py
mdboom Nov 5, 2015
23c2f07
Move from FT2Font to freetypy for all uses
mdboom Nov 5, 2015
c91d988
Update comment
mdboom Nov 5, 2015
440fc51
Move local freetype building resp. to freetypy
mdboom Nov 5, 2015
632a6f6
Bugfix for AFM fonts
mdboom Nov 5, 2015
72cedd3
Remove usage of ttconv, in favor of freetypy.subset
mdboom Nov 18, 2015
4eaa209
Use font config in place of font_manager
mdboom Nov 18, 2015
04c14db
Font layout details
mdboom Nov 18, 2015
d8ca0d5
Bug fixes in new font universe
mdboom Dec 1, 2015
28fb1dc
Build freetypy with specific version of freetype
mdboom Dec 1, 2015
b7d73af
Install fcpy on Travis
mdboom Jan 22, 2016
d45129f
Minor fixes and consistency cleanups
mdboom Jan 22, 2016
cbd59dc
Fix merge
mdboom Jan 22, 2016
19bc0a3
Fix y-alignment
mdboom Apr 11, 2016
2825e44
Fix SVG
mdboom Apr 11, 2016
39a5e79
Fix y-alignment
mdboom Apr 11, 2016
1dfb7b8
Fix rendering of mathtext
mdboom Apr 11, 2016
d2ceb8e
Fix deprecation warnings
mdboom Apr 12, 2016
0208eb5
Remove obsolete test
mdboom Apr 19, 2016
ed07ac8
Fix requirements
mdboom Apr 28, 2016
2261d48
Narrow builds no longer relevant
mdboom Apr 28, 2016
e7e8065
Improve layout-to-bitmap API
mdboom Apr 28, 2016
e3f921e
Fix spacing in PDF
mdboom Apr 28, 2016
6ce033f
Fix slant handling
mdboom Apr 28, 2016
641d2b4
Make single directory font lookup work
mdboom Apr 28, 2016
b55c8eb
Fix deprecated rcParam warnings
mdboom Apr 28, 2016
607767e
Fix font styles test
mdboom Apr 28, 2016
e037636
Fix layout in legends
mdboom Apr 28, 2016
d00777d
Bugfix
mdboom Apr 28, 2016
4c68718
Handle AFM files correctly
mdboom Apr 29, 2016
0d10ec1
Use new layout API
mdboom Apr 29, 2016
9d6632f
Update test images
mdboom Apr 29, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move from FT2Font to freetypy for all uses
  • Loading branch information
mdboom committed Apr 11, 2016
commit 23c2f079333ed6c7c7b04e36ebbf65801c1c3529
58 changes: 25 additions & 33 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
* alpha blending
* DPI scaling properly - everything scales properly (dashes, linewidths, etc)
* draw polygon
* freetype2 w/ ft2font

TODO:

Expand All @@ -24,17 +23,17 @@

from matplotlib.externals import six

import freetypy as ft

import threading
import numpy as np
from math import radians, cos, sin
from matplotlib import verbose, rcParams
from matplotlib.backend_bases import (RendererBase, FigureManagerBase,
FigureCanvasBase)
from matplotlib.cbook import is_string_like, maxdict, restrict_dict
from matplotlib.cbook import is_string_like, restrict_dict
from matplotlib.figure import Figure
from matplotlib.font_manager import findfont, get_font
from matplotlib.ft2font import (LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING,
LOAD_DEFAULT, LOAD_NO_AUTOHINT)
from matplotlib.mathtext import MathTextParser
from matplotlib.path import Path
from matplotlib.transforms import Bbox, BboxBase
Expand All @@ -43,6 +42,8 @@
from matplotlib.backends._backend_agg import RendererAgg as _RendererAgg
from matplotlib import _png

from matplotlib import font_util

try:
from PIL import Image
_has_pil = True
Expand All @@ -51,14 +52,15 @@

backend_version = 'v2.2'


def get_hinting_flag():
mapping = {
True: LOAD_FORCE_AUTOHINT,
False: LOAD_NO_HINTING,
'either': LOAD_DEFAULT,
'native': LOAD_NO_AUTOHINT,
'auto': LOAD_FORCE_AUTOHINT,
'none': LOAD_NO_HINTING
True: ft.LOAD.FORCE_AUTOHINT,
False: ft.LOAD.NO_HINTING,
'either': ft.LOAD.DEFAULT,
'native': ft.LOAD.NO_AUTOHINT,
'auto': ft.LOAD.FORCE_AUTOHINT,
'none': ft.LOAD.NO_HINTING
}
return mapping[rcParams['text.hinting']]

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

if font is None: return None
if len(s) == 1 and ord(s) > 127:
font.load_char(ord(s), flags=flags)
else:
# We pass '0' for angle here, since it will be rotated (in raster
# space) in the following call to draw_text_image).
font.set_text(s, 0, flags=flags)
font.draw_glyphs_to_bitmap(antialiased=rcParams['text.antialiased'])
d = font.get_descent() / 64.0

layout = ft.Layout(font, s)
bm = font_util.draw_layout_to_bitmap(layout, font)
d = layout.ink_bbox.y_min
# The descent needs to be adjusted for the angle
xo, yo = font.get_bitmap_offset()
xo /= 64.0
yo /= 64.0
xo = layout.ink_bbox.x_min
yo = 0
xd = -d * sin(radians(angle))
yd = d * cos(radians(angle))

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

def get_text_width_height_descent(self, s, prop, ismath):
"""
Expand All @@ -237,12 +234,10 @@ def get_text_width_height_descent(self, s, prop, ismath):

flags = get_hinting_flag()
font = self._get_agg_font(prop)
font.set_text(s, 0.0, flags=flags) # the width and height of unrotated string
w, h = font.get_width_height()
d = font.get_descent()
w /= 64.0 # convert from subpixels
h /= 64.0
d /= 64.0
layout = ft.Layout(font, s, load_flags=flags) # the width and height of unrotated string
w = layout.ink_bbox.width
h = layout.ink_bbox.height
d = layout.ink_bbox.y_min
return w, h, d

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

fname = findfont(prop)
font = get_font(
fname,
hinting_factor=rcParams['text.hinting_factor'])
font = get_font(fname)

font.clear()
size = prop.get_size_in_points()
font.set_size(size, self.dpi)
font.set_char_size(float(size), float(size), int(self.dpi), int(self.dpi))

return font

Expand Down
115 changes: 53 additions & 62 deletions lib/matplotlib/backends/backend_pdf.py
9E19
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
from io import BytesIO

import numpy as np

import freetypy as ft

from matplotlib.externals.six import unichr


Expand All @@ -39,8 +42,6 @@
from matplotlib.afm import AFM
import matplotlib.type1font as type1font
import matplotlib.dviread as dviread
from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE,
LOAD_NO_HINTING, KERNING_UNFITTED)
from matplotlib.mathtext import MathTextParser
from matplotlib.transforms import Affine2D, BboxBase
from matplotlib.path import Path
Expand Down Expand Up @@ -757,16 +758,16 @@ def createType1Descriptor(self, t1font, fontfile):
if 0:
flags |= 1 << 18

ft2font = get_font(fontfile)
font = get_font(fontfile)

descriptor = {
'Type': Name('FontDescriptor'),
'FontName': Name(t1font.prop['FontName']),
'Flags': flags,
'FontBBox': ft2font.bbox,
'FontBBox': font.bbox,
'ItalicAngle': italic_angle,
'Ascent': ft2font.ascender,
'Descent': ft2font.descender,
'Ascent': font.ascender,
'Descent': font.descender,
'CapHeight': 1000, # TODO: find this out
'XHeight': 500, # TODO: this one too
'FontFile': fontfileObject,
Expand Down Expand Up @@ -820,7 +821,7 @@ def embedTTF(self, filename, characters):
font = get_font(filename)
fonttype = rcParams['pdf.fonttype']

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

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

# Make the "Widths" array
from encodings import cp1252
# The "decoding_map" was changed
# to a "decoding_table" as of Python 2.5.
if hasattr(cp1252, 'decoding_map'):
def decode_char(charcode):
return cp1252.decoding_map[charcode] or 0
else:
def decode_char(charcode):
return ord(cp1252.decoding_table[charcode])

def decode_char(charcode):
return ord(cp1252.decoding_table[charcode])

def get_char_width(charcode):
s = decode_char(charcode)
width = font.load_char(
s, flags=LOAD_NO_SCALE | LOAD_NO_HINTING).horiAdvance
return cvt(width)
glyph = font.load_char_unicode(
s, load_flags=ft.LOAD.NO_SCALE | ft.LOAD.NO_HINTING)
width = glyph.linear_hori_advance
return cvt(width * 64.0 * 2)

widths = [get_char_width(charcode)
for charcode in range(firstchar, lastchar+1)]
Expand All @@ -886,11 +883,10 @@ def get_char_width(charcode):
glyph_ids = []
differences = []
multi_byte_chars = set()
for c in characters:
ccode = c
gind = font.get_char_index(ccode)
for ccode in characters:
gind = font.get_char_index_unicode(ccode)
glyph_ids.append(gind)
glyph_name = font.get_glyph_name(gind)
glyph_name = font.get_char_name(ccode)
if ccode <= 255:
differences.append((ccode, glyph_name))
else:
Expand Down Expand Up @@ -1000,11 +996,10 @@ def embedTTFType42(font, characters, descriptor):
cid_to_gid_map = ['\u0000'] * 65536
widths = []
max_ccode = 0
for c in characters:
ccode = c
gind = font.get_char_index(ccode)
glyph = font.load_char(ccode, flags=LOAD_NO_HINTING)
widths.append((ccode, glyph.horiAdvance / 6))
for ccode in characters:
gind = font.get_char_index_unicode(ccode)
glyph = font.load_char_unicode(ccode, load_flags=ft.LOAD.NO_HINTING)
widths.append((ccode, glyph.linear_hori_advance * 64.))
if ccode < 65536:
cid_to_gid_map[ccode] = unichr(gind)
max_ccode = max(ccode, max_ccode)
Expand Down Expand Up @@ -1064,31 +1059,30 @@ def embedTTFType42(font, characters, descriptor):
# Beginning of main embedTTF function...

# You are lost in a maze of TrueType tables, all different...
sfnt = font.get_sfnt()
try:
ps_name = sfnt[(1, 0, 0, 6)].decode('macroman') # Macintosh scheme
except KeyError:
# Microsoft scheme:
ps_name = sfnt[(3, 1, 0x0409, 6)].decode('utf-16be')
# (see freetype/ttnameid.h)
ps_name = ps_name.encode('ascii', 'replace')
ps_name = font.get_postscript_name()
ps_name = Name(ps_name)
pclt = font.get_sfnt_table('pclt') or {'capHeight': 0, 'xHeight': 0}
post = font.get_sfnt_table('post') or {'italicAngle': (0, 0)}
if hasattr(font, 'tt_pclt'):
pclt = font.tt_pclt
else:
pclt = Bunch(cap_height=0, x_height=0)
if hasattr(font, 'tt_postscript'):
post = font.tt_postscript
else:
post = Bunch(italicAngle=0)
ff = font.face_flags
sf = font.style_flags

flags = 0
symbolic = False # ps_name.name in ('Cmsy10', 'Cmmi10', 'Cmex10')
if ff & FIXED_WIDTH:
if ff & ft.FACE_FLAG.FIXED_WIDTH:
flags |= 1 << 0
if 0: # TODO: serif
flags |= 1 << 1
if symbolic:
flags |= 1 << 2
else:
flags |= 1 << 5
if sf & ITALIC:
if sf & ft.STYLE_FLAG.ITALIC:
flags |= 1 << 6
if 0: # TODO: all caps
flags |= 1 << 16
Expand All @@ -1104,9 +1098,9 @@ def embedTTFType42(font, characters, descriptor):
'FontBBox': [cvt(x, nearest=False) for x in font.bbox],
'Ascent': cvt(font.ascender, nearest=False),
'Descent': cvt(font.descender, nearest=False),
'CapHeight': cvt(pclt['capHeight'], nearest=False),
'XHeight': cvt(pclt['xHeight']),
'ItalicAngle': post['italicAngle'][1], # ???
'CapHeight': cvt(pclt.cap_height, nearest=False),
'XHeight': cvt(pclt.x_height),
'ItalicAngle': post.italic_angle, # ???
'StemV': 0 # ???
}

Expand Down Expand Up @@ -1571,7 +1565,7 @@ def track_characters(self, font, s):
if isinstance(font, six.string_types):
fname = font
else:
fname = font.fname
fname = font.filename
realpath, stat_key = get_realpath_and_stat(fname)
used_characters = self.file.used_characters.setdefault(
stat_key, (realpath, set()))
Expand Down Expand Up @@ -1959,13 +1953,13 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
else:
font = self._get_font_ttf(prop)
self.track_characters(font, s)
font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
layout = ft.Layout(font, s, load_flags=ft.LOAD.NO_HINTING)

fonttype = rcParams['pdf.fonttype']

# We can't subset all OpenType fonts, so switch to Type 42
# in that case.
if is_opentype_cff_font(font.fname):
if is_opentype_cff_font(font.filename):
fonttype = 42

def check_simple_method(s):
Expand Down Expand Up @@ -2037,30 +2031,30 @@ def draw_text_woven(chunks):
lastgind = None
for c in chunk:
ccode = ord(c)
gind = font.get_char_index(ccode)
gind = font.get_char_index_unicode(ccode)
if gind is not None:
if mode == 2 and chunk_type == 2:
glyph_name = font.get_glyph_name(gind)
glyph_name = font.get_char_name(ccode)
self.file.output(Op.gsave)
self.file.output(0.001 * fontsize, 0,
0, 0.001 * fontsize,
newx, 0, Op.concat_matrix)
name = self.file._get_xobject_symbol_name(
font.fname, glyph_name)
font.filename, glyph_name)
self.file.output(Name(name), Op.use_xobject)
self.file.output(Op.grestore)

# Move the pointer based on the character width
# and kerning
glyph = font.load_char(ccode,
flags=LOAD_NO_HINTING)
# glyph = font.load_char_unicode(ccode, flags=ft.LOAD.NO_HINTING)
if lastgind is not None:
kern = font.get_kerning(
lastgind, gind, KERNING_UNFITTED)
lastgind, gind, ft.KERNING.UNFITTED).x
else:
kern = 0
lastgind = gind
newx += kern/64.0 + glyph.linearHoriAdvance/65536.0
glyph = font.load_glyph(gind, load_flags=ft.LOAD.NO_HINTING)
newx += kern + glyph.linear_hori_advance

if mode == 1:
self.file.output(Op.end_text)
Expand Down Expand Up @@ -2094,13 +2088,10 @@ def get_text_width_height_descent(self, s, prop, ismath):
d *= scale / 1000
else:
font = self._get_font_ttf(prop)
font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
w, h = font.get_width_height()
scale = (1.0 / 64.0)
w *= scale
h *= scale
d = font.get_descent()
d *= scale
layout = ft.Layout(font, s, load_flags=ft.LOAD.NO_HINTING)
w = layout.ink_bbox.width
h = layout.ink_bbox.height
d = layout.ink_bbox.y_min
return w, h, d

def _get_font_afm(self, prop):
Expand All @@ -2124,8 +2115,8 @@ def _get_font_afm(self, prop):
def _get_font_ttf(self, prop):
filename = findfont(prop)
font = get_font(filename)
font.clear()
font.set_size(prop.get_size_in_points(), 72)
font.set_char_size(prop.get_size_in_points(), prop.get_size_in_points(),
72, 72)
return font

def flipy(self):
Expand Down
Loading
0