rustypaper 0.2.2__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rustypaper-0.2.2 → rustypaper-0.2.3}/Cargo.lock +4 -4
- {rustypaper-0.2.2 → rustypaper-0.2.3}/Cargo.toml +3 -3
- rustypaper-0.2.3/PKG-INFO +226 -0
- rustypaper-0.2.3/README.md +212 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/pyproject.toml +1 -1
- rustypaper-0.2.3/rustypaper/README.md +212 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/compress.rs +3 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/doc.rs +234 -8
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/emit/markdown.rs +119 -8
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/emit/text.rs +1 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/emit/typst.rs +82 -9
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/layout/furniture.rs +1 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/layout/order.rs +1 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/pipeline.rs +271 -2
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/refs.rs +937 -42
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/table.rs +1 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/text/lines.rs +204 -5
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/text/vocab.rs +1 -0
- rustypaper-0.2.3/rustypaper/tests/actualtext.rs +147 -0
- rustypaper-0.2.2/PKG-INFO +0 -212
- rustypaper-0.2.2/README.md +0 -198
- rustypaper-0.2.2/rustypaper/README.md +0 -198
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/Cargo.toml +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/__init__.py +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/__main__.py +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/cli.py +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/examples/ingest_share.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/backend/mod.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/backend/rustium.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/cli.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/emit/mod.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/error.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/figure.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/ir.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/layout/columns.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/layout/mod.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/layout/stats.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/lib.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/main.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/math/build.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/math/latex.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/math/mod.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/math/symbols.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/text/mod.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/src/util.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/tests/cli.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper/tests/robustness.rs +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper-py/Cargo.toml +0 -0
- {rustypaper-0.2.2 → rustypaper-0.2.3}/rustypaper-py/src/lib.rs +0 -0
|
@@ -551,9 +551,9 @@ dependencies = [
|
|
|
551
551
|
|
|
552
552
|
[[package]]
|
|
553
553
|
name = "rustium-pdf"
|
|
554
|
-
version = "0.1.
|
|
554
|
+
version = "0.1.3"
|
|
555
555
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
556
|
-
checksum = "
|
|
556
|
+
checksum = "e3e3fd0358b52a11d3547ca03a4bda867ba5a5e3ed9dfb1f6768247af1154208"
|
|
557
557
|
dependencies = [
|
|
558
558
|
"aes",
|
|
559
559
|
"cbc",
|
|
@@ -567,7 +567,7 @@ dependencies = [
|
|
|
567
567
|
|
|
568
568
|
[[package]]
|
|
569
569
|
name = "rustypaper"
|
|
570
|
-
version = "0.2.
|
|
570
|
+
version = "0.2.3"
|
|
571
571
|
dependencies = [
|
|
572
572
|
"anyhow",
|
|
573
573
|
"clap",
|
|
@@ -580,7 +580,7 @@ dependencies = [
|
|
|
580
580
|
|
|
581
581
|
[[package]]
|
|
582
582
|
name = "rustypaper-py"
|
|
583
|
-
version = "0.2.
|
|
583
|
+
version = "0.2.3"
|
|
584
584
|
dependencies = [
|
|
585
585
|
"pyo3",
|
|
586
586
|
"rustypaper",
|
|
@@ -3,21 +3,21 @@ resolver = "2"
|
|
|
3
3
|
members = ["rustypaper", "rustypaper-py"]
|
|
4
4
|
|
|
5
5
|
[workspace.package]
|
|
6
|
-
version = "0.2.
|
|
6
|
+
version = "0.2.3"
|
|
7
7
|
edition = "2021"
|
|
8
8
|
rust-version = "1.94"
|
|
9
9
|
license = "MIT OR Apache-2.0"
|
|
10
10
|
repository = "https://github.com/pgarrett-scripps/rustypaper"
|
|
11
11
|
|
|
12
12
|
[workspace.dependencies]
|
|
13
|
-
rustypaper = { path = "rustypaper", version = "0.2.
|
|
13
|
+
rustypaper = { path = "rustypaper", version = "0.2.3" }
|
|
14
14
|
# The pure-Rust PDF backend, from the registry. Deliberately not a path dependency: a path
|
|
15
15
|
# only resolves where the sibling checkout happens to exist, which is this machine and not a
|
|
16
16
|
# CI runner, and `cargo fmt --all` walks into it whether it is there or not. To develop the
|
|
17
17
|
# two together, add a temporary override rather than committing one:
|
|
18
18
|
# [patch.crates-io]
|
|
19
19
|
# rustium-pdf = { path = "../rustium-pdf" }
|
|
20
|
-
rustium = { package = "rustium-pdf", version = "0.1.
|
|
20
|
+
rustium = { package = "rustium-pdf", version = "0.1.3" }
|
|
21
21
|
|
|
22
22
|
anyhow = "1"
|
|
23
23
|
clap = { version = "4", features = ["derive"] }
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rustypaper
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
6
|
+
Classifier: Topic :: Scientific/Engineering
|
|
7
|
+
Summary: Structure-aware conversion of born-digital scientific PDFs to Markdown, Typst and JSON.
|
|
8
|
+
Keywords: pdf,markdown,typst,science,papers,extraction
|
|
9
|
+
License: MIT OR Apache-2.0
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
12
|
+
Project-URL: Repository, https://github.com/pgarrett-scripps/rustypaper
|
|
13
|
+
|
|
14
|
+
# rustypaper
|
|
15
|
+
|
|
16
|
+
**Turn research PDFs into readable text and structured data.**
|
|
17
|
+
|
|
18
|
+
Rustypaper converts scientific papers to Markdown, plain text, JSON, or Typst. Use it to
|
|
19
|
+
save a paper as an editable document, prepare text for search or an LLM, or extract
|
|
20
|
+
sections for your own analysis tools. Run it from the terminal, Python, or Rust.
|
|
21
|
+
|
|
22
|
+
It runs locally on your CPU. No GPU, AI model, API key, or document upload is required.
|
|
23
|
+
It is designed for PDFs with selectable text, including one-column and two-column papers.
|
|
24
|
+
It does not perform OCR on scans or image-only PDFs.
|
|
25
|
+
|
|
26
|
+
[Quick start](#quick-start) · [Downloads](#install-without-python) ·
|
|
27
|
+
[Python API](#python-api) · [Limitations](#conversion-quality-and-limitations)
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
With Python 3.9 or newer:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
pip install rustypaper
|
|
35
|
+
rustypaper convert paper.pdf --out paper.md
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Replace `paper.pdf` with your PDF's path. This saves the converted Markdown to `paper.md`.
|
|
39
|
+
Omit `--out` to print it in the terminal instead.
|
|
40
|
+
|
|
41
|
+
You can also run the same command through Python:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
python -m rustypaper convert paper.pdf --out paper.md
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The [PyPI package](https://pypi.org/project/rustypaper/) includes both the terminal command
|
|
48
|
+
and the Python library. Precompiled packages are available for the platforms below, so
|
|
49
|
+
supported installations do not need a Rust compiler.
|
|
50
|
+
|
|
51
|
+
## What does the output look like?
|
|
52
|
+
|
|
53
|
+
Rustypaper recognizes headings, paragraphs, figures, tables, equations, and references,
|
|
54
|
+
and reconstructs the reading order of the paper. For example, the start of the Markdown
|
|
55
|
+
from the repository's [synthetic sample PDF](python/tests/fixtures/single-column.pdf) is:
|
|
56
|
+
|
|
57
|
+
```markdown
|
|
58
|
+
# Reliable Widget Measurements
|
|
59
|
+
|
|
60
|
+
Example Author
|
|
61
|
+
|
|
62
|
+
## Abstract
|
|
63
|
+
|
|
64
|
+
We describe a simple study of colored widgets on a wooden bench.
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This is a shortened excerpt. Conversion quality varies with the paper's layout, especially
|
|
68
|
+
for equations and complex tables. See [limitations](#conversion-quality-and-limitations).
|
|
69
|
+
|
|
70
|
+
Choose the output that suits your workflow:
|
|
71
|
+
|
|
72
|
+
| Format | Useful for | CLI option |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| Markdown | Reading, editing, and use in notes or LLM workflows | `--format md` (default) |
|
|
75
|
+
| Plain text | Search and text processing | `--format text` |
|
|
76
|
+
| JSON | Working with sections, content blocks, and their page locations | `--format json` |
|
|
77
|
+
| Typst | Further editing and typesetting in Typst | `--format typst` |
|
|
78
|
+
|
|
79
|
+
## Common tasks
|
|
80
|
+
|
|
81
|
+
Save plain text, structured JSON, or a Typst document:
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
rustypaper convert paper.pdf --format text --out paper.txt
|
|
85
|
+
rustypaper convert paper.pdf --format json --out paper.json
|
|
86
|
+
rustypaper convert paper.pdf --format typst --out paper.typ
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Save figures alongside the Markdown, with links to the image files:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
rustypaper convert paper.pdf --out paper.md --assets figures
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Convert several papers into an output folder:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
rustypaper convert first.pdf second.pdf --out converted
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Each PDF gets its own output file. For multiple inputs, `--out` must be a directory.
|
|
102
|
+
|
|
103
|
+
For all options, run `rustypaper convert --help`. An optional `--caveman light` or
|
|
104
|
+
`--caveman hard` setting shortens prose by removing selected words and phrases. It is
|
|
105
|
+
off by default and changes the wording, so use the default when you want the original prose.
|
|
106
|
+
|
|
107
|
+
## Install without Python
|
|
108
|
+
|
|
109
|
+
Download an executable from the [latest release](https://github.com/pgarrett-scripps/rustypaper/releases/latest).
|
|
110
|
+
These downloads do not require Python or a Rust compiler.
|
|
111
|
+
|
|
112
|
+
| Your computer | Archive name contains |
|
|
113
|
+
|---|---|
|
|
114
|
+
| Linux on Intel or AMD, 64-bit | `x86_64-unknown-linux-gnu` |
|
|
115
|
+
| Linux on ARM64 | `aarch64-unknown-linux-gnu` |
|
|
116
|
+
| Mac with an Intel processor | `x86_64-apple-darwin` |
|
|
117
|
+
| Mac with Apple Silicon | `aarch64-apple-darwin` |
|
|
118
|
+
| Windows on Intel or AMD, 64-bit | `x86_64-pc-windows-msvc` |
|
|
119
|
+
|
|
120
|
+
Extract the archive, then run `./rustypaper --help` on Linux or macOS, or
|
|
121
|
+
`.\rustypaper.exe --help` in Windows PowerShell. Put the executable in a folder on your
|
|
122
|
+
`PATH` to use `rustypaper` from any directory. The release includes `SHA256SUMS` for
|
|
123
|
+
verifying downloads. Standalone Linux executables require glibc 2.35 or newer.
|
|
124
|
+
|
|
125
|
+
If you use [cargo-binstall](https://github.com/cargo-bins/cargo-binstall), it can download
|
|
126
|
+
the executable for you:
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
cargo binstall rustypaper
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
With a Rust toolchain installed, you can also compile from source:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
cargo install rustypaper --locked
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Python API
|
|
139
|
+
|
|
140
|
+
After `pip install rustypaper`, convert a PDF directly in your Python code:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from pathlib import Path
|
|
144
|
+
import rustypaper
|
|
145
|
+
|
|
146
|
+
markdown = rustypaper.to_markdown("paper.pdf")
|
|
147
|
+
Path("paper.md").write_text(markdown, encoding="utf-8")
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
To get readable text and structured data from a single conversion:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
import rustypaper
|
|
154
|
+
|
|
155
|
+
result = rustypaper.convert("paper.pdf")
|
|
156
|
+
print(result.markdown)
|
|
157
|
+
|
|
158
|
+
document = result.document
|
|
159
|
+
for section in document["sections"]:
|
|
160
|
+
print(section["title"], section["first_page"], section["last_page"])
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The document contains a title, typed content blocks, and a section outline. Blocks carry
|
|
164
|
+
text and page positions. Sections include nested children and `start` / `end` indices,
|
|
165
|
+
so `document["blocks"][section["start"]:section["end"]]` selects a section's contents.
|
|
166
|
+
Page numbers are zero-based. Content before the first heading has a section title of `None`.
|
|
167
|
+
|
|
168
|
+
Other helpers include `to_text()`, `to_typst()`, and `to_document()`.
|
|
169
|
+
Image-only documents raise `rustypaper.ScannedDocument`.
|
|
170
|
+
|
|
171
|
+
## Conversion quality and limitations
|
|
172
|
+
|
|
173
|
+
Rustypaper uses the text and geometry already present in a PDF to recover its structure.
|
|
174
|
+
It is designed for scientific papers. Page layout can change during conversion.
|
|
175
|
+
|
|
176
|
+
- Scanned or image-only documents need a separate OCR tool.
|
|
177
|
+
- Equations, complex tables, and unusual layouts can be missed or reconstructed incorrectly.
|
|
178
|
+
- Titles split over several lines, subtle subsection headings, and long bibliographies remain
|
|
179
|
+
challenging. Check the converted output when those details matter to your workflow.
|
|
180
|
+
|
|
181
|
+
We evaluate conversion on 16 papers across 10 template families. Fifteen have usable source
|
|
182
|
+
text for comparison. This small corpus helps us detect regressions, and results on other
|
|
183
|
+
papers will depend on their layout. See the [evaluation results and methodology](eval/README.md)
|
|
184
|
+
for measurements of text recovery, equations, tables, references, and sections.
|
|
185
|
+
|
|
186
|
+
If a conversion looks wrong, [open an issue](https://github.com/pgarrett-scripps/rustypaper/issues).
|
|
187
|
+
Include the Rustypaper version, the command you ran, what you expected, and a public PDF link
|
|
188
|
+
or a small sample you can share. `rustypaper --version` prints the installed version.
|
|
189
|
+
|
|
190
|
+
## For developers
|
|
191
|
+
|
|
192
|
+
Rustypaper is also a [Rust crate](https://crates.io/crates/rustypaper). Add it to `Cargo.toml`:
|
|
193
|
+
|
|
194
|
+
```toml
|
|
195
|
+
[dependencies]
|
|
196
|
+
rustypaper = "0.2"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The Rust API exposes `convert()`, `convert_with()`, and `extract()`.
|
|
200
|
+
PDF reading is provided by [rustium-pdf](https://github.com/pgarrett-scripps/rustium-pdf).
|
|
201
|
+
See the [architecture notes](docs/ARCHITECTURE.md) for how conversion works, the
|
|
202
|
+
[evaluation guide](eval/README.md) for quality checks, and the [release guide](RELEASING.md)
|
|
203
|
+
for packaging and publishing.
|
|
204
|
+
|
|
205
|
+
For local development on Linux, with Rust 1.94 or newer, Python 3.9 or newer, and pytest
|
|
206
|
+
installed, run these commands from a clone of this repository:
|
|
207
|
+
|
|
208
|
+
```sh
|
|
209
|
+
scripts/build.sh
|
|
210
|
+
cargo test --release
|
|
211
|
+
PYTHONPATH=python pytest python/tests -q
|
|
212
|
+
python3 -m unittest discover -s eval/tests
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`scripts/build.sh` builds both the CLI and the Python extension used by the tests. The
|
|
216
|
+
Python tests use committed synthetic PDFs. To run the optional real-paper corpus tests,
|
|
217
|
+
fetch the papers with `scripts/fetch-corpus.sh` before running the tests. See the evaluation
|
|
218
|
+
guide for downloading reference sources and comparing results with the quality baseline.
|
|
219
|
+
|
|
220
|
+
For conversion diagnostics, `rustypaper probe paper.pdf --pages` summarizes the PDF,
|
|
221
|
+
and `rustypaper dump paper.pdf --page 0 --pretty` shows the extracted page data.
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
Licensed under [MIT](LICENSE-MIT) or [Apache 2.0](LICENSE-APACHE), at your option.
|
|
226
|
+
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# rustypaper
|
|
2
|
+
|
|
3
|
+
**Turn research PDFs into readable text and structured data.**
|
|
4
|
+
|
|
5
|
+
Rustypaper converts scientific papers to Markdown, plain text, JSON, or Typst. Use it to
|
|
6
|
+
save a paper as an editable document, prepare text for search or an LLM, or extract
|
|
7
|
+
sections for your own analysis tools. Run it from the terminal, Python, or Rust.
|
|
8
|
+
|
|
9
|
+
It runs locally on your CPU. No GPU, AI model, API key, or document upload is required.
|
|
10
|
+
It is designed for PDFs with selectable text, including one-column and two-column papers.
|
|
11
|
+
It does not perform OCR on scans or image-only PDFs.
|
|
12
|
+
|
|
13
|
+
[Quick start](#quick-start) · [Downloads](#install-without-python) ·
|
|
14
|
+
[Python API](#python-api) · [Limitations](#conversion-quality-and-limitations)
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
With Python 3.9 or newer:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pip install rustypaper
|
|
22
|
+
rustypaper convert paper.pdf --out paper.md
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Replace `paper.pdf` with your PDF's path. This saves the converted Markdown to `paper.md`.
|
|
26
|
+
Omit `--out` to print it in the terminal instead.
|
|
27
|
+
|
|
28
|
+
You can also run the same command through Python:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
python -m rustypaper convert paper.pdf --out paper.md
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The [PyPI package](https://pypi.org/project/rustypaper/) includes both the terminal command
|
|
35
|
+
and the Python library. Precompiled packages are available for the platforms below, so
|
|
36
|
+
supported installations do not need a Rust compiler.
|
|
37
|
+
|
|
38
|
+
## What does the output look like?
|
|
39
|
+
|
|
40
|
+
Rustypaper recognizes headings, paragraphs, figures, tables, equations, and references,
|
|
41
|
+
and reconstructs the reading order of the paper. For example, the start of the Markdown
|
|
42
|
+
from the repository's [synthetic sample PDF](python/tests/fixtures/single-column.pdf) is:
|
|
43
|
+
|
|
44
|
+
```markdown
|
|
45
|
+
# Reliable Widget Measurements
|
|
46
|
+
|
|
47
|
+
Example Author
|
|
48
|
+
|
|
49
|
+
## Abstract
|
|
50
|
+
|
|
51
|
+
We describe a simple study of colored widgets on a wooden bench.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This is a shortened excerpt. Conversion quality varies with the paper's layout, especially
|
|
55
|
+
for equations and complex tables. See [limitations](#conversion-quality-and-limitations).
|
|
56
|
+
|
|
57
|
+
Choose the output that suits your workflow:
|
|
58
|
+
|
|
59
|
+
| Format | Useful for | CLI option |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| Markdown | Reading, editing, and use in notes or LLM workflows | `--format md` (default) |
|
|
62
|
+
| Plain text | Search and text processing | `--format text` |
|
|
63
|
+
| JSON | Working with sections, content blocks, and their page locations | `--format json` |
|
|
64
|
+
| Typst | Further editing and typesetting in Typst | `--format typst` |
|
|
65
|
+
|
|
66
|
+
## Common tasks
|
|
67
|
+
|
|
68
|
+
Save plain text, structured JSON, or a Typst document:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
rustypaper convert paper.pdf --format text --out paper.txt
|
|
72
|
+
rustypaper convert paper.pdf --format json --out paper.json
|
|
73
|
+
rustypaper convert paper.pdf --format typst --out paper.typ
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Save figures alongside the Markdown, with links to the image files:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
rustypaper convert paper.pdf --out paper.md --assets figures
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Convert several papers into an output folder:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
rustypaper convert first.pdf second.pdf --out converted
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Each PDF gets its own output file. For multiple inputs, `--out` must be a directory.
|
|
89
|
+
|
|
90
|
+
For all options, run `rustypaper convert --help`. An optional `--caveman light` or
|
|
91
|
+
`--caveman hard` setting shortens prose by removing selected words and phrases. It is
|
|
92
|
+
off by default and changes the wording, so use the default when you want the original prose.
|
|
93
|
+
|
|
94
|
+
## Install without Python
|
|
95
|
+
|
|
96
|
+
Download an executable from the [latest release](https://github.com/pgarrett-scripps/rustypaper/releases/latest).
|
|
97
|
+
These downloads do not require Python or a Rust compiler.
|
|
98
|
+
|
|
99
|
+
| Your computer | Archive name contains |
|
|
100
|
+
|---|---|
|
|
101
|
+
| Linux on Intel or AMD, 64-bit | `x86_64-unknown-linux-gnu` |
|
|
102
|
+
| Linux on ARM64 | `aarch64-unknown-linux-gnu` |
|
|
103
|
+
| Mac with an Intel processor | `x86_64-apple-darwin` |
|
|
104
|
+
| Mac with Apple Silicon | `aarch64-apple-darwin` |
|
|
105
|
+
| Windows on Intel or AMD, 64-bit | `x86_64-pc-windows-msvc` |
|
|
106
|
+
|
|
107
|
+
Extract the archive, then run `./rustypaper --help` on Linux or macOS, or
|
|
108
|
+
`.\rustypaper.exe --help` in Windows PowerShell. Put the executable in a folder on your
|
|
109
|
+
`PATH` to use `rustypaper` from any directory. The release includes `SHA256SUMS` for
|
|
110
|
+
verifying downloads. Standalone Linux executables require glibc 2.35 or newer.
|
|
111
|
+
|
|
112
|
+
If you use [cargo-binstall](https://github.com/cargo-bins/cargo-binstall), it can download
|
|
113
|
+
the executable for you:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
cargo binstall rustypaper
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
With a Rust toolchain installed, you can also compile from source:
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
cargo install rustypaper --locked
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Python API
|
|
126
|
+
|
|
127
|
+
After `pip install rustypaper`, convert a PDF directly in your Python code:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from pathlib import Path
|
|
131
|
+
import rustypaper
|
|
132
|
+
|
|
133
|
+
markdown = rustypaper.to_markdown("paper.pdf")
|
|
134
|
+
Path("paper.md").write_text(markdown, encoding="utf-8")
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
To get readable text and structured data from a single conversion:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
import rustypaper
|
|
141
|
+
|
|
142
|
+
result = rustypaper.convert("paper.pdf")
|
|
143
|
+
print(result.markdown)
|
|
144
|
+
|
|
145
|
+
document = result.document
|
|
146
|
+
for section in document["sections"]:
|
|
147
|
+
print(section["title"], section["first_page"], section["last_page"])
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The document contains a title, typed content blocks, and a section outline. Blocks carry
|
|
151
|
+
text and page positions. Sections include nested children and `start` / `end` indices,
|
|
152
|
+
so `document["blocks"][section["start"]:section["end"]]` selects a section's contents.
|
|
153
|
+
Page numbers are zero-based. Content before the first heading has a section title of `None`.
|
|
154
|
+
|
|
155
|
+
Other helpers include `to_text()`, `to_typst()`, and `to_document()`.
|
|
156
|
+
Image-only documents raise `rustypaper.ScannedDocument`.
|
|
157
|
+
|
|
158
|
+
## Conversion quality and limitations
|
|
159
|
+
|
|
160
|
+
Rustypaper uses the text and geometry already present in a PDF to recover its structure.
|
|
161
|
+
It is designed for scientific papers. Page layout can change during conversion.
|
|
162
|
+
|
|
163
|
+
- Scanned or image-only documents need a separate OCR tool.
|
|
164
|
+
- Equations, complex tables, and unusual layouts can be missed or reconstructed incorrectly.
|
|
165
|
+
- Titles split over several lines, subtle subsection headings, and long bibliographies remain
|
|
166
|
+
challenging. Check the converted output when those details matter to your workflow.
|
|
167
|
+
|
|
168
|
+
We evaluate conversion on 16 papers across 10 template families. Fifteen have usable source
|
|
169
|
+
text for comparison. This small corpus helps us detect regressions, and results on other
|
|
170
|
+
papers will depend on their layout. See the [evaluation results and methodology](eval/README.md)
|
|
171
|
+
for measurements of text recovery, equations, tables, references, and sections.
|
|
172
|
+
|
|
173
|
+
If a conversion looks wrong, [open an issue](https://github.com/pgarrett-scripps/rustypaper/issues).
|
|
174
|
+
Include the Rustypaper version, the command you ran, what you expected, and a public PDF link
|
|
175
|
+
or a small sample you can share. `rustypaper --version` prints the installed version.
|
|
176
|
+
|
|
177
|
+
## For developers
|
|
178
|
+
|
|
179
|
+
Rustypaper is also a [Rust crate](https://crates.io/crates/rustypaper). Add it to `Cargo.toml`:
|
|
180
|
+
|
|
181
|
+
```toml
|
|
182
|
+
[dependencies]
|
|
183
|
+
rustypaper = "0.2"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The Rust API exposes `convert()`, `convert_with()`, and `extract()`.
|
|
187
|
+
PDF reading is provided by [rustium-pdf](https://github.com/pgarrett-scripps/rustium-pdf).
|
|
188
|
+
See the [architecture notes](docs/ARCHITECTURE.md) for how conversion works, the
|
|
189
|
+
[evaluation guide](eval/README.md) for quality checks, and the [release guide](RELEASING.md)
|
|
190
|
+
for packaging and publishing.
|
|
191
|
+
|
|
192
|
+
For local development on Linux, with Rust 1.94 or newer, Python 3.9 or newer, and pytest
|
|
193
|
+
installed, run these commands from a clone of this repository:
|
|
194
|
+
|
|
195
|
+
```sh
|
|
196
|
+
scripts/build.sh
|
|
197
|
+
cargo test --release
|
|
198
|
+
PYTHONPATH=python pytest python/tests -q
|
|
199
|
+
python3 -m unittest discover -s eval/tests
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`scripts/build.sh` builds both the CLI and the Python extension used by the tests. The
|
|
203
|
+
Python tests use committed synthetic PDFs. To run the optional real-paper corpus tests,
|
|
204
|
+
fetch the papers with `scripts/fetch-corpus.sh` before running the tests. See the evaluation
|
|
205
|
+
guide for downloading reference sources and comparing results with the quality baseline.
|
|
206
|
+
|
|
207
|
+
For conversion diagnostics, `rustypaper probe paper.pdf --pages` summarizes the PDF,
|
|
208
|
+
and `rustypaper dump paper.pdf --page 0 --pretty` shows the extracted page data.
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
Licensed under [MIT](LICENSE-MIT) or [Apache 2.0](LICENSE-APACHE), at your option.
|