sci-vis What the research was showing, and why you could not see it

Guide

The figure with no text in it

Your plotting tool probably converted your axis labels into drawings of letters. The picture is identical, and nobody can check it any more.

· data visualisation, scientific publishing · Edited by Hannah Isles

Open the SVG your plotting script just wrote and search it for one of your own axis labels. On most people’s figures you will find the words, and you will not find any text.

That is not a riddle. Somewhere between your plt.savefig and the file on disk, each letter stopped being a letter and became a small drawing of one. The picture is pixel-for-pixel identical. Everything you would want to ask about the type afterwards, and everything your journal’s production team would want to ask, has quietly stopped having an answer.

What the two files look like

Here is one ordinary figure: 342 penguins, flipper length against body mass, three species, drawn in matplotlib 3.11.2 with nothing customised. It was exported twice from the same code with one setting changed, svg.fonttype, and nothing else touched.

The two images are indistinguishable. The two files are not.

What is inside the two exports

The same figure, exported twice. The setting named path is what matplotlib ships, and at that setting the file holds no text at all: every letter is a reference to an outline in the file's own <defs>, placed 109 times. Set it to none and the 20 pieces of text come back as text. Neither export looks different on the page.

Source sci-vis.com, 2026. Original work for sci-vis.com.

Measured from the two files by scripts/pieces/outlined-type.py.
MeasurementDefault (path)none
Text elements020
Glyph outlines placed1090
Type sizes the file declaresnone10 pt
Fonts the file namesnoneDejaVu Sans, and its fallbacks
Where the words “Flipper length (mm)” appearin a comment onlyin the text
File size84,229 bytes65,556 bytes

The last row of that table is what makes this hard to notice. The default export does still contain the string Flipper length (mm). matplotlib writes it into an XML comment beside the outlines it drew it as, as a courtesy. So if you go looking for your axis label in the file, you will find it, and conclude that everything is fine.

It is not there as text. It is there as a note explaining what the drawing is of. The check most people would run is the one check that passes either way.

It is not a matplotlib problem

We went looking for figures drawn by tools that are not installed here, on Wikimedia Commons, under licences that allow it. The answer was the same from two of the three, by two different mechanisms neither of which announces itself.

Measured by scripts/pieces/outlined-type-survey.mjs and outlined-type.py. Every attribution is sourced in data/outlined-type/survey.json.
ToolLive type?How the letters got there
matplotlib 3.11.2, as shippedNoeach glyph defined once in <defs> and placed with <use>, 109 times, from DejaVuSans
matplotlib 3.11.2, svg.fonttype: noneYeslive type
Julia, Plots.jlNoeach glyph drawn as a <path>
R, the built-in svg() deviceNoeach glyph defined once as a <symbol> and placed with <use>, which is cairo’s way
gnuplot 4.2Yeslive type

gnuplot is the control, and it is the one that behaves: it writes real text and its labels can be read straight out of the file. Julia’s Plots draws each letter as a path. R’s built-in svg() device is cairo-backed, so it defines each letter once and stamps it wherever it is needed. matplotlib works the same way as R, defining its glyphs as paths rather than as symbols. Different routes, same outcome, and no warning from any of them.

A caution about that table, since this site has now made the mistake twice. The Julia file is filed on Commons under Category: Created with Matplotlib and is nothing of the kind: the author published the Julia source on the same page. Every attribution above comes from what the author wrote beside the file or from a string inside the file, never from a category. A label applied by a third party is not evidence about what produced a file.

What it costs

Nothing, most of the time. The figure renders correctly, which is exactly why this survives all the way to submission.

It costs something at the two moments that matter. The first is when somebody asks a question about the type, and here is what that looks like on the very figure above.

The type in that figure, drawn and printed

The figure declares itself 132.1 mm wide and sets its type at 10 pt. Reproduced in a 85 mm single column, every size in it is multiplied by 0.643, and that type arrives at 6.4 pt against a floor of 8. The sizes below are shown in proportion to one another; how big they are on your screen depends on your screen, which is the whole difficulty with judging type by eye.

Source sci-vis.com, 2026. Original work for sci-vis.com.

That figure is too small at a single column, and it is too small in both exports, because the outlining changed nothing about the picture. The difference is that one of the two files can be asked and the other cannot. In the default export there is no type size to read, no font to name and nothing for a checker, a copyeditor or you in six months to measure. The problem is still there. The evidence of it has gone.

The second moment is when someone needs to change a word. A typo in a panel label, a gene name the journal wants italicised, a unit that changed in revision: all of these are a two-second edit in a file with text in it, and a redraw-from-the-script in a file without. If the script still runs. If you still have it.

The journal’s own account of this

PLOS says it plainly on its figure guidelines page, under Text within EPS figures, retrieved 2026-09-18:

Embed fonts, or convert them to outlines to prevent missing or improperly rendered text. In those files that are created in software like Matlab, open Illustrator or Inkscape and convert text to outlines. You will not be able to change your text after it has been converted to outlines.

Read that carefully, because it is not on our side of the argument. PLOS recommends outlining for EPS figures, and gives a good reason: a font that is not embedded goes missing in production and the text renders wrong or not at all. Outlining makes a figure immune to that. It is a legitimate and sometimes correct thing to do.

The same page also requires, of every figure, arial, times, or symbol font only in 8-12 point.

Both of those are PLOS’s, and together they describe the trap exactly. Convert your text to outlines and the second requirement becomes uncheckable, by you and by them. The figure may well comply. Nobody can demonstrate that it does.

The difference between PLOS’s advice and your tool’s default is when. Outlining is a finishing step, done deliberately, to a figure that is already the right size with the right type in it, on the way out the door. Your tool does it first, silently, to every figure, including the seventeen drafts you are still revising.

What to do about it

  1. Set the export to keep your type as type, once, in whatever you plot with. For matplotlib that is svg.fonttype: none, in a matplotlibrc or with plt.rcParams at the top of the script. In R it is a different device rather than a setting: svglite::svglite() in place of the built-in svg().
  2. Know what that costs, because it is a trade and not a free win. matplotlib’s own configuration file describes none as “assume fonts are installed on the machine where the SVG will be viewed”, and svglite says the same thing from the other side: outlining “does not require the font to be present on the system that displays the SVG but makes it more or less impossible to edit the text after the fact”. Live type is a file that needs its font; outlined type is a file nobody can correct. Choose per stage, not once and for ever.
  3. Check what you actually have. Open the file and search it for <text, not for your axis label. Your label is in there either way; <text is only in there when the type is real. The figure checker on this site does the same thing and will tell you which kind you have.
  4. Outline last, if at all, and only on the copy you are submitting. Keep the version with text in it, because that is the one you will edit at revision.
  5. If you are sending EPS or PDF to PLOS, follow PLOS, not us: embed the fonts or outline them, as they ask. Do it to a figure whose type you have already checked.

One thing that is not a problem: this is a vector-export setting and it does not touch your PDF. The same figure saved as a PDF keeps its text either way, which is worth knowing when a journal takes both.

The one-page card

Keep your type as type (PDF, A4). The export setting for matplotlib and for R, each quoted from that tool’s own documentation, the line to paste, and the check to run on a file you already have. Tools whose setting we could not verify are named on the card as unverified rather than guessed at. Also on the site as a page.