Support colorization of text via TeX directives by MaddieM4 · Pull Request #31234 · matplotlib/matplotlib
Conversation
PR summary
This is my work in progress to fix #6724, allowing TeX-formatted tech fields to respect color directives. I'm making this PR as a draft, because I'd like some advice on a few things, before bringing it to a more finished state. Here's some example code, and the resulting images with my patch - note that the text in the examples is partially colorized, demonstrating that it works by inline TeX directives.
# Traditional plot from matplotlib import pyplot as plt plt.rcParams.update({ 'pgf.texsystem': 'pdflatex', 'pgf.preamble': r'\usepackage{color}\usepackage{dashrule}', 'text.usetex': True, 'text.latex.preamble': r'\usepackage{color}\usepackage{dashrule}', }) fig, ax = plt.subplots() ax.set_ylabel(r'Y $\;$ \textcolor[rgb]{1.0, 0.0, 0.0}{abc\hdashrule[0.5ex]{3cm}{1pt}{1pt 0pt}}') ax.set_xlabel(r'N $\;$ \textcolor[rgb]{0.0, 1.0, 0.0}{abc\rule[0.5ex]{3cm}{1pt}}') plt.savefig('axes.png') # Direct rendering to demonstrate that rotated and unrotated text work correctly from matplotlib.backends.backend_agg import RendererAgg from matplotlib.font_manager import FontProperties from matplotlib.image import imsave renderer = RendererAgg(400, 400, 120); renderer.clear() gc = renderer.new_gc() props = FontProperties(size=12) angle = -45 tex_string = r'Y $\;$ \textcolor[rgb]{1.0, 0.0, 0.0}{abc\hdashrule[0.5ex]{3cm}{1pt}{1pt 0pt}}' renderer.draw_tex(gc, 50, 50, tex_string, props, 0) renderer.draw_tex(gc, 50, 250, tex_string, props, angle) imsave('rotations.png', renderer.buffer_rgba())
So, what are the remaining issues?
- At an API level, there's an incompatibility between thinking of text as a greyscale thing that can be colorized to any RGB value, vs text that brings its own full RGBA data to the table. The new logic can handle the latter, but loses support for the former.
- As some temporary scaffolding, I wrote the original version of this code as a parallel coexisting copy that lives next to the original logic. After I got it working, I did take a look at replacing the original code (I think that's necessary for a complete PR), but there's some logic that really depends on feeding greyscale image data into
RendererAgg::draw_text_image, so that code would need to be rethought, and would likely run into the problems from Point 1 (expecting external colorization support). - Because there's some subtle changes to the way that text is composited in the new code, I saw the rotation rendering test fail with a very slight difference between the expected and rendered images. We might choose to regenerate some expected images in the final version of this PR.
- When the API questions are sorted out, it makes sense to add some tests for this specific new functionality.
AI Disclosure
I don't use it, I don't need it.
PR checklist
- "closes #0000" is in the body of the PR description to link the related issue
- new and changed code is tested
- Plotting related features are demonstrated in an example
- New Features and API Changes are noted with a directive and release note
- Documentation complies with general and docstring guidelines
Thank you for opening your first PR into Matplotlib!
If you have not heard from us in a week or so, please leave a new comment below and that should bring it to our attention. Most of our reviewers are volunteers and sometimes things fall through the cracks.
You can also join us on gitter for real-time discussion.
For details on testing, writing docs, and our review process, please see the developer guide.
Please let us know if (and how) you use AI, it will help us give you better feedback on your PR.
We strive to be a welcoming and open project. Please follow our Code of Conduct.
The current failing tests all seem to be the image comparison test I mentioned in the main body of the PR.
______________________________ test_rotation[png] ______________________________
[gw0] linux -- Python 3.12.12 /opt/hostedtoolcache/Python/3.12.12/x64/bin/python
args = ()
kwds = {'extension': 'png', 'request': <FixtureRequest for <Function test_rotation[png]>>}
@wraps(func)
def inner(*args, **kwds):
with self._recreate_cm():
> return func(*args, **kwds)
^^^^^^^^^^^^^^^^^^^
E matplotlib.testing.exceptions.ImageComparisonFailure: images not close (RMS 0.299):
E result_images/test_usetex/rotation.png
E result_images/test_usetex/rotation-expected.png
E result_images/test_usetex/rotation-failed-diff.png
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/contextlib.py:81: ImageComparisonFailure
There's also a stub test failure, which I take to be because I've added a new (temporary) alternative implementation of TexManager.get_grey (get_rgba_mm4, which needs a better name, but get_rgba is taken by the external coloring API, so I'm open to ideas) without adding it to the corresponding .pyi file. I didn't think it would make sense to add the stub for a temporary method in an in-flux API, but it could be sensible for clearing up noise in the CI.
error: matplotlib.texmanager.TexManager.get_rgba_mm4 is not present in stub
Stub: in file /home/runner/work/matplotlib/matplotlib/lib/matplotlib/texmanager.pyi
MISSING
Runtime: in file /home/runner/work/matplotlib/matplotlib/lib/matplotlib/texmanager.py:347
def (tex, fontsize=None, dpi=None)
Found 1 error (checked 266 modules)
The final CI error I see is a bit opaque to me, in the middle of one of the Windows builds. It seems to not be related to the PR itself.
Starting: Upload to codecov.io
==============================================================================
Task : Bash
Description : Run a Bash script on macOS, Linux, or Windows
Version : 3.268.1
Author : Microsoft Corporation
Help : https://docs.microsoft.com/azure/devops/pipelines/tasks/utility/bash
==============================================================================
Generating script.
"C:\Program Files\Git\bin\bash.exe" -c pwd
/d/a/_temp
"C:\Program Files\Git\bin\bash.exe" -c pwd
/d/a
========================== Starting Command Output ===========================
"C:\Program Files\Git\bin\bash.exe" /d/a/_temp/cda1b7ba-f6a4-478a-8a49-b3c1562e6fae.sh
/dev/fd/63: line 2: syntax error near unexpected token `<'
/dev/fd/63: line 2: `<html><head>'
##[error]Bash exited with code '2'.
Finishing: Upload to codecov.io
Thanks for working on this. Note that in #30039 I introduced a new way of rendering TeX in Agg (by actually parsing the dvi file and rendering the glyphs one at a time, similarly to what is already done for all the vector backends) which I hope to make the default in the future (if only because that will be necessary to support xetex/luatex); this PR will need to be adapted to handle that approach. (I believe that this amounts to parsing and using the xcolor specials that get inserted in the dvi file.)
Doing so should also allow (mostly?) handling the API incompatibility between the color specified at the TeX level and the one specified at the Matplotlib level: I'd suggest that if a glyph has its color explicitly specified by TeX (i.e. a xcolor special is currently active) then the Matplotlib color should be ignored (likely except for the alpha channel, as that cannot be specified from TeX's side), whereas glyphs with no color specified by TeX should use the Matplotlib color. [Edit: From a bit of experimentation, whether the "default" black text corresponds to no color specified in dvi or an explicit "black" being specified seems to depends on the active packages. Likely we need to explore a bit the xcolor driver behaviors...]
The codecov CI error can be ignored. I would suggest adding the typestub for now even if needs to be changed later, to make reviewing easier.
| { | ||
| int x, y; | ||
|
|
||
| if (auto value = std::get_if<double>(&vx)) { |
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Go straight for the newer API here, no need to introduce the old one.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters

