rxiv2pdf 0.1.0__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.
Files changed (33) hide show
  1. rxiv2pdf-0.1.0/LICENSE +21 -0
  2. rxiv2pdf-0.1.0/PKG-INFO +191 -0
  3. rxiv2pdf-0.1.0/README.md +158 -0
  4. rxiv2pdf-0.1.0/pyproject.toml +66 -0
  5. rxiv2pdf-0.1.0/pyproject.toml.orig +53 -0
  6. rxiv2pdf-0.1.0/src/rxiv2pdf/__init__.py +8 -0
  7. rxiv2pdf-0.1.0/src/rxiv2pdf/__main__.py +5 -0
  8. rxiv2pdf-0.1.0/src/rxiv2pdf/cli.py +348 -0
  9. rxiv2pdf-0.1.0/src/rxiv2pdf/compile.py +285 -0
  10. rxiv2pdf-0.1.0/src/rxiv2pdf/fetch.py +808 -0
  11. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/OFL-SourceSans3.md +93 -0
  12. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/OFL-SourceSerif4.md +93 -0
  13. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/README.md +6 -0
  14. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/SourceSans3-Bold.ttf +0 -0
  15. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/SourceSans3-BoldIt.ttf +0 -0
  16. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/SourceSans3-It.ttf +0 -0
  17. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/SourceSans3-Regular.ttf +0 -0
  18. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/SourceSans3-Semibold.ttf +0 -0
  19. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/SourceSerif4SmText-Bold.ttf +0 -0
  20. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/SourceSerif4SmText-BoldIt.ttf +0 -0
  21. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/SourceSerif4SmText-It.ttf +0 -0
  22. rxiv2pdf-0.1.0/src/rxiv2pdf/fonts/SourceSerif4SmText-Regular.ttf +0 -0
  23. rxiv2pdf-0.1.0/src/rxiv2pdf/images.py +240 -0
  24. rxiv2pdf-0.1.0/src/rxiv2pdf/jats.py +1113 -0
  25. rxiv2pdf-0.1.0/src/rxiv2pdf/mathml.py +275 -0
  26. rxiv2pdf-0.1.0/src/rxiv2pdf/model.py +301 -0
  27. rxiv2pdf-0.1.0/src/rxiv2pdf/net.py +290 -0
  28. rxiv2pdf-0.1.0/src/rxiv2pdf/render_typst.py +764 -0
  29. rxiv2pdf-0.1.0/src/rxiv2pdf/resolve.py +125 -0
  30. rxiv2pdf-0.1.0/src/rxiv2pdf/templates/journal.typ +785 -0
  31. rxiv2pdf-0.1.0/src/rxiv2pdf/templates/themes/compact.typ +28 -0
  32. rxiv2pdf-0.1.0/src/rxiv2pdf/templates/themes/default.typ +3 -0
  33. rxiv2pdf-0.1.0/src/rxiv2pdf/templates/themes/sans.typ +27 -0
