Make text in PDF generated matplotlib editable in Illustrator
You draw a figure with Python using matplotlib in PDF, open it in Illustrator to fix one label before the submission deadline, and the text is there but you cannot select it. Click a word and you get a cluster of tiny filled shapes that happen to be letter-shaped. The glyphs are right. The text is gone.
This is not an Illustrator problem and it is not a corrupted file. It is a matplotlib default.
Tested on matplotlib 3.10.8. Backend defaults do occasionally move, so check yours with matplotlib.__version__ if the behavior below does not match.
Type 3 versus Type 42
The PDF specification supports several ways to embed a font. Matplotlib’s PDF backend defaults to Type 3, which defines each glyph as a small PostScript drawing procedure. A Type 3 font is a set of instructions for painting shapes. It carries no reliable mapping back to characters, so a design tool can render it faithfully and still have nothing to hand you when you click.
Type 42 wraps a real TrueType font, outlines and character encoding intact. Illustrator reads it as text because it is text.
Matplotlib picked Type 3 for a defensible reason: Type 3 fonts are smaller and were historically safer for print and LaTeX pipelines. For anyone who intends to touch the figure afterward, the tradeoff runs backwards.
The fix
import matplotlib.pyplot as plt
plt.rcParams.update({
"pdf.fonttype": 42, # TrueType in PDF, editable downstream
"ps.fonttype": 42, # same for EPS
"svg.fonttype": "none", # SVG keeps <text> instead of converting to paths
})
Put it at the top of your plotting script, before you create any figures. That is the whole fix for editability.
Verify it from the command line rather than trusting the setting:
pdffonts figure.pdf
You want CID TrueType in the type column:
name type encoding emb sub uni
-------------------------------- ----------------- ----------- --- --- ---
FRZNSF+LiberationSans-Bold CID TrueType Identity-H yes yes yes
FRZNSF+LiberationSans-Italic CID TrueType Identity-H yes yes yes
FRZNSF+LiberationSans CID TrueType Identity-H yes yes yes
If it still says Type 3, the rcParam did not take effect. The usual cause is something resetting the params after you set them: a style sheet, a seaborn call, or an import that applies its own theme.
The second problem, which looks like the first
Now the text is editable, and Illustrator opens a dialog complaining about a missing font. Matplotlib’s default family is DejaVu Sans, which ships with matplotlib and is installed on very few design machines. Illustrator substitutes, the substitute has different metrics, and your carefully positioned labels drift.
Switch to a font your design environment actually has. On Linux that usually means Liberation Sans, which is metric-compatible with Arial, so the substitution is dimensionally identical:
plt.rcParams["font.family"] = "Liberation Sans"
Check what you have before committing to one:
import matplotlib.font_manager as fm
print(sorted({f.name for f in fm.fontManager.ttflist}))
Worth being clear about the division of labor here, because the two fixes arrive together and are easy to conflate. pdf.fonttype decides whether you get text at all. font.family decides whether that text keeps its layout. Neither substitutes for the other.
Prefer SVG when you can
For figures headed into a design tool, SVG usually imports more cleanly than PDF. With svg.fonttype set to none, every label stays as a <text> element referencing a font by name, and everything else is ordinary vector geometry.
One caveat: that font name is whatever matplotlib used. If it rendered with Liberation Sans and your machine has Arial, a single find-and-replace fixes it before you ever open Illustrator.
sed -i 's/Liberation Sans/Arial, Helvetica, sans-serif/g' figure.svg
What still will not be editable
Hatch patterns. Matplotlib writes them as clipped groups of individual stroke paths, not as fill patterns, in both PDF and SVG. If you need to restyle a hatched element, change set_hatch in the script and re-export. Fighting the clipped group in Illustrator takes longer than regenerating the figure.
Which is the broader lesson. Three rcParams at the top of a script save you a round of manual repair on every figure you export, and keep the script as the single source of truth for what the figure looks like. The moment a label only exists in an Illustrator file, your figure and your data have quietly come apart.
Comments
Post a Comment