Publication-correct matplotlib figures, journal by journal (APS, Nature, IEEE, + presentation).
Project description
paperplot
Publication-correct matplotlib figures, journal by journal.
Most matplotlib styling focuses on the look. paperplot does that too — but it
also handles the parts that decide whether a journal accepts your figure:
- sizes it to the journal's real column width (8.6 cm, 17.8 cm, …), not a generic default;
- embeds fonts correctly (Type-42, no Type-3);
- preflights it for the rules figures actually get rejected over — font type, line weight, lettering height, and grayscale legibility.
Ships Physical Review (APS) (incl. PRL/PRX/PRB), Nature, and IEEE, plus a presentation target for slides. matplotlib-only core; seaborn/IPython are optional extras.
Just want the look?
pp.register_mplstyles()thenplt.style.use("paperplot-aps")— no API to learn. You give up only the parts a style sheet can't do (true column sizing, font embedding, preflight);pp.figure()/pp.save()add those back. See Drop-in style sheets.
Install
pip install paperplot-quantum # core (matplotlib + numpy)
pip install "paperplot-quantum[notebook]" # + IPython for pp.show()
The PyPI package is paperplot-quantum, but you still import paperplot.
Python ≥ 3.10. Latest main from GitHub:
pip install "git+https://github.com/zlatko-minev/paperplot.git". Full matrix on
the Install page.
30-second quickstart
import numpy as np
import paperplot as pp
pp.use("aps") # or "nature" / "ieee" / "prl" / "talk" — set once
fig, ax = pp.figure(width="single") # 8.6 cm wide, golden ratio, styled
ax.plot(np.linspace(0, 10, 200), np.sin(np.linspace(0, 10, 200)))
ax.set_xlabel(r"delay $\tau$ (ns)")
ax.set_ylabel(r"population $\langle n \rangle$")
pp.save(fig, "fig1.pdf") # embeds fonts, runs preflight()
Gallery
Same three-curve plot, same data — stock matplotlib versus one pp.use("aps").
paperplot fixes the column width, type scale, color cycle, ticks, and line weights
in a single call.
| matplotlib defaults |
paperplot — pp.use("aps") |
One figure, many panels
A custom GridSpec, four panel letters: (a) $T_1$ relaxation and (b) Ramsey
fringes — each a fit with a flush residual strip — plus (c) outlined $T_1$
distributions and (d) a chevron map. Journal styling carries through a hand-built grid.
See it on the page before you submit
The part most styling packages skip: pp.preview_in_page(fig) drops your figure
into a true-to-scale mock journal page with real body text — so you see exactly
how big it lands in the column and whether the lettering still reads, before you
submit. More in the gallery.
single column |
histograms, in context |
double column, across the page |
Every image is generated from examples/showcase.py by
docs/generate_gallery.py and regenerated by CI on each
docs build (published to GitHub Pages). Build locally:
pip install -e ".[docs,notebook]"
python docs/generate_gallery.py && mkdocs serve
What you get
| True journal sizing | width="single"/"onehalf"/"double"/"full_page" → exact column widths in inches. |
| Correct export | PDF default, EPS first-class; fonts embedded as Type-42, no Type-3; RGB; revision stamped in metadata. |
| Preflight linter | pp.preflight(fig) → structured Report: flags sub-spec fonts/lines and grayscale-ambiguous colors. Warn, never block. |
| Colorblind-safe by default | Okabe-Ito cycle; sequential/diverging maps kept separate (pp.cmap("BuGn")); custom via pp.register_palette. Fill/stroke convention: pp.fills() (muted) under pp.strokes() (bright). pp.show_palettes() shows every palette tagged for colorblind-safety. |
| Plot helpers | pp.hist_outline (translucent fill + crisp staircase outline), pp.hist_filled, pp.data_fit_band (markers + fit + CI band), pp.swatches. |
| See it on the page | pp.preview_in_page(fig) drops the figure into a true-to-scale mock journal page with real body text — catch sizing/legibility before you submit (a paperplot original). Plus pp.show(fig, zoom=2) and pp.grayscale_proof(fig). |
| Helpers | pp.panel_labels(axes), pp.despine(ax), pp.clean_shared_axes(fig). |
A bit more
# multi-panel, double column
fig, axes = pp.figure(width="double", nrows=2, ncols=2, sharex=True)
pp.panel_labels(axes, fmt="({})") # (a) (b) (c) (d)
pp.clean_shared_axes(fig)
# sans-serif labels with Computer Modern math is the default (the physics look);
# math="sans" pairs Arial-style math instead, font_scale nudges every size
pp.use("aps", math="cm", font_scale=1.1) # sticky: pp.figure() inherits both
# scoped style (doesn't leak), serif to match REVTeX Times
with pp.style("aps", serif=True):
fig, ax = pp.figure(width="full_page")
# per-figure overrides
fig, ax = pp.figure("single", journal="prl", palette="mylab")
# overlapping histograms, fig-3 style: muted fills, crisp outlines
fig, ax = pp.figure("single")
for s, c in zip((sample_a, sample_b), pp.fills()):
pp.hist_outline(s, ax, bins=60, rescale=True, color=c)
ax.axvline(ideal, color=pp.strokes()[8], ls="--", lw=1.2, zorder=100)
# data + fit + ±1σ band (e.g. lmfit result.eval / eval_uncertainty)
pp.data_fit_band(ax, x, y, yerr=yerr, x_fit=xf, y_fit=yf, y_fit_err=yf_err)
# pre-submission check
report = pp.preflight(fig)
if not report: # truthy == clean
print(report) # aligned table of issues
Targets
pp.use(...) / pp.figure(journal=...) accept any of:
| key | target | single / double width |
|---|---|---|
aps (+ prl prx prb) |
Physical Review | 8.6 cm / 17.8 cm |
nature |
Nature | 8.9 cm / 18.3 cm |
ieee |
IEEE Transactions / conference | 8.9 cm (3.5″) / 18.2 cm (7.16″) |
talk |
slides / presentation | larger type, thicker lines (scale with the target) |
pp.available() lists them all. New journals are pure data — add a table to
data/journals.toml; no code.
Drop-in style sheets
The product is pp.figure()/pp.save() — they size to the real column, embed
fonts, and preflight, which a style sheet cannot. But the look is just
rcParams, and rcParams travel as .mplstyle files, so you can adopt it with zero
buy-in and graduate later:
import paperplot as pp, matplotlib.pyplot as plt
pp.register_mplstyles() # registers paperplot-aps/-nature/-ieee/-talk
plt.style.use("paperplot-aps") # now use plain matplotlib
# composable, matplotlib-native: layer a built-in modifier on top
plt.style.use(["paperplot-nature", "ggplot"])
# or write a file to commit / share / drop into ~/.config/matplotlib/stylelib/
pp.export_mplstyle("ieee", "ieee.mplstyle", serif=True)
paperplot never touches matplotlib's global style library on import — registration
is an explicit opt-in. A style sheet carries the target's default (single-column)
size; reach for pp.figure(width=...) when you need the column-true figure.
Fonts
Journals want figure lettering in Helvetica or Arial (Nature requires it), but
those are proprietary and can't be redistributed. So paperplot ships TeX Gyre
Heros — a free, metric-compatible Helvetica clone — and registers it
automatically. The font preference is Arial → Helvetica → TeX Gyre Heros → DejaVu Sans: if you have the real thing installed it's used, otherwise the
bundled clone gives the same Helvetica look on every machine (and embeds a
proper Type-42 font in the PDF, instead of silently falling back to matplotlib's
DejaVu Sans). This is why the figures look identical on your laptop, a
collaborator's box, and CI. Bundled under the free GUST Font License
(paperplot/data/fonts/LICENSE.md).
Design
See paperplot_DESIGN.md for the full design — journal specs
as data (data/journals.toml), the rcParams mapping, and the registry/versioning
model.
Layout
paperplot/
journals.py registry.py data/journals.toml layout.py
style.py lint.py palettes.py preview.py
fonts.py save.py core.py plots.py
mplstyle.py
Develop
pip install -e ".[dev]"
pytest # tests
python examples/run_all.py # run every example -> examples/out/render/*.png
Releasing
Published to PyPI as paperplot-quantum via Trusted Publishing (OIDC — no
stored tokens). To cut a release: bump version in pyproject.toml, push, then
create a GitHub Release with tag vX.Y.Z. The
release.yml workflow builds, runs twine check,
and publishes. (One-time: register the trusted publisher on PyPI — repo
zlatko-minev/paperplot, workflow release.yml, environment pypi.)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file paperplot_quantum-0.1.0.tar.gz.
File metadata
- Download URL: paperplot_quantum-0.1.0.tar.gz
- Upload date:
- Size: 304.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41a387148036ddf598f06975fda1d88d7292431abd01bdafe1544a8effe60d85
|
|
| MD5 |
e6d3d34c688f7ccec0e4151b13aac8a4
|
|
| BLAKE2b-256 |
c583ae5d2a742af68f08c244abd8364b8784204d3824aec44b38d2fabe257688
|
Provenance
The following attestation bundles were made for paperplot_quantum-0.1.0.tar.gz:
Publisher:
release.yml on zlatko-minev/paperplot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
paperplot_quantum-0.1.0.tar.gz -
Subject digest:
41a387148036ddf598f06975fda1d88d7292431abd01bdafe1544a8effe60d85 - Sigstore transparency entry: 1819713152
- Sigstore integration time:
-
Permalink:
zlatko-minev/paperplot@5bcc2b0c48c041ca3552895bdc3ac49fb8eb2664 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zlatko-minev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5bcc2b0c48c041ca3552895bdc3ac49fb8eb2664 -
Trigger Event:
release
-
Statement type:
File details
Details for the file paperplot_quantum-0.1.0-py3-none-any.whl.
File metadata
- Download URL: paperplot_quantum-0.1.0-py3-none-any.whl
- Upload date:
- Size: 299.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e2518dd410f7f176b81abd47d575082c61450046c91acc7110608fddd70a4ab
|
|
| MD5 |
b1cbf9fa687868376765a82d9c198d36
|
|
| BLAKE2b-256 |
8f8e5a3b9074d1d55c59eedac9b197d099e20b5a123645eda580c1b7e0bdb3f2
|
Provenance
The following attestation bundles were made for paperplot_quantum-0.1.0-py3-none-any.whl:
Publisher:
release.yml on zlatko-minev/paperplot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
paperplot_quantum-0.1.0-py3-none-any.whl -
Subject digest:
6e2518dd410f7f176b81abd47d575082c61450046c91acc7110608fddd70a4ab - Sigstore transparency entry: 1819713201
- Sigstore integration time:
-
Permalink:
zlatko-minev/paperplot@5bcc2b0c48c041ca3552895bdc3ac49fb8eb2664 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zlatko-minev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5bcc2b0c48c041ca3552895bdc3ac49fb8eb2664 -
Trigger Event:
release
-
Statement type: