Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
84 changes: 75 additions & 9 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from matplotlib import _api, cbook
from matplotlib.backend_bases import (
_Backend, FigureCanvasBase, FigureManagerBase, RendererBase)
from matplotlib.dviread import Dvi
from matplotlib.font_manager import fontManager as _fontManager, get_font
from matplotlib.ft2font import LoadFlags, RenderMode
from matplotlib.mathtext import MathTextParser
Expand Down Expand Up @@ -266,19 +267,84 @@ def get_text_width_height_descent(self, s, prop, ismath):
def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None):
# docstring inherited
# todo, handle props, angle, origins

size = prop.get_size_in_points()

texmanager = self.get_texmanager()
if mpl.rcParams["text.latex.engine"] == "latex+dvipng":
Z = self.get_texmanager().get_grey(s, size, self.dpi)
Z = (Z * 0xff).astype(np.uint8)
w, h, d = self.get_text_width_height_descent(s, prop, ismath="TeX")
xd = d * math.sin(math.radians(angle))
yd = d * math.cos(math.radians(angle))
x = round(x + xd)
y = round(y + yd)
self._renderer.draw_text_image(Z, x, y, angle, gc)
return

dvifile = self.get_texmanager().make_dvi(s, size)
with Dvi(dvifile, self.dpi) as dvi:
page, = dvi

Z = texmanager.get_grey(s, size, self.dpi)
Z = np.array(Z * 255.0, np.uint8)
cos = math.cos(math.radians(angle))
sin = math.sin(math.radians(angle))

w, h, d = self.get_text_width_height_descent(s, prop, ismath="TeX")
xd = d * math.sin(math.radians(angle))
yd = d * math.cos(math.radians(angle))
x = round(x + xd)
y = round(y + yd)
self._renderer.draw_text_image(Z, x, y, angle, gc)
for text in page.text:
hf = mpl.rcParams["text.hinting_factor"]
# Resolving text.index will implicitly call get_font(), which
# resets the font transform, so it has to be done before explicitly
# setting the font transform below.
index = text.index
font = get_font(text.font_path)
font.set_size(text.font_size, self.dpi)
slant = text.font_effects.get("slant", 0)
extend = text.font_effects.get("extend", 1)
font._set_transform(
(0x10000 * np.array([[cos, -sin], [sin, cos]])
@ [[extend, extend * slant], [0, 1]]
@ [[1 / hf, 0], [0, 1]]).round().astype(int),
[round(0x40 * (x + text.x * cos - text.y * sin)),
# FreeType's y is upwards.
round(0x40 * (self.height - y + text.x * sin + text.y * cos))]
)
bitmap = font._render_glyph(
index, get_hinting_flag(),
RenderMode.NORMAL if gc.get_antialiased() else RenderMode.MONO)
buffer = np.asarray(bitmap.buffer)
if not gc.get_antialiased():
buffer *= 0xff
# draw_text_image's y is downwards & the bitmap bottom side.
self._renderer.draw_text_image(
buffer,
bitmap.left, int(self.height) - bitmap.top + buffer.shape[0],
0, gc)

rgba = gc.get_rgb()
if len(rgba) == 3 or gc.get_forced_alpha():
rgba = rgba[:3] + (gc.get_alpha(),)
gc1 = self.new_gc()
gc1.set_linewidth(0)
gc1.set_snap(gc.get_snap())
for box in page.boxes:
bx = box.x
by = box.y
bw = box.width
bh = box.height
if gc1.get_snap() in [None, True]:
# Prevent thin bars from disappearing by growing symmetrically.
if bw < 1:
bx -= (1 - bw) / 2
bw = 1
if bh < 1:
by -= (1 - bh) / 2
bh = 1
path = Path._create_closed([
(bx, by), (bx + bw, by), (bx + bw, by + bh), (bx, by + bh)])
self._renderer.draw_path(
gc1, path,
mpl.transforms.Affine2D()
.rotate_deg(angle).translate(x, self.height - y),
rgba)
gc1.restore()

def get_canvas_width_height(self):
# docstring inherited
Expand Down
10 changes: 10 additions & 0 deletions lib/matplotlib/mpl-data/matplotlibrc
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,16 @@
# zapf chancery, charter, serif, sans-serif, helvetica,
# avant garde, courier, monospace, computer modern roman,
# computer modern sans serif, computer modern typewriter

## The TeX engine/format to use. The following values are supported:
## - "latex": The classic TeX engine (the current default). All backends render
## TeX's output by parsing the DVI output into glyphs and boxes and emitting
## those one by one.
## - "latex+dvipng": The same as "latex", with the exception that Agg-based
## backends rely on dvipng to rasterize TeX's output. This value was the
## default up to Matplotlib 3.10.
#text.latex.engine: latex
Comment thread
QuLogic marked this conversation as resolved.

#text.latex.preamble: # IMPROPER USE OF THIS FEATURE WILL LEAD TO LATEX FAILURES
# AND IS THEREFORE UNSUPPORTED. PLEASE DO NOT ASK FOR HELP
# IF THIS FEATURE DOES NOT DO WHAT YOU EXPECT IT TO.
Expand Down
15 changes: 15 additions & 0 deletions lib/matplotlib/rcsetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,7 @@ def _convert_validator_spec(key, conv):
# text props
"text.color": validate_color,
"text.usetex": validate_bool,
"text.latex.engine": ["latex", "latex+dvipng"],
"text.latex.preamble": validate_string,
"text.hinting": ["default", "no_autohint", "force_autohint",
"no_hinting", "auto", "native", "either", "none"],
Expand Down Expand Up @@ -1831,6 +1832,20 @@ class _Param:
"monospace, computer modern roman, computer modern sans serif, "
"computer modern typewriter"
),
_Param(
"text.latex.engine",
default="latex",
validator=["latex", "latex+dvipng"],
description=(
"The TeX engine/format to use. The following values are supported:\n"
"- 'latex': The classic TeX engine (the current default). All backends "
"render TeX's output by parsing the DVI output into glyphs and boxes and "
"emitting those one by one.\n"
"- 'latex+dvipng': The same as 'latex', with the exception that Agg-based "
"backends rely on dvipng to rasterize TeX's output. This value was the "
"default up to Matplotlib 3.10."
)
),
_Param(
"text.latex.preamble",
default="",
Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@
"text.hinting_factor",
"text.kerning_factor",
"text.language",
"text.latex.engine",
"text.latex.preamble",
"text.parse_math",
"text.usetex",
Expand Down
2 changes: 1 addition & 1 deletion src/ft2font_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ const char *PyFT2Font_draw_glyph_to_bitmap__doc__ = R"""(
image : 2d array of uint8
The image buffer on which to draw the glyph.
x, y : int
The pixel location at which to draw the glyph.
The position of the glyph's top left corner.
glyph : Glyph
The glyph to draw.
antialiased : bool, default: True
Expand Down
Loading