Metadata-Version: 2.4
Name: paperplot-quantum
Version: 0.1.0
Summary: Publication-correct matplotlib figures, journal by journal (APS, Nature, IEEE, + presentation).
Author: Zlatko Minev
License: MIT
Project-URL: Homepage, https://github.com/zlatko-minev/paperplot
Project-URL: Documentation, https://zlatko-minev.github.io/paperplot/
Project-URL: Repository, https://github.com/zlatko-minev/paperplot
Project-URL: Issues, https://github.com/zlatko-minev/paperplot/issues
Keywords: matplotlib,publication,figures,physics,APS,Physical Review
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Visualization
Classifier: Framework :: Matplotlib
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: paperplot/data/fonts/LICENSE.md
License-File: paperplot/data/fonts/GUST-FONT-LICENSE.txt
Requires-Dist: matplotlib>=3.6
Requires-Dist: numpy>=1.21
Requires-Dist: tomli>=1.2; python_version < "3.11"
Provides-Extra: notebook
Requires-Dist: IPython>=7; extra == "notebook"
Provides-Extra: seaborn
Requires-Dist: seaborn>=0.12; extra == "seaborn"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
Requires-Dist: mkdocs-jupyter>=0.24; extra == "docs"
Requires-Dist: jupytext>=1.16; extra == "docs"
Requires-Dist: nbconvert>=7; extra == "docs"
Requires-Dist: ipykernel>=6; extra == "docs"
Dynamic: license-file

# paperplot

[![CI](https://github.com/zlatko-minev/paperplot/actions/workflows/ci.yml/badge.svg)](https://github.com/zlatko-minev/paperplot/actions/workflows/ci.yml)
[![Docs](https://github.com/zlatko-minev/paperplot/actions/workflows/docs.yml/badge.svg)](https://zlatko-minev.github.io/paperplot/)
[![Python](https://img.shields.io/badge/python-%E2%89%A53.10-blue)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/zlatko-minev/paperplot/blob/main/docs/showcase.ipynb)
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/zlatko-minev/paperplot/main?labpath=docs/showcase.ipynb)

**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()` then `plt.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](#drop-in-style-sheets).

## Install

```bash
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](https://zlatko-minev.github.io/paperplot/install/).

## 30-second quickstart

```python
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.

<table>
<tr>
<td align="center"><b>matplotlib defaults</b><br><img src="docs/assets/gallery/before.png" width="380"></td>
<td align="center"><b>paperplot — <code>pp.use("aps")</code></b><br><img src="docs/assets/gallery/after.png" width="380"></td>
</tr>
</table>

<table>
<tr>
<td><img src="docs/assets/gallery/aps_4panel_single.png" width="250"></td>
<td><img src="docs/assets/gallery/hist_overlap.png" width="250"></td>
<td><img src="docs/assets/gallery/data_fit_band.png" width="250"></td>
</tr>
</table>

### 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.

<p align="center"><img src="docs/assets/gallery/composite.png" width="720"></p>

### 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](https://zlatko-minev.github.io/paperplot/gallery/#on-the-page).

<table>
<tr>
<td align="center"><img src="docs/assets/gallery/in_page_aps.png" width="250"><br>single column</td>
<td align="center"><img src="docs/assets/gallery/in_page_hist.png" width="250"><br>histograms, in context</td>
<td align="center"><img src="docs/assets/gallery/in_page_double.png" width="250"><br>double column, across the page</td>
</tr>
</table>

Every image is generated from [`examples/showcase.py`](examples/showcase.py) by
[`docs/generate_gallery.py`](docs/generate_gallery.py) and regenerated by CI on each
docs build (published to GitHub Pages). Build locally:

```bash
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

```python
# 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`](paperplot/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:

```python
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`](paperplot/data/fonts/LICENSE.md)).

## Design

See [`paperplot_DESIGN.md`](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

```bash
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`](.github/workflows/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`.)