rxiv2pdf-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ali Zaeim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: rxiv2pdf
3
+ Version: 0.1.0
4
+ Summary: Turn a bioRxiv/medRxiv preprint into a two-column, journal-style PDF built from its JATS XML.
5
+ Keywords: biorxiv,medrxiv,preprint,jats,pdf,typst
6
+ Author: Ali Zaeim
7
+ License-Expression: MIT AND OFL-1.1
8
+ License-File: LICENSE
9
+ License-File: src/rxiv2pdf/fonts/OFL-SourceSans3.md
10
+ License-File: src/rxiv2pdf/fonts/OFL-SourceSerif4.md
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
20
+ Classifier: Topic :: Text Processing :: Markup :: XML
21
+ Requires-Dist: httpx>=0.27
22
+ Requires-Dist: lxml>=5.3
23
+ Requires-Dist: pillow>=11.0
24
+ Requires-Dist: rich>=13.7
25
+ Requires-Dist: typer>=0.16
26
+ Requires-Dist: typst>=0.15
27
+ Requires-Python: >=3.11
28
+ Project-URL: Homepage, https://github.com/ali-zaeim/rxiv2pdf
29
+ Project-URL: Repository, https://github.com/ali-zaeim/rxiv2pdf
30
+ Project-URL: Issues, https://github.com/ali-zaeim/rxiv2pdf/issues
31
+ Project-URL: Changelog, https://github.com/ali-zaeim/rxiv2pdf/blob/main/CHANGELOG.md
32
+ Description-Content-Type: text/markdown
33
+
34
+ # rxiv2pdf
35
+
36
+ [![CI](https://github.com/ali-zaeim/rxiv2pdf/actions/workflows/ci.yml/badge.svg)](https://github.com/ali-zaeim/rxiv2pdf/actions/workflows/ci.yml)
37
+ [![PyPI](https://img.shields.io/pypi/v/rxiv2pdf)](https://pypi.org/project/rxiv2pdf/)
38
+ [![Python](https://img.shields.io/pypi/pyversions/rxiv2pdf)](https://pypi.org/project/rxiv2pdf/)
39
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/ali-zaeim/rxiv2pdf/blob/main/LICENSE)
40
+
41
+ Turn a bioRxiv or medRxiv preprint into a two-column, journal-style PDF.
42
+
43
+ The PDF on bioRxiv and medRxiv is the authors' submitted manuscript, often double-spaced and
44
+ line-numbered, with the figures at the end. rxiv2pdf rebuilds the paper from the preprint's JATS
45
+ XML instead, with each figure placed after the text that first cites it and the citations linked
46
+ to the references.
47
+
48
+ ```console
49
+ $ rxiv2pdf https://www.biorxiv.org/content/10.64898/2026.10.16.759679v1
50
+ PDF 2026.10.16.759679v1.pdf
51
+ ```
52
+
53
+ | Page 1 | A figure page |
54
+ |:--:|:--:|
55
+ | ![Page 1: front matter, abstract box, two-column body](https://raw.githubusercontent.com/ali-zaeim/rxiv2pdf/main/docs/screenshots/page1.png) | ![A full-width figure with its caption](https://raw.githubusercontent.com/ali-zaeim/rxiv2pdf/main/docs/screenshots/figure-page.png) |
56
+
57
+ <sub>Screenshots on this page: Ghosh *et al.*, "Astrocytic sEH–EET signaling regulates microglial
58
+ lysosomal function and suppresses the STING–Type I interferon pathway in Alzheimer's disease",
59
+ bioRxiv [10.64898/2026.10.16.759679](https://doi.org/10.64898/2026.10.16.759679), CC BY 4.0.</sub>
60
+
61
+ ## Install
62
+
63
+ Requires Python 3.11+. Typst comes with the `typst` Python package and the fonts are bundled, so
64
+ nothing else is needed.
65
+
66
+ ```console
67
+ $ pipx install rxiv2pdf # or: uv tool install rxiv2pdf
68
+ $ rxiv2pdf --help
69
+ ```
70
+
71
+ `pip install rxiv2pdf` works too. To try it without installing anything:
72
+ `uvx rxiv2pdf 10.1101/2024.04.17.590000`.
73
+
74
+ ## Usage
75
+
76
+ ```console
77
+ $ rxiv2pdf 10.1101/2024.04.17.590000 # bare DOI (bioRxiv by default, medRxiv tried next)
78
+ $ rxiv2pdf https://www.medrxiv.org/content/10.64898/2026.09.25.26364016v1.full
79
+ $ rxiv2pdf https://doi.org/10.64898/2026.10.22.760766 -o papers/ --theme compact --paper a4
80
+ $ rxiv2pdf 10.1101/2020.03.22.002386v2 # a specific version (default: latest)
81
+ $ rxiv2pdf --list dois.txt -o papers/ # batch: one URL/DOI per line, # comments ok
82
+ ```
83
+
84
+ All options are listed under [Options](#options).
85
+
86
+ ## What you get
87
+
88
+ - **Front matter:** title, authors and affiliations, abstract, and a banner with the DOI, date and
89
+ version. Page 1 notes that the preprint isn't peer reviewed and gives its license.
90
+ - **Two columns** with aligned baselines and a balanced last page.
91
+ - **Figures and tables** after their first citation, full width when wide, and sized by their
92
+ lettering, so a simple chart isn't blown up. Long tables and figures after the references get
93
+ pages of their own.
94
+ - **Linked citations,** collapsed into ranges, and DOI links in the references.
95
+ - **Equations, back matter** (funding, data availability…) and a banner on withdrawn preprints.
96
+
97
+ ## Themes
98
+
99
+ Choose a look with `--theme`:
100
+
101
+ - `default`: serif body, numbered sans headings in navy, a shaded abstract box.
102
+ - `compact`: a dense letters-journal look, with smaller serif text on narrow margins, a serif title,
103
+ a bold abstract, capitalised headings and run-in subheadings. About 20% fewer pages.
104
+ - `sans`: a modern open-access look, with a teal band, a large title, ragged-right sans text, ruled
105
+ headings and bracketed citations.
106
+
107
+ One column per theme, each showing page 1, page 2 and a figure page of the same preprint:
108
+
109
+ ![The same preprint in the three themes: page 1, page 2 and a figure page](https://raw.githubusercontent.com/ali-zaeim/rxiv2pdf/main/docs/screenshots/themes.png)
110
+
111
+ ## Options
112
+
113
+ | Option | Description |
114
+ |---|---|
115
+ | `-o, --outdir DIR` | Where the PDF goes. Default: the current directory. |
116
+ | `--preprint-version N` | Preprint version; overrides a `vN` in the URL. Default: the `vN` in the URL, else the latest. |
117
+ | `--server biorxiv\|medrxiv` | Override server detection. Default: detected from the URL; a bare DOI tries bioRxiv, then medRxiv. |
118
+ | `--theme default\|compact\|sans` | Look of the PDF; see [Themes](#themes). Default: `default`. |
119
+ | `--paper letter\|a4` | Paper size. Default: `letter` (US Letter). |
120
+ | `--list FILE` | Batch mode. Keeps going after failures, prints a summary, exits 1 if any failed. |
121
+ | `--supplements` | Also download the supplementary files, into `<name>-supplements/` next to the PDF. They are listed and linked in the PDF either way. |
122
+ | `--append-supplements` | Append the supplementary PDFs and images to the end of the PDF, one bookmarked section per file (other files, such as spreadsheets, stay linked). |
123
+ | `--fallback-pdf` | If no JATS XML exists yet (very new posts), save the server's original PDF. |
124
+ | `--refresh` | Ignore the cache and download again. |
125
+ | `--open` | Open the PDF when done. |
126
+ | `-v`, `-vv` | `-v`: log each figure download, list every figure and table, and print a traceback on internal errors. `-vv`: also log every HTTP request. |
127
+ | `--version` | Show the rxiv2pdf version and exit. |
128
+
129
+ Exit codes: 0 success, 1 failure (not found, no XML yet, network, internal error), 2 bad input,
130
+ 130 interrupted (re-run to resume from the cache).
131
+
132
+ ### Cache and rate limits
133
+
134
+ Downloads are cached under `~/.cache/rxiv2pdf/` (`%LOCALAPPDATA%\rxiv2pdf\Cache` on Windows;
135
+ `$XDG_CACHE_HOME` and `RXIV2PDF_CACHE_DIR` are honoured), so re-runs make no network requests
136
+ unless you pass `--refresh`.
137
+
138
+ bioRxiv rate-limits heavily: after about ten requests it asks clients to wait for up to 4 minutes.
139
+ rxiv2pdf waits and carries on, so a paper with many figures can take a few minutes the first time.
140
+ An interrupted run resumes where it stopped. To add a contact address to the User-Agent, set
141
+ `RXIV2PDF_CONTACT=you@example.org`.
142
+
143
+ ## Limitations
144
+
145
+ - **The PDF can differ slightly from the one on bioRxiv.** It is rebuilt from bioRxiv's XML
146
+ conversion of the submission, not from the authors' PDF. Tables, equations and the lettering
147
+ inside figures come only as images, so they can't be searched or copied as text. The conversion
148
+ sometimes drops a space or garbles a character; rxiv2pdf repairs the cases it recognises and
149
+ prints a `fixed: …` line for each kind. Details such as word counts or ORCID IDs on the authors'
150
+ title page aren't in the XML. The running text otherwise matches closely. If you find a
151
+ difference in a preprint you care about, please
152
+ [open an issue](https://github.com/ali-zaeim/rxiv2pdf/issues).
153
+ - **Some false citations stay linked.** bioRxiv's XML sometimes marks numbers in superscript as
154
+ citations, such as residue labels like D118<sup>3.49</sup> or powers of ten; rxiv2pdf unlinks
155
+ those. A superscript range such as a peptide fragment (NTS<sup>8–13</sup>) looks exactly like a
156
+ citation range, so it stays linked. In the `default` and `compact` themes it still reads as on
157
+ bioRxiv; in `sans`, which brackets citations, it shows as NTS [8–13].
158
+ - **Equation images can differ in size,** because authors export them at different zoom levels.
159
+ - **The page before end-of-article figures can be sparse.** Those figures start a new page, so
160
+ the last page of text may hold only a few lines.
161
+ - **CJK characters** aren't covered by the bundled fonts.
162
+ - **Only bioRxiv and medRxiv are supported.** ChemRxiv, for example, publishes only the authors'
163
+ PDFs, so there is no XML to rebuild a paper from.
164
+
165
+ ## Development
166
+
167
+ The pipeline is resolve → fetch → parse → render → compile, one module per stage in
168
+ `src/rxiv2pdf/`. All styling lives in `templates/journal.typ`; themes are parameter sets in
169
+ `templates/themes/`.
170
+
171
+ ```console
172
+ $ uv sync # Python env with dev dependencies
173
+ $ uv run pytest # offline tests
174
+ $ uv run ruff check src tests scripts # lint (CI runs this too)
175
+ $ uv run pytest -m network # live test against bioRxiv (slow: rate limits)
176
+ $ UPDATE_SNAPSHOTS=1 uv run pytest tests/test_jats.py # after an intended model change
177
+ $ uv run python scripts/preview.py previews/ # page-1 PNG of every fixture
178
+ ```
179
+
180
+ `tests/fixtures/` holds real bioRxiv/medRxiv API responses and JATS XML, and `tests/snapshots/`
181
+ the parsed model of each as JSON. bioRxiv supplies every table and equation as an image, so the
182
+ XML-table and MathML code is tested on native-JATS articles from other journals in
183
+ `tests/fixtures/external/` (sources and licenses in its README).
184
+
185
+ ## License
186
+
187
+ rxiv2pdf is released under the MIT License (see `LICENSE`). Bundled fonts: Source Serif 4 and
188
+ Source Sans 3 (SIL Open Font License 1.1, see `src/rxiv2pdf/fonts/`).
189
+
190
+ rxiv2pdf is meant for personal reading. A rebuilt PDF is a derivative of the preprint, so sharing
191
+ it must follow the preprint's own license, which is printed on page 1.
@@ -0,0 +1,158 @@
1
+ # rxiv2pdf
2
+
3
+ [![CI](https://github.com/ali-zaeim/rxiv2pdf/actions/workflows/ci.yml/badge.svg)](https://github.com/ali-zaeim/rxiv2pdf/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/rxiv2pdf)](https://pypi.org/project/rxiv2pdf/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/rxiv2pdf)](https://pypi.org/project/rxiv2pdf/)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/ali-zaeim/rxiv2pdf/blob/main/LICENSE)
7
+
8
+ Turn a bioRxiv or medRxiv preprint into a two-column, journal-style PDF.
9
+
10
+ The PDF on bioRxiv and medRxiv is the authors' submitted manuscript, often double-spaced and
11
+ line-numbered, with the figures at the end. rxiv2pdf rebuilds the paper from the preprint's JATS
12
+ XML instead, with each figure placed after the text that first cites it and the citations linked
13
+ to the references.
14
+
15
+ ```console
16
+ $ rxiv2pdf https://www.biorxiv.org/content/10.64898/2026.10.16.759679v1
17
+ PDF 2026.10.16.759679v1.pdf
18
+ ```
19
+
20
+ | Page 1 | A figure page |
21
+ |:--:|:--:|
22
+ | ![Page 1: front matter, abstract box, two-column body](https://raw.githubusercontent.com/ali-zaeim/rxiv2pdf/main/docs/screenshots/page1.png) | ![A full-width figure with its caption](https://raw.githubusercontent.com/ali-zaeim/rxiv2pdf/main/docs/screenshots/figure-page.png) |
23
+
24
+ <sub>Screenshots on this page: Ghosh *et al.*, "Astrocytic sEH–EET signaling regulates microglial
25
+ lysosomal function and suppresses the STING–Type I interferon pathway in Alzheimer's disease",
26
+ bioRxiv [10.64898/2026.10.16.759679](https://doi.org/10.64898/2026.10.16.759679), CC BY 4.0.</sub>
27
+
28
+ ## Install
29
+
30
+ Requires Python 3.11+. Typst comes with the `typst` Python package and the fonts are bundled, so
31
+ nothing else is needed.
32
+
33
+ ```console
34
+ $ pipx install rxiv2pdf # or: uv tool install rxiv2pdf
35
+ $ rxiv2pdf --help
36
+ ```
37
+
38
+ `pip install rxiv2pdf` works too. To try it without installing anything:
39
+ `uvx rxiv2pdf 10.1101/2024.04.17.590000`.
40
+
41
+ ## Usage
42
+
43
+ ```console
44
+ $ rxiv2pdf 10.1101/2024.04.17.590000 # bare DOI (bioRxiv by default, medRxiv tried next)
45
+ $ rxiv2pdf https://www.medrxiv.org/content/10.64898/2026.09.25.26364016v1.full
46
+ $ rxiv2pdf https://doi.org/10.64898/2026.10.22.760766 -o papers/ --theme compact --paper a4
47
+ $ rxiv2pdf 10.1101/2020.03.22.002386v2 # a specific version (default: latest)
48
+ $ rxiv2pdf --list dois.txt -o papers/ # batch: one URL/DOI per line, # comments ok
49
+ ```
50
+
51
+ All options are listed under [Options](#options).
52
+
53
+ ## What you get
54
+
55
+ - **Front matter:** title, authors and affiliations, abstract, and a banner with the DOI, date and
56
+ version. Page 1 notes that the preprint isn't peer reviewed and gives its license.
57
+ - **Two columns** with aligned baselines and a balanced last page.
58
+ - **Figures and tables** after their first citation, full width when wide, and sized by their
59
+ lettering, so a simple chart isn't blown up. Long tables and figures after the references get
60
+ pages of their own.
61
+ - **Linked citations,** collapsed into ranges, and DOI links in the references.
62
+ - **Equations, back matter** (funding, data availability…) and a banner on withdrawn preprints.
63
+
64
+ ## Themes
65
+
66
+ Choose a look with `--theme`:
67
+
68
+ - `default`: serif body, numbered sans headings in navy, a shaded abstract box.
69
+ - `compact`: a dense letters-journal look, with smaller serif text on narrow margins, a serif title,
70
+ a bold abstract, capitalised headings and run-in subheadings. About 20% fewer pages.
71
+ - `sans`: a modern open-access look, with a teal band, a large title, ragged-right sans text, ruled
72
+ headings and bracketed citations.
73
+
74
+ One column per theme, each showing page 1, page 2 and a figure page of the same preprint:
75
+
76
+ ![The same preprint in the three themes: page 1, page 2 and a figure page](https://raw.githubusercontent.com/ali-zaeim/rxiv2pdf/main/docs/screenshots/themes.png)
77
+
78
+ ## Options
79
+
80
+ | Option | Description |
81
+ |---|---|
82
+ | `-o, --outdir DIR` | Where the PDF goes. Default: the current directory. |
83
+ | `--preprint-version N` | Preprint version; overrides a `vN` in the URL. Default: the `vN` in the URL, else the latest. |
84
+ | `--server biorxiv\|medrxiv` | Override server detection. Default: detected from the URL; a bare DOI tries bioRxiv, then medRxiv. |
85
+ | `--theme default\|compact\|sans` | Look of the PDF; see [Themes](#themes). Default: `default`. |
86
+ | `--paper letter\|a4` | Paper size. Default: `letter` (US Letter). |
87
+ | `--list FILE` | Batch mode. Keeps going after failures, prints a summary, exits 1 if any failed. |
88
+ | `--supplements` | Also download the supplementary files, into `<name>-supplements/` next to the PDF. They are listed and linked in the PDF either way. |
89
+ | `--append-supplements` | Append the supplementary PDFs and images to the end of the PDF, one bookmarked section per file (other files, such as spreadsheets, stay linked). |
90
+ | `--fallback-pdf` | If no JATS XML exists yet (very new posts), save the server's original PDF. |
91
+ | `--refresh` | Ignore the cache and download again. |
92
+ | `--open` | Open the PDF when done. |
93
+ | `-v`, `-vv` | `-v`: log each figure download, list every figure and table, and print a traceback on internal errors. `-vv`: also log every HTTP request. |
94
+ | `--version` | Show the rxiv2pdf version and exit. |
95
+
96
+ Exit codes: 0 success, 1 failure (not found, no XML yet, network, internal error), 2 bad input,
97
+ 130 interrupted (re-run to resume from the cache).
98
+
99
+ ### Cache and rate limits
100
+
101
+ Downloads are cached under `~/.cache/rxiv2pdf/` (`%LOCALAPPDATA%\rxiv2pdf\Cache` on Windows;
102
+ `$XDG_CACHE_HOME` and `RXIV2PDF_CACHE_DIR` are honoured), so re-runs make no network requests
103
+ unless you pass `--refresh`.
104
+
105
+ bioRxiv rate-limits heavily: after about ten requests it asks clients to wait for up to 4 minutes.
106
+ rxiv2pdf waits and carries on, so a paper with many figures can take a few minutes the first time.
107
+ An interrupted run resumes where it stopped. To add a contact address to the User-Agent, set
108
+ `RXIV2PDF_CONTACT=you@example.org`.
109
+
110
+ ## Limitations
111
+
112
+ - **The PDF can differ slightly from the one on bioRxiv.** It is rebuilt from bioRxiv's XML
113
+ conversion of the submission, not from the authors' PDF. Tables, equations and the lettering
114
+ inside figures come only as images, so they can't be searched or copied as text. The conversion
115
+ sometimes drops a space or garbles a character; rxiv2pdf repairs the cases it recognises and
116
+ prints a `fixed: …` line for each kind. Details such as word counts or ORCID IDs on the authors'
117
+ title page aren't in the XML. The running text otherwise matches closely. If you find a
118
+ difference in a preprint you care about, please
119
+ [open an issue](https://github.com/ali-zaeim/rxiv2pdf/issues).
120
+ - **Some false citations stay linked.** bioRxiv's XML sometimes marks numbers in superscript as
121
+ citations, such as residue labels like D118<sup>3.49</sup> or powers of ten; rxiv2pdf unlinks
122
+ those. A superscript range such as a peptide fragment (NTS<sup>8–13</sup>) looks exactly like a
123
+ citation range, so it stays linked. In the `default` and `compact` themes it still reads as on
124
+ bioRxiv; in `sans`, which brackets citations, it shows as NTS [8–13].
125
+ - **Equation images can differ in size,** because authors export them at different zoom levels.
126
+ - **The page before end-of-article figures can be sparse.** Those figures start a new page, so
127
+ the last page of text may hold only a few lines.
128
+ - **CJK characters** aren't covered by the bundled fonts.
129
+ - **Only bioRxiv and medRxiv are supported.** ChemRxiv, for example, publishes only the authors'
130
+ PDFs, so there is no XML to rebuild a paper from.
131
+
132
+ ## Development
133
+
134
+ The pipeline is resolve → fetch → parse → render → compile, one module per stage in
135
+ `src/rxiv2pdf/`. All styling lives in `templates/journal.typ`; themes are parameter sets in
136
+ `templates/themes/`.
137
+
138
+ ```console
139
+ $ uv sync # Python env with dev dependencies
140
+ $ uv run pytest # offline tests
141
+ $ uv run ruff check src tests scripts # lint (CI runs this too)
142
+ $ uv run pytest -m network # live test against bioRxiv (slow: rate limits)
143
+ $ UPDATE_SNAPSHOTS=1 uv run pytest tests/test_jats.py # after an intended model change
144
+ $ uv run python scripts/preview.py previews/ # page-1 PNG of every fixture
145
+ ```
146
+
147
+ `tests/fixtures/` holds real bioRxiv/medRxiv API responses and JATS XML, and `tests/snapshots/`
148
+ the parsed model of each as JSON. bioRxiv supplies every table and equation as an image, so the
149
+ XML-table and MathML code is tested on native-JATS articles from other journals in
150
+ `tests/fixtures/external/` (sources and licenses in its README).
151
+
152
+ ## License
153
+
154
+ rxiv2pdf is released under the MIT License (see `LICENSE`). Bundled fonts: Source Serif 4 and
155
+ Source Sans 3 (SIL Open Font License 1.1, see `src/rxiv2pdf/fonts/`).
156
+
157
+ rxiv2pdf is meant for personal reading. A rebuilt PDF is a derivative of the preprint, so sharing
158
+ it must follow the preprint's own license, which is printed on page 1.
@@ -0,0 +1,66 @@
1
+ [project]
2
+ name = "rxiv2pdf"
3
+ version = "0.1.0"
4
+ description = "Turn a bioRxiv/medRxiv preprint into a two-column, journal-style PDF built from its JATS XML."
5
+ readme = "README.md"
6
+ license = "MIT AND OFL-1.1"
7
+ license-files = [
8
+ "LICENSE",
9
+ "src/rxiv2pdf/fonts/OFL-*.md",
10
+ ]
11
+ requires-python = ">=3.11"
12
+ keywords = [
13
+ "biorxiv",
14
+ "medrxiv",
15
+ "preprint",
16
+ "jats",
17
+ "pdf",
18
+ "typst",
19
+ ]
20
+ classifiers = [
21
+ "Development Status :: 4 - Beta",
22
+ "Environment :: Console",
23
+ "Intended Audience :: Science/Research",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Programming Language :: Python :: 3.14",
29
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
30
+ "Topic :: Text Processing :: Markup :: XML",
31
+ ]
32
+ dependencies = [
33
+ "httpx>=0.27",
34
+ "lxml>=5.3",
35
+ "pillow>=11.0",
36
+ "rich>=13.7",
37
+ "typer>=0.16",
38
+ "typst>=0.15",
39
+ ]
40
+
41
+ [[project.authors]]
42
+ name = "Ali Zaeim"
43
+
44
+ [project.scripts]
45
+ rxiv2pdf = "rxiv2pdf.cli:main"
46
+
47
+ [project.urls]
48
+ Homepage = "https://github.com/ali-zaeim/rxiv2pdf"
49
+ Repository = "https://github.com/ali-zaeim/rxiv2pdf"
50
+ Issues = "https://github.com/ali-zaeim/rxiv2pdf/issues"
51
+ Changelog = "https://github.com/ali-zaeim/rxiv2pdf/blob/main/CHANGELOG.md"
52
+
53
+ [dependency-groups]
54
+ dev = [
55
+ "pytest>=8.3",
56
+ "ruff>=0.17,<0.18",
57
+ ]
58
+
59
+ [build-system]
60
+ requires = ["uv_build>=0.8,<0.14"]
61
+ build-backend = "uv_build"
62
+
63
+ [tool.pytest.ini_options]
64
+ testpaths = ["tests"]
65
+ addopts = "-m 'not network'"
66
+ markers = ["network: hits the live bioRxiv/medRxiv servers (run with: pytest -m network)"]
@@ -0,0 +1,53 @@
1
+ [project]
2
+ name = "rxiv2pdf"
3
+ version = "0.1.0"
4
+ description = "Turn a bioRxiv/medRxiv preprint into a two-column, journal-style PDF built from its JATS XML."
5
+ readme = "README.md"
6
+ license = "MIT AND OFL-1.1" # code MIT; bundled Source Serif/Sans fonts OFL-1.1
7
+ license-files = ["LICENSE", "src/rxiv2pdf/fonts/OFL-*.md"]
8
+ authors = [{ name = "Ali Zaeim" }]
9
+ requires-python = ">=3.11"
10
+ keywords = ["biorxiv", "medrxiv", "preprint", "jats", "pdf", "typst"]
11
+ classifiers = [
12
+ "Development Status :: 4 - Beta",
13
+ "Environment :: Console",
14
+ "Intended Audience :: Science/Research",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Programming Language :: Python :: 3.14",
20
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
21
+ "Topic :: Text Processing :: Markup :: XML",
22
+ ]
23
+ dependencies = [
24
+ "httpx>=0.27",
25
+ "lxml>=5.3",
26
+ "pillow>=11.0",
27
+ "rich>=13.7",
28
+ "typer>=0.16", # older versions crash on --help with Click 8.2+
29
+ "typst>=0.15", # Compiler.compile_with_warnings / query with sys_inputs
30
+ ]
31
+
32
+ [project.scripts]
33
+ rxiv2pdf = "rxiv2pdf.cli:main"
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/ali-zaeim/rxiv2pdf"
37
+ Repository = "https://github.com/ali-zaeim/rxiv2pdf"
38
+ Issues = "https://github.com/ali-zaeim/rxiv2pdf/issues"
39
+ Changelog = "https://github.com/ali-zaeim/rxiv2pdf/blob/main/CHANGELOG.md"
40
+
41
+ [dependency-groups]
42
+ dev = ["pytest>=8.3", "ruff>=0.17,<0.18"] # ruff pinned: its default rule set changes between minor versions
43
+
44
+ [build-system]
45
+ requires = ["uv_build>=0.8,<0.14"]
46
+ build-backend = "uv_build"
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = ["tests"]
50
+ addopts = "-m 'not network'"
51
+ markers = [
52
+ "network: hits the live bioRxiv/medRxiv servers (run with: pytest -m network)",
53
+ ]
@@ -0,0 +1,8 @@
1
+ """rxiv2pdf: bioRxiv/medRxiv preprint -> journal-style PDF, built from JATS XML."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("rxiv2pdf") # single source: pyproject.toml
7
+ except PackageNotFoundError: # running from a source tree that was never installed
8
+ __version__ = "0.0.0+unknown"
@@ -0,0 +1,5 @@
1
+ """`python -m rxiv2pdf` runs the CLI."""
2
+
3
+ from .cli import main
4
+
5
+ main()