Skip to main content

pdfmarks

Read the data back out of a vector figure in a PDF.

A scatter plot in a paper looks like a picture of the data, and if you want the numbers the usual answer is to email the authors or trace the dots by hand. But a vector PDF does not contain a picture. It contains a list of instructions for placing marks, and the coordinate in each instruction is where the measurement was. The numbers never left. They are sitting in the file at full precision.

pdfmarks walks a page's drawing instructions and hands you every mark on it.

pip install pdfmarks
from pdfmarks import marks, affine

ms = marks('paper.pdf', 4)                       # 0-based page index

# a series is a colour and a marker shape. matplotlib draws a circle with 26
# path points, MATLAB with 15; a triangle is 5, a square or diamond is 4.
series = [m for m in ms
          if m['fill'] == (0.121569, 0.466667, 0.705882) and m['npts'] == 26]
series.sort(key=lambda m: m['seq'])              # drawing order = data order

# calibrate from two marks whose data values you know -- axis tick stubs are
# ideal, they are short two-point strokes on the frame.
fx, fy = affine((225.28, 292.21), (0.0, 0.0),
                (410.30, 427.67), (1.0, 1.0))
data = [(fx(m['x']), fy(m['y'])) for m in series]

Each mark is a dict: x, y (centre, PDF user space, points, y upwards), w, h (bounding box), fill, stroke, npts, op (the paint operator, or Do for a placed image), seq (position in drawing order), and pts (every vertex, so a line series comes back whole).

There is a command line too:

pdfmarks paper.pdf 5 --colours          # what series are on page 5
pdfmarks paper.pdf 5 --long 40          # paths long enough to be a line series
pdfmarks paper.pdf 5 --path 27          # every vertex of that path
pdfmarks paper.pdf 5 --csv marks.csv --max-size 12

How exact is it

Exact. It reads numbers, not pixels.

The test suite downloads two real papers and checks two things whose answers exist independently of this code. In one (a MATLAB figure) two panels carry copies of a third panel's markers; matched in drawing order the recovered coordinates agree to 0.0012 pt. In the other (matplotlib) three series are plotted against a parameter running 0 to 1, and calibrated from nothing but the tick stubs the recovered values land on k/24 — the authors' linspace, which the reader is never told about — to 3.3 × 10⁻⁵.

What it does not do

It does not find your axes. Every plotting library writes them differently, and a wrong guess produces numbers that are confidently wrong to four decimal places, which is worse than no numbers. You supply two reference points and affine does the rest. Tick stubs are easy to spot: two-point strokes, a few points long, sitting on the frame.

It also cannot help with a raster figure. If the plot was saved as PNG and pasted in, there are no instructions to read and you want a tracing tool instead.

Three things worth knowing

Series separate by shape as well as colour. Two panels that reuse one colour still come apart if their markers differ, because npts differs. Run --colours and check the counts against what the figure should contain; if a series is short, something is merging.

Drawing order is usually data order, and often time order. Trajectories come out in the order they were measured. It is also how you tell a legend swatch from data: the swatch is the same marker in the same colour, drawn in a separate run.

A filled marker is often drawn twice, once filled and once as a stroked outline at the same centre. De-duplicate by adjacency in the stream, never by proximity in the plane: in a dense trajectory, neighbouring real data points sit closer together than a marker is wide, and a proximity rule quietly eats them.

Why it exists

It came out of wanting the numbers behind one figure — twelve lab earthquakes published as a scatter plot with no data file. Reading them back out took an afternoon and turned three of the paper's qualitative sentences into measurements. It also caught something invisible: the copies of one panel's data laid into the other two had been translated rather than replotted, so they sat 1.4 % too far right. Harmless, and the sort of thing only the instructions know.

MIT licensed. Written by Iris.

Release files for pdfmarks 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pdfmarks 0.1.1
File Size Uploaded
pdfmarks-0.1.1.tar.gz 12.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pdfmarks 0.1.1
File Interpreter ABI Platform
pdfmarks-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 21.4 kB

Release files / pdfmarks-0.1.1.tar.gz

Download URL pdfmarks-0.1.1.tar.gz
Size 12.2 kB
Tags Source
SHA-256 checksum
How to use checksums
0ac3e85ae1a35b58646c27659823e36ae824b26ba307db5931ceb8726412c411
BLAKE2b-256 checksum
How to use checksums
a80d25909fe22d7063144ccf1f942555f5fce458110d10ad741c09a638e9fc79
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release files / pdfmarks-0.1.1-py3-none-any.whl

Download URL pdfmarks-0.1.1-py3-none-any.whl
Size 9.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d2e67e5634d102bfbf2cd1b267f21f8adc343fb800b5203fcb5dcbca0d80e4f3
BLAKE2b-256 checksum
How to use checksums
d3290d33377ec034e74187b5d9983d28bec59f51ccd8dda74fa61640a5c89570
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page