pdfmd-cli 3.21.5__tar.gz → 3.24.19__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 (89) hide show
  1. pdfmd_cli-3.24.19/PKG-INFO +1131 -0
  2. pdfmd_cli-3.24.19/README.md +1097 -0
  3. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd.py +10562 -5238
  4. pdfmd_cli-3.24.19/pdfmd_cli.egg-info/PKG-INFO +1131 -0
  5. pdfmd_cli-3.24.19/pdfmd_cli.egg-info/SOURCES.txt +81 -0
  6. pdfmd_cli-3.24.19/pdfmd_cli.egg-info/entry_points.txt +5 -0
  7. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_cli.egg-info/requires.txt +7 -0
  8. pdfmd_cli-3.24.19/pdfmd_cli.egg-info/top_level.txt +5 -0
  9. pdfmd_cli-3.24.19/pdfmd_office/__init__.py +29 -0
  10. pdfmd_cli-3.24.19/pdfmd_office/docx.py +426 -0
  11. pdfmd_cli-3.24.19/pdfmd_office/docx_post.py +92 -0
  12. pdfmd_cli-3.24.19/pdfmd_office/fonts.py +39 -0
  13. pdfmd_cli-3.24.19/pdfmd_office/fragments.py +232 -0
  14. pdfmd_cli-3.24.19/pdfmd_office/labels.py +131 -0
  15. pdfmd_cli-3.24.19/pdfmd_office/odt.py +155 -0
  16. pdfmd_cli-3.24.19/pdfmd_office/office.lua +1408 -0
  17. pdfmd_cli-3.24.19/pdfmd_office/spec.py +106 -0
  18. pdfmd_cli-3.24.19/pdfmd_office/units.py +117 -0
  19. pdfmd_cli-3.24.19/pdfmd_setup/__init__.py +26 -0
  20. pdfmd_cli-3.24.19/pdfmd_setup/fancy.py +192 -0
  21. pdfmd_cli-3.24.19/pdfmd_setup/plain.py +113 -0
  22. pdfmd_cli-3.24.19/pdfmd_setup/registry.py +221 -0
  23. pdfmd_cli-3.24.19/pdfmd_unicode/__init__.py +16 -0
  24. pdfmd_cli-3.24.19/pdfmd_unicode/_catalog.py +549 -0
  25. pdfmd_cli-3.24.19/pdfmd_unicode/_scripts.py +1009 -0
  26. pdfmd_cli-3.24.19/pdfmd_unicode/_tessdata_catalog.py +133 -0
  27. pdfmd_cli-3.24.19/pdfmd_unicode/colorfont.py +331 -0
  28. pdfmd_cli-3.24.19/pdfmd_unicode/emoji.py +38 -0
  29. pdfmd_cli-3.24.19/pdfmd_unicode/fonts.py +287 -0
  30. pdfmd_cli-3.24.19/pdfmd_unicode/install.py +215 -0
  31. pdfmd_cli-3.24.19/pdfmd_unicode/plan.py +696 -0
  32. pdfmd_cli-3.24.19/pdfmd_unicode/scripts.py +149 -0
  33. pdfmd_cli-3.24.19/pdfmd_unicode/tessdata.py +160 -0
  34. pdfmd_cli-3.24.19/pdfmd_unicode/translit.py +220 -0
  35. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pyproject.toml +14 -2
  36. pdfmd_cli-3.24.19/tests/test_api.py +69 -0
  37. pdfmd_cli-3.24.19/tests/test_apple_emoji.py +59 -0
  38. pdfmd_cli-3.24.19/tests/test_attach.py +739 -0
  39. pdfmd_cli-3.24.19/tests/test_cache_location.py +164 -0
  40. pdfmd_cli-3.24.19/tests/test_completion.py +47 -0
  41. pdfmd_cli-3.24.19/tests/test_config.py +148 -0
  42. pdfmd_cli-3.24.19/tests/test_csv_table.py +62 -0
  43. pdfmd_cli-3.24.19/tests/test_display_path.py +43 -0
  44. pdfmd_cli-3.24.19/tests/test_doctor.py +45 -0
  45. pdfmd_cli-3.24.19/tests/test_install_ocr.py +57 -0
  46. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/tests/test_lookup.py +5 -0
  47. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/tests/test_native.py +6 -1
  48. pdfmd_cli-3.24.19/tests/test_office.py +476 -0
  49. pdfmd_cli-3.24.19/tests/test_office_latex.py +411 -0
  50. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/tests/test_packaging.py +12 -0
  51. pdfmd_cli-3.24.19/tests/test_pdf_input.py +143 -0
  52. pdfmd_cli-3.24.19/tests/test_polish.py +289 -0
  53. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/tests/test_sections.py +5 -0
  54. pdfmd_cli-3.24.19/tests/test_setup.py +218 -0
  55. pdfmd_cli-3.24.19/tests/test_tessdata.py +102 -0
  56. pdfmd_cli-3.24.19/tests/test_text_input.py +69 -0
  57. pdfmd_cli-3.24.19/tests/test_translit.py +133 -0
  58. pdfmd_cli-3.24.19/tests/test_unicode.py +819 -0
  59. pdfmd_cli-3.21.5/PKG-INFO +0 -606
  60. pdfmd_cli-3.21.5/README.md +0 -577
  61. pdfmd_cli-3.21.5/pdfmd_cli.egg-info/PKG-INFO +0 -606
  62. pdfmd_cli-3.21.5/pdfmd_cli.egg-info/SOURCES.txt +0 -36
  63. pdfmd_cli-3.21.5/pdfmd_cli.egg-info/entry_points.txt +0 -2
  64. pdfmd_cli-3.21.5/pdfmd_cli.egg-info/top_level.txt +0 -2
  65. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/LICENSE +0 -0
  66. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_cli.egg-info/dependency_links.txt +0 -0
  67. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/LICENSE +0 -0
  68. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/VENDORED.md +0 -0
  69. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/__init__.py +0 -0
  70. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/_kerning_data.py +0 -0
  71. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/assets/fonts/DejaVuSans-LICENSE.txt +0 -0
  72. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/assets/fonts/DejaVuSans.ttf +0 -0
  73. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/ast.py +0 -0
  74. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/cidfont.py +0 -0
  75. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/embedded.py +0 -0
  76. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/embedded_metrics.py +0 -0
  77. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/emoji.py +0 -0
  78. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/emoji_font.py +0 -0
  79. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/fonts.py +0 -0
  80. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/html_filter.py +0 -0
  81. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/image_loader.py +0 -0
  82. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/layout.py +0 -0
  83. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/parser.py +0 -0
  84. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/pdf.py +0 -0
  85. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/py.typed +0 -0
  86. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/render.py +0 -0
  87. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/truetype.py +0 -0
  88. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/pdfmd_inkmd/url_filter.py +0 -0
  89. {pdfmd_cli-3.21.5 → pdfmd_cli-3.24.19}/setup.cfg +0 -0
@@ -0,0 +1,1131 @@
1
+ Metadata-Version: 2.4
2
+ Name: pdfmd-cli
3
+ Version: 3.24.19
4
+ Summary: One command from Markdown to a good-looking PDF: a Pandoc wrapper with smart defaults, project-file discovery and a multi-engine fallback chain
5
+ Author: Ali Perdekhan
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/aliperdehan/pdfmd
8
+ Project-URL: Changelog, https://github.com/aliperdehan/pdfmd/blob/main/CHANGELOG.md
9
+ Keywords: pandoc,markdown,pdf,latex,cli
10
+ Classifier: Environment :: Console
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
13
+ Classifier: Topic :: Text Processing :: Markup :: LaTeX
14
+ Requires-Python: >=3.9
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ License-File: pdfmd_inkmd/LICENSE
18
+ License-File: pdfmd_inkmd/assets/fonts/DejaVuSans-LICENSE.txt
19
+ Requires-Dist: pyyaml
20
+ Requires-Dist: pypdf
21
+ Provides-Extra: math
22
+ Requires-Dist: pymd2pdf<0.7,>=0.6; python_version >= "3.11" and extra == "math"
23
+ Requires-Dist: matplotlib; extra == "math"
24
+ Provides-Extra: emoji
25
+ Requires-Dist: inkmd<0.6,>=0.5; extra == "emoji"
26
+ Provides-Extra: translit
27
+ Requires-Dist: pypinyin; extra == "translit"
28
+ Requires-Dist: anyascii; extra == "translit"
29
+ Provides-Extra: pandoc
30
+ Requires-Dist: pypandoc_binary; extra == "pandoc"
31
+ Provides-Extra: tui
32
+ Requires-Dist: prompt_toolkit>=3; extra == "tui"
33
+ Dynamic: license-file
34
+
35
+ # pdfmd
36
+
37
+ [![Test](https://github.com/aliperdehan/pdfmd/actions/workflows/publish.yml/badge.svg)](https://github.com/aliperdehan/pdfmd/actions/workflows/publish.yml) [![PyPI](https://img.shields.io/pypi/v/pdfmd-cli)](https://pypi.org/project/pdfmd-cli/)
38
+
39
+ **One command from Markdown to a good-looking PDF.** `pdfmd` wraps
40
+ [Pandoc](https://pandoc.org) and fills in everything you would otherwise
41
+ have to remember: sensible fonts and margins, the right Markdown dialect,
42
+ your project's metadata/preamble/filter files, and a fallback chain across
43
+ every PDF engine you have installed.
44
+
45
+ ```console
46
+ $ pdfmd lecture
47
+ AUTO: READER TITLE MARGIN MONOFONT. Use --verbose to see in full
48
+ OK lecture.md
49
+ ```
50
+
51
+ <p align="center">
52
+ <img src="https://raw.githubusercontent.com/aliperdehan/pdfmd/main/docs/lecture.png" width="560" alt="lecture.md rendered to PDF">
53
+ </p>
54
+
55
+ That PDF came from [`examples/lecture.md`](https://github.com/aliperdehan/pdfmd/blob/main/examples/lecture.md), a plain
56
+ Markdown file with no front matter and no configuration. Plain `pandoc
57
+ lecture.md -o lecture.pdf` doesn't even get that far: its default engine,
58
+ `pdflatex`, stops at the `Δ` on line 5 with an error. With a Unicode engine
59
+ it would build, but with wide default margins, and with the `# Title`
60
+ line as an ordinary section heading instead of a title.
61
+
62
+ ## Why
63
+
64
+ Pandoc can do nearly anything, but its defaults assume you'll pass the right
65
+ flags every time. In practice that means one of two things: a long command
66
+ you copy from an old shell history, or a Makefile in every folder. `pdfmd`
67
+ turns that knowledge into defaults:
68
+
69
+ - **It decides per document, not globally.** A file with no YAML front
70
+ matter is treated as ordinary GitHub-flavoured Markdown. A file that has
71
+ front matter is assumed to be written for Pandoc and is left alone.
72
+ - **It finds your project files.** A `metadata.yaml`, `preamble.tex` or
73
+ `<name>.lua` beside the document (or in a `metadata/` folder next to it)
74
+ is picked up automatically, so every document in a folder shares one
75
+ house style without any flags.
76
+ - **It doesn't give up on the first engine.** If `lualatex` fails or isn't
77
+ installed, it tries the next engine, then the next, and tells you why each
78
+ one failed.
79
+ - **It handles every script and emoji.** Text the main font cannot draw (Arabic,
80
+ Hebrew, Chinese, Japanese, Korean, Greek, Cyrillic with Kazakh letters, Indic and
81
+ more) is set run by run in an installed font for its script, emoji become colour
82
+ pictures, and `pdfmd --install fonts` fetches missing fonts without admin rights
83
+ (see [Other scripts](#other-scripts)).
84
+ - **It finds documents by name, in any script.** `pdfmd glyukoza` opens
85
+ `Глюкоза.md`; with `--translit` packs, `pdfmd tyche` finds `Τύχη.md` and
86
+ `pdfmd mingyun` finds `命運.md`.
87
+ - **It reads PDFs too.** `pdfmd paper.pdf` restores a PDF's own embedded source or
88
+ OCRs the pages to Markdown (see [PDF to Markdown](#pdf-to-markdown)).
89
+ - **It shows what it did.** Every automatic decision prints an `AUTO` line,
90
+ `-v` shows the exact Pandoc command, and every default can be switched off.
91
+
92
+ ## Install
93
+
94
+ ```sh
95
+ pipx install pdfmd-cli
96
+ ```
97
+
98
+ That puts a `pdfmd` command on your PATH, with its Python dependencies, in
99
+ its own isolated environment. The same with uv:
100
+
101
+ ```sh
102
+ uv tool install pdfmd-cli # install; update with: uv tool upgrade pdfmd-cli
103
+ uvx --from pdfmd-cli pdfmd file.md # or run once without installing
104
+ ```
105
+
106
+ On macOS, Homebrew installs it together with Pandoc and Typst, so nothing else
107
+ is needed for a first PDF:
108
+
109
+ ```sh
110
+ brew install aliperdehan/tap/pdfmd
111
+ ```
112
+
113
+ To update later, run `pipx upgrade pdfmd-cli`. (The package is named
114
+ `pdfmd-cli` because `pdfmd` on PyPI is an unrelated PDF-to-Markdown tool.
115
+ The command is still `pdfmd`. For the latest unreleased code, use `pipx
116
+ install git+https://github.com/aliperdehan/pdfmd`.)
117
+
118
+ For the full pipeline, `pdfmd` drives programs that pip can't install, so you
119
+ also need (without them it still makes a plain PDF; see
120
+ [No Pandoc?](#no-pandoc-the-built-in-fallback) below). The quickest way, with no
121
+ admin rights, is `pdfmd --install full`, which puts Pandoc and Typst in pdfmd's own
122
+ folders; the system installers below do the same job:
123
+
124
+ - **[Pandoc](https://pandoc.org/installing.html)**
125
+ - **at least one PDF engine**. [Typst](https://typst.app) is the quickest
126
+ start; a TeX distribution (MacTeX, TeX Live) gives the best results and
127
+ is what you need for LaTeX packages and math-heavy documents.
128
+
129
+ ```sh
130
+ brew install pipx pandoc typst # macOS, the quick start
131
+ brew install --cask mactex-no-gui # optional: full LaTeX (large)
132
+ ```
133
+
134
+ ```sh
135
+ sudo apt install pipx pandoc texlive-xetex # Debian/Ubuntu
136
+ sudo dnf install pipx pandoc texlive-xetex # Fedora (add Typst with `pdfmd --install typst`)
137
+ sudo pacman -S python-pipx pandoc-cli typst texlive # Arch
138
+ ```
139
+
140
+ On any Linux, `pdfmd --install full` (Pandoc and Typst) needs no package manager and no root.
141
+
142
+ ```powershell
143
+ py -m pip install --user pipx; py -m pipx ensurepath # Windows
144
+ winget install --id JohnMacFarlane.Pandoc; winget install --id Typst.Typst
145
+ ```
146
+
147
+ Every push is tested on Windows, macOS and Linux (Pandoc + Typst: single
148
+ files, CSV tables, a book, HTML output); see the
149
+ [Test and publish workflow](https://github.com/aliperdehan/pdfmd/actions/workflows/publish.yml).
150
+ LaTeX engines aren't part of that automated test on Windows yet.
151
+
152
+ Optional extras: [Quarto](https://quarto.org) for `.qmd` files,
153
+ `pandoc-crossref` for `@fig:`/`@tbl:` references, and LibreOffice for
154
+ Office files.
155
+
156
+ <details>
157
+ <summary>Without pipx</summary>
158
+
159
+ `pdfmd.py` is a single file that needs Python 3.9+ (with `pdfmd_inkmd/`
160
+ beside it for the no-Pandoc fallback). It also runs
161
+ directly, and `pyyaml`/`pypdf` are optional (features that need them are
162
+ skipped with a warning):
163
+
164
+ ```sh
165
+ git clone https://github.com/aliperdehan/pdfmd.git ~/pdfmd
166
+ echo 'alias pdfmd="python3 ~/pdfmd/pdfmd.py"' >> ~/.zshrc # or ~/.bashrc
167
+ ```
168
+
169
+ macOS's built-in `/usr/bin/python3` is 3.9, which is too old; pdfmd says
170
+ so and exits.
171
+ </details>
172
+
173
+ Check what `pdfmd` can find on your system:
174
+
175
+ ```console
176
+ $ pdfmd --check-dependencies
177
+ OK pandoc (/opt/homebrew/bin/pandoc)
178
+ OK 1. lualatex (/Library/TeX/texbin/lualatex)
179
+ OK 2. xelatex (/Library/TeX/texbin/xelatex)
180
+ OK 3. pdflatex (/Library/TeX/texbin/pdflatex)
181
+ OK 4. latexmk (/Library/TeX/texbin/latexmk)
182
+ OK 5. tectonic (/opt/homebrew/bin/tectonic)
183
+ OK 6. typst (/opt/homebrew/bin/typst)
184
+ OK 7. weasyprint (/opt/homebrew/bin/weasyprint)
185
+ MISS 8. wkhtmltopdf
186
+ ...
187
+ OK 14. soffice (/Applications/LibreOffice.app/Contents/MacOS/soffice)
188
+ OK quarto (only needed for .qmd files) (/usr/local/bin/quarto)
189
+ ```
190
+
191
+ The numbers are the fallback order, and also shortcuts: `-e 6` means
192
+ `-e typst`.
193
+
194
+ For everything at once, `pdfmd --doctor` is one page on the Markdown-to-PDF tools, fonts
195
+ and emoji, PDF reading (batchocr, Tesseract, Poppler, OCR languages), your config file and
196
+ the cache, ending with the command that fixes each missing piece:
197
+
198
+ ```console
199
+ $ pdfmd --doctor
200
+ ...
201
+ Reading PDFs (PDF to Markdown):
202
+ OK batchocr 1.2.4
203
+ MISS tesseract: not found
204
+ MISS poppler: not found
205
+
206
+ 1 thing(s) to fix:
207
+ 1. tesseract (scanned PDFs) and poppler (scanned PDFs)
208
+ brew install tesseract poppler
209
+ ```
210
+
211
+ ### Tab completion
212
+
213
+ `pdfmd --completion bash|zsh|fish` prints a completion script, generated from the command
214
+ line itself so it never goes out of date:
215
+
216
+ ```sh
217
+ pdfmd --completion zsh > ~/.zfunc/_pdfmd # ~/.zfunc in $fpath, then compinit
218
+ pdfmd --completion bash > ~/.local/share/bash-completion/completions/pdfmd
219
+ pdfmd --completion fish > ~/.config/fish/completions/pdfmd.fish
220
+ ```
221
+
222
+ ### No Pandoc? The built-in fallback
223
+
224
+ On a machine with no Pandoc, or Pandoc but no PDF engine, `pdfmd` does not
225
+ stop: it builds a plain PDF with a pure-Python renderer that `pip` installed
226
+ with it, and says so.
227
+
228
+ ```console
229
+ $ pdfmd notes.md
230
+ NOTE Pandoc was not found: building with the built-in renderer (inkmd). The output is plain ...
231
+ NATIVE notes.md via inkmd
232
+ WARN native (inkmd): notes.md: 2 math expression(s) set as plain text (Unicode, sub/superscripts, display math centred); ...
233
+ OK notes.md
234
+ ```
235
+
236
+ - **[inkmd](https://github.com/eagredev/inkmd)**, written by Dylan Moir (MIT
237
+ licence), is built in (a copy is vendored in `pdfmd_inkmd/`, about 2 MB,
238
+ standard library only, works offline, same input gives the same bytes). It reads
239
+ GitHub-flavoured Markdown: headings, emphasis, lists, task lists, tables,
240
+ code blocks, quotes, links, images, and PNG/JPEG. It cannot typeset math,
241
+ so formulas are set as readable text instead: Greek letters and operators
242
+ as Unicode, `x^2` and `x_i` as super/subscripts, `\frac{a}{b}` as `a/b`,
243
+ and display math (`$$...$$`, `\begin{equation}`, `aligned`) as its own
244
+ centred lines. No bookmarks, no page numbers.
245
+ - **md2pdf** ([pymd2pdf](https://pypi.org/project/pymd2pdf/), ReportLab based) is
246
+ used when installed, for documents with footnotes, math or a title:
247
+ `pdfmd --install math` (same as `pip install "pdfmd-cli[math]"`; about
248
+ 150 MB, Python 3.11+, installs matplotlib so formulas render offline, centred,
249
+ as real math; the few matplotlib cannot read are set as text like inkmd's).
250
+ Footnotes, bookmarks and syntax-highlighted code come with it.
251
+ - `pdfmd --install emoji` (about 11 MB; `pdfmd-cli[emoji]` is the pip way for the
252
+ built-in renderer only) puts the colour emoji font in pdfmd's own fonts folder,
253
+ where the built-in renderer and every engine that can draw colour emoji
254
+ (Typst) find it; without it emoji print as `[rocket]`-style labels in the
255
+ built-in renderer.
256
+ - `pdfmd --install full` leaves the fallback behind: it installs **Pandoc** (the
257
+ real binary from PyPI's `pypandoc_binary`, about 35 MB, also `--install pandoc`
258
+ / `pdfmd-cli[pandoc]`) and **Typst** (Typst's own release from GitHub, about
259
+ 15 MB, also `--install typst`, checked against the SHA-256 GitHub lists) into
260
+ pdfmd's own folders (`~/.local/share/pdfmd/bin`, `%LOCALAPPDATA%\pdfmd\bin`),
261
+ with no admin rights. A Pandoc or Typst already on your PATH always wins; to
262
+ remove them, `pip uninstall pypandoc_binary` and delete the `typst` file.
263
+
264
+ Every input is treated as GitHub-flavoured Markdown. Pandoc-only syntax is
265
+ converted where possible (`\newpage` and `<!-- pagebreak -->` become a page
266
+ break, footnotes become endnotes for inkmd, `$` prices are not mistaken for math)
267
+ and removed otherwise (heading and image attributes, `:::` divs, raw LaTeX,
268
+ `<!-- comments -->`), with one warning per kind. CSV tables
269
+ (`::: {.csv file="data.csv"}`) work as they do with Pandoc. The title, author and date in the
270
+ front matter become a title block and the PDF's own title and author; other
271
+ front-matter keys (`documentclass`, `header-includes`, `pdfmd-options`) are
272
+ listed as not used. There are no filters, preambles, citations, slides, parts or
273
+ report mode, and only Markdown input: for any of those, `pdfmd --install full`
274
+ (or `brew install pandoc typst`, `winget install JohnMacFarlane.Pandoc Typst.Typst`).
275
+
276
+ The built-in renderer is chosen automatically only when there is no Pandoc
277
+ route; a Pandoc build that fails never falls back to it. Ask for it with
278
+ `-e inkmd`, `-e md2pdf` or `-e native`, or `pdf-engine: inkmd` in `pdfmd-options`.
279
+ When it runs on a terminal and had to leave something out, `pdfmd` offers the
280
+ upgrades above (set `PDFMD_NO_PROMPT=1` to silence that; choosing "don't ask
281
+ again" remembers it).
282
+
283
+ ## Usage
284
+
285
+ Every command below can be run from inside [`examples/`](https://github.com/aliperdehan/pdfmd/tree/main/examples/).
286
+
287
+ ### One document
288
+
289
+ ```sh
290
+ pdfmd lecture # finds lecture.md, writes lecture.pdf beside it
291
+ pdfmd lecture.md --open # ...and opens it when done
292
+ pdfmd ~/notes/lecture.md -d # write the PDF into the current directory instead
293
+ pdfmd lecture -w # watch: rebuild on every save, until Ctrl+C
294
+ ```
295
+
296
+ A bare name is looked up as `<name>.md`. The name can contain dots:
297
+ `pdfmd notes-v1.2` builds `notes-v1.2.md`.
298
+
299
+ If no file has exactly that name, pdfmd also looks at what each document is
300
+ called inside: its title, and any `pdfmd-options: {alias: ...}`. Case, spaces,
301
+ `_`, `-`, accents and script don't matter (`pdfmd animportantdocument` finds
302
+ `An Important Document.md`; `pdfmd glyukoza` finds a document titled
303
+ `Глюкоза`), and a unique *start* of a name or title works too, with a warning
304
+ saying what it matched (`pdfmd animp`, `pdfmd glucose`). A name that fits two
305
+ documents is an error, never a guess; `--no-auto lookup` switches the guessing
306
+ off. An exact file name always wins.
307
+
308
+ **Names in other scripts.** Russian, Ukrainian, Belarusian and Kazakh Cyrillic are
309
+ always understood. Other scripts are off until you ask, one pack per script, so
310
+ nobody carries tables they don't use: `--translit greek,hangul` (or
311
+ `PDFMD_TRANSLIT=greek,hangul`) makes `pdfmd tyche` find `Τύχη.md` and
312
+ `pdfmd unmyeong` find `운명.md`. Built in: `greek`, `armenian`, `georgian`,
313
+ `hebrew`, `arabic` (consonants only, as the scripts write them), `hangul`
314
+ (Revised Romanization) and `kana` (Hepburn). `han` gives pinyin without tones
315
+ (`pdfmd mingyun` finds `命運.md`) and `other` covers every remaining script;
316
+ they use [pypinyin](https://pypi.org/project/pypinyin/) (MIT) and
317
+ [anyascii](https://pypi.org/project/anyascii/) (ISC), installed with
318
+ `pdfmd --install translit` (`pip install "pdfmd-cli[translit]"`; about 3 MB).
319
+ `--translit all` turns every pack on, `--translit none` even Cyrillic off,
320
+ `--translit list` shows what is ready. These are for *finding* a name: plain
321
+ letters, no tones, vowels left out where the script leaves them out. Names that
322
+ romanize alike (homophones) are listed as ambiguous, never picked between. The
323
+ same spelling rules find a section by its heading (`doc#tyche`).
324
+
325
+ ### One section
326
+
327
+ `pdfmd doc#onlyapart` builds just the section whose heading is "Only a Part"
328
+ (down to the next heading of the same or a higher level), written as
329
+ `doc.only-a-part.pdf` beside the document. Headings are named the way files
330
+ are: case, spaces and spelling don't matter, a unique start works (with a
331
+ warning), and so does a `{#label}`. `doc##yield` asks for a level-2 heading,
332
+ `doc#results/yield` for one under another, `doc#a+b` for several, and
333
+ `pdfmd '#yield'` uses the folder's only Markdown file. `pdfmd doc --list-parts`
334
+ shows what can be named. In a document split into parts the same names work
335
+ for headings inside the parts.
336
+
337
+ Anything with a `{#label}` can be named too: `pdfmd doc#fig:setup` builds just
338
+ that figure, `doc#eq:energy` that equation, and likewise a table, a fenced div,
339
+ a code block or a span (the figure's number restarts at 1).
340
+
341
+ ### Other output formats
342
+
343
+ The format is taken from `-o`'s extension, or given explicitly with `--to`:
344
+
345
+ ```sh
346
+ pdfmd lecture -o lecture.html
347
+ pdfmd lecture -o lecture.docx
348
+ pdfmd lecture --to typst -o lecture.typ
349
+ pdfmd lecture -o lecture.tex # a complete, compilable .tex, not a fragment
350
+ ```
351
+
352
+ ### Word and OpenDocument output
353
+
354
+ Plain Pandoc ignores `papersize`, `geometry`, `mainfont` and `fontsize` when it writes `.docx`
355
+ or `.odt`: you get a Letter page in the default fonts. pdfmd writes them into the *reference
356
+ document* Pandoc builds the file from, so the page and the text follow the same front matter as the PDF:
357
+
358
+ | Front matter | In the Word file |
359
+ |---|---|
360
+ | `papersize: a4` (also `letter`, `legal`, `a5`, `b5`..., `classoption: [a4paper, landscape]`) | page size and orientation |
361
+ | `geometry:` (a list, `margin=2cm`, `top=1in,left=...`, `paperwidth=...`) or `margin:` | page margins |
362
+ | `mainfont`, `sansfont`, `monofont`, `CJKmainfont`, `fontsize`, `linestretch`, `lang`, `indent` | body, heading and code fonts, sizes, line spacing, language, first-line indent |
363
+
364
+ A `.docx` carries font *names*, not fonts, and the person opening it has Times New Roman, not
365
+ STIX Two Text. So well-known fonts Word does not ship are mapped to the closest one it does (STIX
366
+ Two Text, Libertinus, Latin Modern to Times New Roman; Helvetica Neue, Inter to Arial; JetBrains
367
+ Mono, Menlo, Fira Code to Consolas), with an `AUTO OFFICE` note; `office: {fonts: exact}` keeps
368
+ your names. Without any of these keys pdfmd's own look applies: black bold headings sized like a LaTeX
369
+ article, a centred title block, booktabs-style table rules, Times New Roman and Consolas.
370
+
371
+ **Your own template.** A reference document decides the page and fonts itself, and pdfmd then
372
+ changes only what the document's `office:` block names. It is found, in order, as
373
+ `office: {reference-doc: FILE}`, `<name>-reference.docx` or `reference.docx` (`.dotx` works too,
374
+ `.odt`/`.ott` for ODT, `.pptx`/`.potx` for slides) beside the document, in its `metadata/` folder or
375
+ beside its metadata files, and finally in `~/.config/pdfmd/`. Pandoc's own `--reference-doc` is left
376
+ alone. Only builds whose output is `.docx`/`.odt`/`.pptx` look for one. `pdfmd --init-reference`
377
+ writes `reference.docx` (also `odt`, `pptx`): Pandoc's styles with pdfmd's look, to restyle in Word.
378
+
379
+ A template keeps its own style names. `office: {styles: {Heading1: LRH1, BodyText: LRNormal, Table: TableGrid}}`
380
+ makes the Heading 1, Body Text and table Pandoc writes look like the template's own `LR H1`, `LR Normal` and
381
+ `Table Grid` (a missing Pandoc style is created from the source); `office: {replace: {NULAB-KICKER: "Course
382
+ wording"}}` rewrites text in the template's headers and footers; `office: {title-page: true}` gives the first
383
+ page a page of its own, without header or footer.
384
+
385
+ **A LaTeX package can tell pdfmd about itself.** Beside a package's `.sty` (or in an `office/` or `pdfmd/` folder there)
386
+ pdfmd looks for `<package>-pdfmd.yaml`, and for its Word support: `<package>-reference.docx`/`.dotx`,
387
+ `<package>-office.lua` (the profile above) and `<package>-office.yaml` (the `office:` defaults, with `by-option:`
388
+ entries for what an option of `\usepackage[opt]{package}` adds). A document that loads the package gets all of it with
389
+ no setting of its own, and a package without these files behaves exactly as before.
390
+
391
+ `<package>-pdfmd.yaml` holds `latex-keys:`, the front-matter keys the package's macros are filled from
392
+ (`experiment: LabExperiment` turns `experiment: Titration` into `\renewcommand{\LabExperiment}{Titration}` after the
393
+ preamble), and an optional `office:` section, the same as `<package>-office.yaml`, for a package that wants one file:
394
+
395
+ ```yaml
396
+ latex-keys:
397
+ experiment: LabExperiment
398
+ group: LabGroup
399
+ office:
400
+ reference-doc: house.dotx
401
+ ```
402
+
403
+ ```yaml
404
+ pdfmd-options:
405
+ office:
406
+ reference-doc: ../templates/house.dotx # or leave it to be found
407
+ papersize: a4 # change just this on top of the template
408
+ fonts: exact # keep STIX Two Text and friends as named
409
+ style: plain # no pdfmd look (headings, title block, table rules)
410
+ ```
411
+
412
+ `--no-auto officeref` turns the search off, `--no-auto officestyle` leaves Pandoc's default alone.
413
+
414
+ **LaTeX in the document.** Pandoc turns `$x^2$` into a Word equation and drops everything else of
415
+ LaTeX. For `.docx` and `.odt` pdfmd adds a filter that makes it native where it can:
416
+
417
+ - `\ce{Ag+(aq) + Cl-(aq) -> AgCl(s)}` (mhchem) and `\si{\gram\per\mole}`, `\SI{5}{\milli\liter}`,
418
+ `\num{1.5e-3}` (siunitx) become formatted text with real subscripts, charges and units, and, inside
419
+ math, Word equation pieces;
420
+ - `\begin{equation}`, `align`, `reaction`... become Word equations with their number at the right;
421
+ - raw `figure`, `tabular`, lists, `\emph`, `\newpage`... are read by Pandoc's own LaTeX reader;
422
+ - `\ref`, `\eqref`, `\cref`, `\Cref`, `\pageref` and the "Figure 3." in front of a caption use the numbers
423
+ LaTeX itself gave (pdfmd builds the PDF once, in its cache, and reads the `.aux`; reused while the sources
424
+ are older), with the caption separator (`labelsep=`) and `\crefname`s of your preamble;
425
+ - **what is left is drawn by LaTeX**: tikz, chemfig, a house style's own macros, math Pandoc cannot convert are compiled in
426
+ *your document's own preamble* (the same fonts, packages and macros as the PDF, one fragment to a page), and the
427
+ pages go into the Word file as vector pictures: an SVG with a 300 dpi PNG fallback (Word 2016 and later draw the SVG,
428
+ LibreOffice and older readers the PNG). An inline fragment sits on the text's baseline, a wide one is scaled to the
429
+ text width, a figure's caption stays text with its number. `\includegraphics{plot.pdf}` and `![](plot.pdf)` (Word cannot
430
+ show a PDF) are converted the same way. Pictures are cached by content, so a rebuild draws only what changed
431
+ (`pdfmd --clear-cache` empties it). A fragment that will not compile is named in a warning and left out; the rest of
432
+ the document is built. Needs `pdftocairo` (Poppler) and a LaTeX engine; without them the fragments are reported only.
433
+ - **a house style's own macros** can be told to pdfmd: `office.lua` (or `<name>-office.lua`) beside the document, in its
434
+ `metadata/` folder or in `~/.config/pdfmd/`, or `office: {profile: FILE}`, is a small Lua file:
435
+ `return {ignore = {"\\LabCaptionBegin"}, commands = {prelab = function(args) return {pandoc.Strong{pandoc.Str("[pre-lab " .. args[1] .. "]")}} end}}`
436
+ says which macros to drop and what the others mean in a Word file (a command may return text, or blocks such as a
437
+ native table); an optional `pandoc = function(doc, helpers)` hook may restructure the whole document. The helpers
438
+ include `inlines(tex)`/`blocks(tex)` (LaTeX read with the filter's own math, `\ce` and `\si` applied),
439
+ `read_file(name)` (a file the document's `\input` would find), `number_caption(table, "table")`, `labels()`,
440
+ `preamble()`, `meta()`, `warn(text)`; a profile can thus build a house table from the package's own data, as nulabreport
441
+ does for `\chemicals`.
442
+ - what cannot be done at all is named in a warning (`-v` lists it).
443
+
444
+ `pdfmd report.md --check-docx` says what a Word build would make native and what it would draw as pictures, naming the
445
+ environment or macro behind each (`the tikzpicture environment`, `the macro \irspectrum`), without drawing or writing anything: a
446
+ quick way to see what is left to give a recipe (see the profile below).
447
+
448
+ `pdfmd-options: {office: {latex: off}}` or `--no-auto officelatex` skips all of it, `office: {labels: off}`
449
+ skips the PDF build (numbers are then counted).
450
+
451
+ The same filter serves the other outputs when LaTeX is the problem: **the soffice fallback** (the Word file above, then LibreOffice: `pdfmd -e soffice report.md` is the `.docx` as LibreOffice draws it) now
452
+ keeps the page size, margins and fonts, shows equations and draws what LaTeX can; a **Typst or WeasyPrint run after LaTeX
453
+ failed** gets what the document says in LaTeX as native text or pictures instead of nothing; and HTML, Typst and
454
+ WeasyPrint builds do it on request (`pdfmd-options: {office: {latex: auto}}`).
455
+
456
+ ### Stopping part-way
457
+
458
+ `--stop-at` ends the build after a stage; everything before it runs as normal:
459
+
460
+ ```sh
461
+ pdfmd report --stop-at markdown # report.assembled.md: the parts joined into one file
462
+ pdfmd report --assemble-only # the same, shorter
463
+ pdfmd report --stop-at tex # the standalone .tex a LaTeX engine would get (= --to latex)
464
+ ```
465
+
466
+ The assembled file holds just the document text (and is marked
467
+ `pdfmd-assembled: true`), so pdfmd never joins its parts a second time.
468
+
469
+ By default it holds just the text. `--embed-metadata` also folds in what pdfmd
470
+ finds beside the document (metadata, preamble, Lua filters, and the
471
+ bibliography and CSL files the metadata names), so the file no longer needs
472
+ them beside it. What the text points at otherwise (images, files a preamble
473
+ `\input`s) is not embedded: keep it where the document finds it, relative to
474
+ the assembled file.
475
+
476
+ ```sh
477
+ pdfmd report --assemble-only --embed-metadata # metadata.yaml, preamble.tex, Lua filters, .bib/.csl
478
+ pdfmd report --assemble-only --embed-metadata metadata preamble # only those
479
+ pdfmd report --assemble-only --embed-metadata --lua-mode ref # name the filter instead of copying it
480
+ ```
481
+
482
+ An embedded Lua filter sits in a `{=pdfmd}` block at the end of the file. A
483
+ Lua filter can run any command, so one only runs if this machine's pdfmd
484
+ embedded it (otherwise it is skipped with a warning, unless you pass
485
+ `--trust-embedded`).
486
+
487
+ `--lua-mode apply` runs the filters at assembly time instead, so the text
488
+ already has their effect (approximate: Pandoc re-writes the text, and a filter
489
+ that looks at `FORMAT` is embedded instead). `--unpack` goes the other way:
490
+
491
+ ```sh
492
+ pdfmd report.assembled.md --unpack # filters, preamble and metadata back into report.assembled.unpacked/
493
+ ```
494
+
495
+ `pdfmd` finds `report.assembled.unpacked/` beside `report.assembled.md` by
496
+ itself (its metadata, preambles and Lua filters), and `--unpack --slim` strips
497
+ the unpacked parts out of the assembled file, leaving the lean document plus
498
+ that folder.
499
+
500
+ A document can name its own files and set what to embed, in `pdfmd-options`:
501
+
502
+ ```yaml
503
+ pdfmd-options:
504
+ yaml: [base.yaml] # metadata files (like -y); also `metadata:`, or grouped:
505
+ metadata:
506
+ preamble: my-preamble.tex
507
+ lua-filter: my.lua
508
+ embed: {lua: ref} # what --assemble-only embeds without the flag
509
+ ```
510
+
511
+ Plain HTML output is a fragment. For a finished page, or one file with
512
+ everything (images, CSS) inlined, ask for it, on the command line or in the
513
+ document:
514
+
515
+ ```sh
516
+ pdfmd lecture -o lecture.html --self-contained
517
+ ```
518
+
519
+ ```yaml
520
+ pdfmd-options:
521
+ default-output: html # build to HTML when no format is given
522
+ html: {self-contained: true, css: style.css}
523
+ ```
524
+
525
+ ### The PDF carries its own source
526
+
527
+ A PDF can hold the document it was built from, so the file you send is also the
528
+ file you can edit again. Ask for it, on the command line or in the document:
529
+
530
+ ```sh
531
+ pdfmd report --attach-source # the source goes into report.pdf as an attachment
532
+ pdfmd report --bundle # ... and the data and files its LaTeX reads
533
+ pdfmd --restore report.pdf # write the folder back into report.restored/
534
+ ```
535
+
536
+ ```yaml
537
+ pdfmd-options:
538
+ attach-source: true # alias: embed-source
539
+ bundle: true # or `all`: the whole folder, not just what the text points at
540
+ bundle-packages: true # also your own LaTeX packages (see below)
541
+ strip-comments: true # the default for the attached copy; see "Comments"
542
+ attach-bibliography: used # or `all`
543
+ ```
544
+
545
+ Put these in the shared metadata file and every document that finds it carries
546
+ its source. The attachment is the assembled Markdown with the metadata,
547
+ preamble, Lua filters, bibliography and CSL style folded in (the file
548
+ `--assemble-only --embed-metadata` writes), plus a manifest of the original
549
+ layout, so `--restore` gives back `report.md`, `metadata/`, `parts/` and the rest
550
+ where they were, each as a standalone file (a symlinked metadata file or
551
+ bibliography comes back as a copy). Any PDF viewer lists the attachments, and
552
+ `pdfmd --restore report.pdf --list` shows them and what a rebuild needs. A
553
+ restored folder builds the same document: checked on lab reports with a house
554
+ style, a compound library, a shared bibliography and parts.
555
+
556
+ - **Images come out of the PDF itself.** The PDF holds every picture it drew, so
557
+ a plain attach needs no bundle for them: a JPEG comes back byte for byte, a PNG
558
+ as the same pixels (alpha kept), and an SVG or PDF figure as a vector PDF
559
+ (`fig.svg` becomes `fig.pdf`, and the restored Markdown follows).
560
+ - **`--bundle` is for what the PDF does not draw:** CSV data, LaTeX included
561
+ with `\input`, listings. They are stored as separate attachments (`files/...`),
562
+ found from the text, the preamble and, in turn, from the files found; also
563
+ those only your own TeX tree resolves (`\input{library}` from `texmf-home`).
564
+ `--bundle all` takes the whole folder except output and housekeeping. Over
565
+ 100 MB (`bundle-max-mb`) only the source is attached. Data whose file name a
566
+ macro builds cannot be found: use `all`.
567
+ - **Your own LaTeX packages** (a house style in `texmf-home`) are recorded with
568
+ their version, and `--restore` tells you what is missing; `--bundle-packages`
569
+ stores them too (with what they `\input` and the graphics they name).
570
+ The TeX distribution's own packages, fonts and Pandoc are not carried.
571
+ - **Comments.** Notes to yourself do not travel: the attached source loses its
572
+ `<!-- -->` comments, the LaTeX preamble's `%` comments, the text between a
573
+ `.bib`'s entries and a CSL file's XML comments. Choose per kind:
574
+ `strip-comments: [preamble, bibliography]` or `{markdown: false}`
575
+ (`--strip-comments-in`, `--keep-comments-in`). Code blocks, `\verb`, `\url` and
576
+ verbatim environments are never touched, and a `%` that closes a line keeps a
577
+ bare `%`, because it swallows the line break. YAML metadata is merged and
578
+ written again, so its `#` comments are always gone.
579
+ - **Only the bibliography entries you cite** are attached (and what they
580
+ cross-reference), not a shared file of hundreds; `attach-bibliography: all`
581
+ keeps it whole, and `nocite: '@*'` keeps every entry.
582
+ - **It is opt-in, and it is everything you wrote.** An attached source includes
583
+ what the author would not send in an email; check `--list` before sharing.
584
+ - `--restore` never overwrites a file and never writes outside its folder. A
585
+ restored Lua filter is code that pdfmd runs beside the document, so restore
586
+ names it: read it first if the PDF is not from you.
587
+ - `-r/--report` builds attach their chapters too. A section build (`doc#section`)
588
+ attaches nothing.
589
+
590
+ ### PDF to Markdown
591
+
592
+ The direction follows the files you give it:
593
+
594
+ ```sh
595
+ pdfmd paper.pdf # -> paper.md, beside it
596
+ pdfmd paper.pdf -o notes/p.md # an exact file; -o notes/ puts notes/paper.md
597
+ pdfmd paper.pdf --to txt # plain text
598
+ pdfmd scan.pdf --lang eng+rus --ocr tesseract --export-images
599
+ ```
600
+
601
+ A PDF that carries its own pdfmd source (above) is restored instead; `--extract`
602
+ (or naming an output file) reads its pages. Everything else is read by
603
+ [batchocr](https://github.com/aliperdehan/batchocr): the text layer in reading
604
+ order, OCR for scanned pages, headings, lists, tables, running headers removed,
605
+ `<!-- Page N -->` markers (`--no-page-markers`, `--page-breaks`, `--keep-headers`).
606
+ pdfmd only routes: any flag it does not know goes to batchocr unchanged, so the
607
+ options of [M1ck4's pdfmd](https://github.com/M1ck4/pdfmd) (`--ocr`, `--lang`,
608
+ `--export-images`, `--page-breaks`, `--preview-only`, `--stats`, `-q`, `--output`)
609
+ work as they did. Install the reader once:
610
+
611
+ ```sh
612
+ pdfmd --install batchocr # pip install batchocr[md] from its GitHub release,
613
+ # then offers the system programs below
614
+ ```
615
+
616
+ Scanned pages also need **Tesseract** (OCR) and **Poppler** (`pdftotext`, `pdftoppm`).
617
+ Those are separate programs with their own builds for every platform, so pdfmd does not
618
+ bundle them; `--install batchocr` finds your package manager (Homebrew, apt, dnf, pacman,
619
+ zypper, apk, scoop, Chocolatey or winget), shows the exact command and, on an interactive
620
+ terminal, runs it if you say yes (`PDFMD_NO_PROMPT=1` only prints it). By hand:
621
+
622
+ ```sh
623
+ brew install tesseract poppler # macOS (and Linuxbrew)
624
+ sudo apt install tesseract-ocr poppler-utils # Debian/Ubuntu; dnf: tesseract poppler-utils
625
+ scoop install tesseract poppler # Windows (or choco install ...)
626
+ ```
627
+
628
+ OCR languages are packs of their own. `pdfmd --install ocr` lists the Tesseract languages
629
+ pdfmd can fetch, and `pdfmd --install ocr:rus,kaz` (or `ru`, `kazakh`, `zh`, `ja`...) downloads
630
+ them into pdfmd's own folder, with no admin rights and each file checked against a checksum
631
+ pinned in pdfmd (the compact `tessdata_fast` models, Apache-2.0; English and the orientation
632
+ model come along). `pdfmd scan.pdf --lang rus` then uses that folder whenever it has every
633
+ language you asked for, and Tesseract's own languages otherwise (a `TESSDATA_PREFIX` you set
634
+ always wins). `pdfmd --uninstall ocr:rus` removes one. The package managers have them too:
635
+ `brew install tesseract-lang`, `apt install tesseract-ocr-rus`, `dnf install
636
+ tesseract-langpack-rus`. `pdfmd --check-dependencies` reports batchocr, Tesseract and Poppler.
637
+
638
+ Credits and licences: batchocr's Markdown structure stages are M1ck4's
639
+ (MIT; that project is archived) and are vendored inside batchocr with their
640
+ licence. PyMuPDF, which they use, is AGPL-3.0: it is installed as its own
641
+ package by batchocr's `md` extra and is never part of pdfmd. The command name
642
+ `pdfmd` is also used by that project and by an unrelated PyPI package of the same
643
+ name; this one installs as `pdfmd-cli` (and `pdfmd`), so if two are installed use
644
+ `pdfmd-cli` to be sure which one runs.
645
+
646
+ ### Bookmarks, header and footer, linked files
647
+
648
+ Ideas from [mdpdf](https://github.com/normanlorrain/mdpdf) (MIT), done on the
649
+ finished PDF with pypdf, so they work with every engine. All off unless you ask:
650
+
651
+ ```sh
652
+ pdfmd report.md --bookmarks # PDF bookmarks from the headings, if the engine made none
653
+ pdfmd report.md --header "{title},,{date}" --footer ",Page {page} of {pages},{header}"
654
+ pdfmd report.md --attach-links # the local files the text links to travel inside the PDF
655
+ pdfmd report.md --pdf-author "A. Writer" --pdf-keywords "kinetics, rate" --paper letter
656
+ ```
657
+
658
+ A template is `left,middle,right`; `{page} {pages} {header} {date} {title}` are
659
+ filled in per page (`{header}` is the current top-level heading; write `\,` for a
660
+ comma inside a field). The text is Helvetica, so letters outside Latin-1 become
661
+ their base letter or `?`. `--attach-links` stores each linked file as
662
+ `linked/PATH` and puts a paperclip in the margin beside the link.
663
+
664
+ ### Use it from Python
665
+
666
+ ```python
667
+ import pdfmd
668
+
669
+ pdfmd.convert_file("report.md", "docx") # -> Path("report.docx")
670
+ pdfmd.convert_file("report.md", "pdf", "out/report.pdf", variables={"geometry": "margin=2cm"},
671
+ extra_args=["--engine", "typst"])
672
+ html = pdfmd.convert_text("# Hi\n\nSome *text*.\n", "html") # str for text formats, bytes for pdf/docx/odt/pptx/epub
673
+ rst = pdfmd.convert_text(open("notes.rst").read(), "docx", format="rst")
674
+ ```
675
+
676
+ Each call runs the command-line tool (as `pypandoc` runs Pandoc), so a document builds exactly as `pdfmd` would build it,
677
+ with all the auto-discovery of the document's folder, and nothing in the caller's process is touched. The source is not
678
+ stamped or backed up unless you ask (`stamp=True`, `backup=True`). A failure raises `pdfmd.PdfmdError` (with
679
+ `returncode`, `stdout` and `stderr`); `check=False` returns the path anyway. `metadata=` takes metadata files,
680
+ `variables=` a dict of Pandoc variables, `extra_args=` anything else the command line takes, `cwd=` the folder it runs
681
+ in and `timeout=` seconds.
682
+
683
+ ### Other command names: `mdpdf` and `inkmd`
684
+
685
+ pdfmd also installs `mdpdf` and `inkmd` commands that take those tools' own keys
686
+ and do what they did, with pdfmd behind them:
687
+
688
+ ```sh
689
+ mdpdf -o out.pdf -f ",,{page}" -a "Name" -p A4 chapter1.md chapter2.md
690
+ inkmd notes.md -o notes.pdf --page-size a4 --family times
691
+ cat notes.md | inkmd > notes.pdf # standard input and output, like inkmd
692
+ inkmd notes.md -e lualatex # naming another engine uses it instead
693
+ ```
694
+
695
+ `mdpdf` turns bookmarks and attached links on and combines several inputs into one
696
+ PDF. `inkmd` is `pdfmd -e inkmd`; like inkmd it writes the PDF to standard output
697
+ when you give `-o -`, or no `-o` while output is piped (at a terminal it writes
698
+ `notes.pdf` beside the file instead). The default page is pdfmd's, not Letter: pass
699
+ `-p letter` / `--page-size letter`. If the original `mdpdf` or `inkmd` package is
700
+ installed too, whichever was installed last owns the command name; both accept the
701
+ original's keys.
702
+
703
+ ### Slides
704
+
705
+ ```sh
706
+ pdfmd slides -p # Beamer slides; each heading starts a slide
707
+ ```
708
+
709
+ <p align="center">
710
+ <img src="https://raw.githubusercontent.com/aliperdehan/pdfmd/main/docs/slides.png" width="380" alt="A Beamer slide from examples/slides.md">
711
+ </p>
712
+
713
+ ### A whole folder
714
+
715
+ ```console
716
+ $ pdfmd notes -b
717
+ AUTO: READER TITLE MARGIN MONOFONT. Use --verbose to see in full
718
+ AUTO: MARGIN. Use --verbose to see in full
719
+ AUTO: MARGIN. Use --verbose to see in full
720
+ OK notes/lecture.md
721
+ OK notes/week1.md
722
+ OK notes/week2.md
723
+ ```
724
+
725
+ `-b` converts every `.md` in the folder into its own PDF, in parallel
726
+ (`-j N` sets the number of workers). Add `--recursive` to include
727
+ subfolders, and `-o DIR` to collect the PDFs somewhere else.
728
+
729
+ ### A book or report from several files
730
+
731
+ ```console
732
+ $ pdfmd book -r -o book.pdf
733
+ OK book/01-intro.md
734
+ OK book/02-methods.md
735
+ AUTO: MARGIN. Use --verbose to see in full
736
+ OK REPORT book.pdf
737
+ ```
738
+
739
+ `-r` (also spelled `--report` or `--book`) joins every `.md` in the folder
740
+ into a single PDF, in the order of each file's `chapter:` front-matter
741
+ field. See [`examples/book/`](https://github.com/aliperdehan/pdfmd/tree/main/examples/book/). `-i FILE` leaves one file
742
+ out, and `--exclude-unnumbered` skips files without a `chapter:`.
743
+
744
+ ### One long document in several files
745
+
746
+ For an article-style document that has grown too long to edit as one file
747
+ (and unlike `-r`, one that should read as a single document, not a
748
+ sequence of chapters), keep a *scaffold* with the front matter, and the
749
+ text in a `parts/` folder beside it, one file per section, each starting with
750
+ its own heading:
751
+
752
+ ```
753
+ report.md front matter only (title, author, ...)
754
+ parts/10-introduction.md
755
+ parts/20-methods.md
756
+ parts/30-discussion.md
757
+ ```
758
+
759
+ Switch it on once, in a `metadata.yaml` shared by your documents, so the
760
+ content files carry no typesetting:
761
+
762
+ ```yaml
763
+ pdfmd-options:
764
+ parts: auto # a document is a scaffold only if parts/ or sections/ exists
765
+ ```
766
+
767
+ ```console
768
+ $ pdfmd report # the whole document, report.pdf
769
+ $ pdfmd report#methods # just that part, report.methods.pdf
770
+ $ pdfmd report#discussion+appendix # several: always in report order
771
+ $ pdfmd parts/20-methods.md # same as report#methods
772
+ $ pdfmd report --list-parts
773
+ $ pdfmd old-report.md --split new-folder # cut an existing single file into parts
774
+ $ pdfmd old-report.md --split new-folder --split-depth 2 # ...and subsections too
775
+ ```
776
+
777
+ The parts are joined in filename order into one Pandoc run, so the result is
778
+ identical to the same text in a single file: labels, citations and numbering
779
+ work across parts, and paths are written relative to the scaffold's folder
780
+ whichever part they are in. A part rebuilt alone is much faster to compile
781
+ but cannot see the others, so references to them print as `??`. See
782
+ [`examples/parts/`](https://github.com/aliperdehan/pdfmd/tree/main/examples/parts/).
783
+
784
+ #### Faster rebuilds: the cache
785
+
786
+ ```yaml
787
+ pdfmd-options:
788
+ cache: {aux: true} # or: pdfmd report --cache
789
+ ```
790
+
791
+ keeps LaTeX's cross-reference files between builds (in `~/.cache/pdfmd`), so
792
+ an unchanged document is typeset once instead of two or three times, and a
793
+ part built on its own shows the real numbers of the parts left out (taken
794
+ from the last full build) instead of `??`. It never skips a build: Pandoc and
795
+ LaTeX still process the whole document from the current sources and package
796
+ every time, so a change shows in the next build. `cache: {plots: true}` (or
797
+ `--cache-plots`, with nulabreport >= 1.26.0) additionally stores each plot as a
798
+ PDF and reuses it until the package, the preamble, the engine or the plot's
799
+ data file changes. Off by default; `--no-cache` for one build; `pdfmd --clear-cache`
800
+ (or deleting the folder) is always safe.
801
+
802
+ **Where the cache lives.** By default in `~/.cache/pdfmd`, one folder per document.
803
+ Folders get moved (a lab report from Downloads into OneDrive), and a cache keyed by
804
+ the old path would be lost: so each cache folder records which document it is, and a
805
+ document with no cache under its new path takes over the one of the same document
806
+ that has lost its original: the same content (SHA-256), or, if you edited it before
807
+ moving it, the same name in a folder of the same name, when only one fits. A copy
808
+ (the original still there) never takes the original's cache, and plot entries find
809
+ their data files again under the new folders. Or keep the cache with the document:
810
+
811
+ ```yaml
812
+ pdfmd-options:
813
+ cache: {aux: true, location: document} # or: --cache-location document
814
+ ```
815
+
816
+ puts it in a `.cache/pdfmd` folder beside the document (with a `CACHEDIR.TAG`, so
817
+ backup tools skip it, and a `.gitignore`), which travels with the folder. It is
818
+ yours to choose per document, in the config file for everything, or with
819
+ `--cache-location`. `--clear-cache` clears both places. (The colour-emoji pictures
820
+ LaTeX uses are in the same cache.)
821
+
822
+ ### Tables straight from a CSV file
823
+
824
+ ```markdown
825
+ Measured values:
826
+
827
+ ::: {.csv file="data.csv"}
828
+ :::
829
+ ```
830
+
831
+ <p align="center">
832
+ <img src="https://raw.githubusercontent.com/aliperdehan/pdfmd/main/docs/results.png" width="480" alt="A CSV file rendered as a table">
833
+ </p>
834
+
835
+ - Cells are read as Pandoc Markdown (`H~2~O`, `$x^2$`, `\ce{...}`, `[@key]`); `reader="gfm"` makes them plain GFM.
836
+ - A caption is the line after the block (`: Caption {#tbl:id}`, or `Table: Caption`), or `caption="Caption"` inside the
837
+ braces, with the identifier as `#tbl:id` there too. A caption in the attributes wins over a line after the block.
838
+ - A relative `file=` is found beside the document (and along the resource path), wherever Pandoc runs.
839
+ - The delimiter is detected from the extension (`.tsv` means tab), or set
840
+ with `delimiter=";"`.
841
+ - The first row is the header unless you add `header="false"`.
842
+ - Large files are capped at 10 rows × 7 columns, so a huge CSV can't
843
+ silently fill 40 pages. `rows=all` or `cols=20` raise the cap. When a
844
+ table is cut, the PDF itself shows a note saying so.
845
+
846
+ This works for every output format.
847
+
848
+ ### Not just Markdown
849
+
850
+ ```sh
851
+ pdfmd paper.tex # compiled directly with a LaTeX engine: reruns until
852
+ # references settle, runs bibtex/biber, and leaves
853
+ # no .aux/.log clutter (--keep-aux keeps them)
854
+ pdfmd minutes.docx # Word/PowerPoint/Excel/ODF: converted by LibreOffice
855
+ pdfmd analysis.qmd # handed to Quarto, so code chunks actually run
856
+ pdfmd page.html # anything else Pandoc can read (give the extension)
857
+ ```
858
+
859
+ **A plain-text file as Markdown (opt-in).** `pdfmd notes.txt --text-to-markdown` reads a `.txt` file the way a person typed it
860
+ and guesses its structure: headings (capitals, `1.2 Title`, `====` underlines), bullet and numbered lists, tables from
861
+ columns separated by runs of spaces, indented blocks as code, hard-wrapped lines joined into paragraphs. The words never
862
+ change (the letters and digits of the result are the input's), and a text that measures as garbled (symbol soup, OCR
863
+ noise, replacement characters) gets paragraphs only, with the rule that fired printed. `MODE` is `auto` (default),
864
+ `force` (guess even in a garbled text) or `paragraphs`. It needs batchocr 1.2.5 (`pdfmd --install batchocr`), writes `notes.md`
865
+ beside the text (never over a file that is there) and builds from it; with `-o x.md` the conversion is all it does.
866
+ Off unless asked: `--text-to-markdown`, or `options: {text-to-markdown: auto}` in the config file (or `pdfmd --setup`).
867
+
868
+ ## What it does automatically
869
+
870
+ Most of these print an `AUTO` line, and each can be switched off individually.
871
+
872
+ | `AUTO` kind | When | What happens |
873
+ |---|---|---|
874
+ | `READER` | no YAML front matter | reads the file as GitHub-flavoured Markdown (content-sized table columns, relaxed blank-line rules) |
875
+ | `TITLE` | no front matter, first line is `# Title` | that heading becomes the document title, and the remaining headings move up one level |
876
+ | `MARGIN` | no margin or geometry set anywhere | 1-inch margins instead of LaTeX's wide defaults |
877
+ | `MAINFONT` | no `mainfont:` and no `-f` | STIX Two Text (Times New Roman if it isn't installed), or a serif that has the letters when the document is mostly in a script STIX Two Text lacks (Kazakh Cyrillic, say); retried with DejaVu Serif if a glyph is still missing. A font you name is never changed except by `fallback: document` |
878
+ | `UNICODE` | text the main font cannot draw (Arabic, Han, Greek with accents, rare symbols...) | sets each run in an installed font for its script (see *Other scripts*), instead of printing boxes |
879
+ | `MONOFONT` | the document contains code | JetBrains Mono for code (Menlo or another installed monospace font if it isn't installed) |
880
+ | `tablewidth` | a wide pipe table | balances column widths so the table fits the page, and leaves narrow tables at their natural width |
881
+ | `YAML` / `TEX` / `LUA` | project files found | attaches `metadata.yaml`, `preamble.tex`, `<name>.lua` (see below) |
882
+ | `citeproc` | `@key` / `[@key, p. 90]` citations | adds `--citeproc`, so citations and the reference list render from your `bibliography:` without any flag (`--no-citeproc` turns it off) |
883
+ | `crossref` | `@fig:`/`@tbl:` references | adds the `pandoc-crossref` filter, ahead of citeproc |
884
+ | `papersize` | `pagesize: a4` (a common typo) | converts it to Pandoc's real `papersize:` |
885
+
886
+ `-v` explains each decision and prints the exact command it runs:
887
+
888
+ ```console
889
+ $ pdfmd lecture -v
890
+ CMD pandoc lecture.md -o lecture.pdf --pdf-engine=lualatex -f gfm -V 'mainfont=STIX Two Text' -V geometry:margin=1in -V 'monofont=JetBrains Mono' --shift-heading-level-by=-1 --lua-filter .../pdfmd-tablewidth.lua
891
+ CMD pandoc lecture.md -o lecture.pdf --pdf-engine=lualatex -f gfm -V 'mainfont=DejaVu Serif' ...
892
+ AUTO READER lecture.md: no YAML front matter; reading as gfm
893
+ AUTO TITLE lecture.md: promoted leading '# ' heading to Pandoc title metadata
894
+ AUTO MARGIN lecture.md: no geometry/margin set; using geometry:margin=1in on LaTeX-family engines
895
+ AUTO MONOFONT lecture.md: has code but no monofont set; using JetBrains Mono on LaTeX-family engines
896
+ OK lecture.md
897
+ ```
898
+
899
+ The second `CMD` line is the font fallback at work: STIX Two Text was
900
+ missing a glyph, so the document was rebuilt with DejaVu Serif. (Temporary
901
+ file paths are shortened here.)
902
+
903
+ **When a PDF doesn't look the way the Markdown suggests, run `-v` first.**
904
+ The cause is usually one of these automatic decisions, and `-v` names it.
905
+
906
+ ### Other scripts
907
+
908
+ <p align="center">
909
+ <img src="https://raw.githubusercontent.com/aliperdehan/pdfmd/main/docs/scripts.png" width="560" alt="examples/scripts.md: Kazakh, Greek, Arabic, Hebrew, Chinese, Japanese, Korean and Hindi text, and emoji, in one PDF">
910
+ </p>
911
+
912
+ (That is [`examples/scripts.md`](https://github.com/aliperdehan/pdfmd/blob/main/examples/scripts.md), built with no configuration.)
913
+
914
+ A document that mixes scripts needs more than one font. With lualatex or
915
+ xelatex, `pdfmd` reads the character tables of your installed fonts, finds
916
+ the characters the main font cannot draw and sets each run of them in a font
917
+ that can: Arabic and Hebrew right to left, Chinese/Japanese/Korean in the
918
+ flavour the text (kana, hangul) or `lang:` points to, punctuation and marks in
919
+ the font of the text beside them. Characters that no installed font has are
920
+ named in a warning, never dropped or swapped for something else. `-v` shows
921
+ which font each script got.
922
+
923
+ **How much changes is up to you** (`pdfmd-options: {fallback: MODE}`, `--fallback MODE`,
924
+ or the config file's `options:`):
925
+
926
+ | `fallback:` | What happens to a character the main font lacks |
927
+ |---|---|
928
+ | `word` (default) | the whole word goes in one fallback font that draws all of it, so no word mixes two fonts; your main font stays for every other word, even if the document names `DejaVu Serif` |
929
+ | `char` | just that character is set in another installed font |
930
+ | `document` | the installed font that draws most of the document replaces the main font (the old retry-with-DejaVu idea, chosen by coverage) |
931
+ | `off` (or `false`) | nothing: what Pandoc and TeX do without pdfmd (a missing glyph vanishes or prints a box, and they warn) |
932
+ | `box` | no fallback; each missing character is drawn as a black box, so it cannot be overlooked |
933
+ | `error` | no fallback; the build stops, naming the missing characters |
934
+
935
+ `pdfmd --check-fonts doc.md` shows what would happen without building: the main font (and
936
+ why it was changed, if it was), which scripts get which fallback font, the characters no
937
+ installed font draws and the `--install fonts:...` that would fix it; it exits 1 when
938
+ something can't be drawn.
939
+
940
+ Code is held to the same standard: a character the monofont lacks (an arrow, a CJK
941
+ comment) in inline code or a code block is set in a fallback font under LaTeX (a code
942
+ block that needs one is typeset as plain verbatim, without syntax highlighting), and
943
+ through Typst's `codefont` list or a CSS font stack on Typst and WeasyPrint. Math is
944
+ left to its own font.
945
+
946
+ `missing: warn | box | error` (`--missing`) says what happens to a character that *no* installed
947
+ font draws, after the fallback (`box` and `error` as a fallback mode imply it). `char`,
948
+ `word` and `document` also turn emoji into pictures under LaTeX; `off`, `box` and
949
+ `error` leave them alone.
950
+
951
+ Emoji are set as pictures with lualatex and xelatex (which cannot draw colour
952
+ fonts): each emoji, flag, skin tone, family or keycap is looked up in the colour
953
+ emoji font (`pdfmd --install emoji`, a system Noto Color Emoji, or else Apple Color Emoji on
954
+ a Mac), its picture taken out of the font file and included at the height of the text, in
955
+ inline code and code blocks too. Apple's font is read through its glyph names (the same
956
+ way for single emoji, skin tones, flags, keycaps and the joined emoji it has a glyph for,
957
+ such as a woman scientist); the few it composes in tables pdfmd does not read (a family of
958
+ four, say) are reported as undrawable (install Noto for those). The pictures
959
+ are kept in pdfmd's cache folder; a `.tex` written with `--to latex` points
960
+ there.
961
+
962
+ The same fallback works with Typst (`-e typst`: `#text(font: ...)` runs, and
963
+ the fonts folder handed to Typst) and WeasyPrint (styled spans). WeasyPrint draws
964
+ Noto Color Emoji's bitmaps badly, so emoji are left to the system's own emoji font
965
+ there; Typst draws them well.
966
+
967
+ **Getting fonts.** Anything installed on the machine is used. For the rest,
968
+ `pdfmd --install fonts` lists what pdfmd can fetch and
969
+ `pdfmd --install fonts:arabic,cjk-sc` (or `fonts:core`, `fonts:scripts`,
970
+ `fonts:all`; also `fonts:ja`, `fonts:kazakh`...) fetches it into pdfmd's own
971
+ folder (`~/.local/share/pdfmd/fonts`, `%LOCALAPPDATA%\pdfmd\fonts`; no admin
972
+ rights, nothing system-wide). Every file is checked against a checksum pinned in
973
+ pdfmd before it is kept, and each package keeps its licence beside it (all SIL OFL,
974
+ except DejaVu's own free licence). The list: STIX Two (pdfmd's default main font),
975
+ JetBrains Mono (its default code font), Noto Serif, Amiri and Noto Naskh Arabic,
976
+ Hebrew, Armenian, Georgian, the Indic and Southeast Asian scripts, Syriac,
977
+ Thaana, Cherokee, Oriya, Mongolian, Coptic, Gothic, Runic, Tifinagh, N'Ko,
978
+ Canadian syllabics, Urdu Nastaliq (used for `lang: ur`), sans-serif Indic faces,
979
+ symbols, Noto Serif for Chinese (simplified/traditional), Japanese and Korean (one
980
+ weight each, bold is synthesised) and the Noto Sans CJK ones (`cjk-sans`), DejaVu,
981
+ and colour emoji. When a document needs a script no installed font has, the warning names
982
+ the package to install. `pdfmd --uninstall fonts:NAME` removes some.
983
+
984
+ It stays out of the way of a document that has set up its own fonts per
985
+ script (`ucharclasses`, `\newfontfamily`, `xeCJK`, `\babelfont`,
986
+ `CJKmainfont`, `mainfontfallback`): `pdfmd-options: {unicode: true}` adds
987
+ pdfmd's anyway, and `--no-auto unicode` turns it off.
988
+
989
+ ### Switching defaults off
990
+
991
+ ```sh
992
+ pdfmd lecture --no-auto # everything off: close to plain pandoc
993
+ pdfmd lecture --no-auto margin mainfont # only these (kinds: reader title margin mainfont monofont tablewidth
994
+ # unicode lookup yaml tex lua ...; `pdfmd --help` lists them)
995
+ ```
996
+
997
+ ### Passing options to Pandoc
998
+
999
+ Any option `pdfmd` doesn't recognise is passed straight to Pandoc:
1000
+
1001
+ ```sh
1002
+ pdfmd lecture --toc --number-sections
1003
+ pdfmd lecture -V fontsize=12pt
1004
+ ```
1005
+
1006
+ ### Settings inside the document
1007
+
1008
+ Or put settings in the document itself, so nobody has to remember the flag:
1009
+
1010
+ ```yaml
1011
+ ---
1012
+ title: Lab report
1013
+ pdfmd-options:
1014
+ no-auto: [margin, monofont]
1015
+ pdf-engine: tex # only TeX engines; never fall back to HTML ones
1016
+ ---
1017
+ ```
1018
+
1019
+ `pdf-engine:` takes an engine name (`lualatex`, `typst`, ...) or a family:
1020
+ `tex`, `typst`, `html` or `office`. A family limits the fallback chain to
1021
+ that kind of engine. For example, a document full of chemical structures
1022
+ can require TeX, and a plain memo can skip the slow LaTeX run. `-e` on the
1023
+ command line always wins, and a bare `-e` means "try every engine".
1024
+
1025
+ ### Your own defaults: the config file
1026
+
1027
+ `pdfmd --init-config` writes a commented `~/.config/pdfmd/config.yaml`
1028
+ (`$XDG_CONFIG_HOME`; `%APPDATA%\pdfmd` on Windows), `pdfmd --show-config` says
1029
+ where it is and what it sets. Its `options:` block takes the same keys as a
1030
+ document's `pdfmd-options:` and applies to every document, below anything the
1031
+ document or its metadata files say (the command line beats all of them);
1032
+ `translit:` is the romanization packs of the name lookup. `PDFMD_CONFIG=other.yaml`
1033
+ uses another file, `PDFMD_CONFIG=` (empty) none.
1034
+
1035
+ ```yaml
1036
+ translit: [greek, hangul]
1037
+ options:
1038
+ fallback: word
1039
+ pdf-engine: lualatex
1040
+ ```
1041
+
1042
+ **`pdfmd --setup`** changes the same defaults from a menu, without editing the files: a numbered list (nothing to
1043
+ install, works over ssh), each setting with its choices and what they mean. It covers what a document would otherwise
1044
+ say in `pdfmd-options:` or its front matter, for every document:
1045
+
1046
+ - *global metadata* (a `metadata.yaml` beside the config file, below anything a document's own folder says): author, language,
1047
+ paper size, font size, page geometry, main and code font, line spacing, coloured links, table of contents, and for
1048
+ citations the bibliography file, the CSL style and linked citations;
1049
+ - characters a font lacks or none draws, fonts for other scripts, romanization packs;
1050
+ - the citation engine, the PDF engine, the default output format, HTML (one file, math method), LaTeX and reference
1051
+ numbers in Word files;
1052
+ - PDF finishing (header, footer, bookmarks, attached links) and the source file (the "Compiled with..." stamp, backups,
1053
+ comment stripping, attaching the source and its files to the PDF);
1054
+ - the cache, reading `.txt` as Markdown, and which of pdfmd's automatic behaviours (`--no-auto` kinds) are off.
1055
+
1056
+ Choosing 0 (for a text, `-`) gives a setting its built-in default back; `s` saves, `q` quits. The files are rewritten without your
1057
+ comments (the old one is kept as `.bak`). The same keys can be written by hand under `options:` of `config.yaml`. A
1058
+ full-screen version is optional: `pdfmd --install tui` (prompt_toolkit, about 1 MB), then `pdfmd --setup fancy`, or set
1059
+ "This setup screen" to fancy to make it the one `pdfmd --setup` opens. The first time pdfmd finishes a build in a terminal
1060
+ with no config file yet, it prints one `TIP` line about `--setup` (never in a pipe, in CI, or from the Python API;
1061
+ `PDFMD_NO_PROMPT=1` silences it).
1062
+
1063
+ The decisions pdfmd remembers for you (the Lua filters it trusts, an offer you
1064
+ dismissed) live in the same folder, not in `~/.cache/pdfmd`, so cleaning the cache
1065
+ (`--clear-cache`) cannot lose them; files from an older version are moved there
1066
+ the first time they are needed.
1067
+
1068
+ ## Project files
1069
+
1070
+ `pdfmd` looks next to the document, and in a `metadata/` folder beside it,
1071
+ for:
1072
+
1073
+ | File | Used as |
1074
+ |---|---|
1075
+ | `metadata.yaml` | shared Pandoc metadata (fonts, bibliography, CSL, ...) |
1076
+ | `<name>.yaml` | per-document metadata, layered on top of `metadata.yaml` |
1077
+ | `report.yaml` / `book.yaml` | metadata for `-r` builds |
1078
+ | `preamble.tex`, `latex-preamble.tex`, `<name>-preamble.tex`, `preamble-*.tex` | LaTeX added to the header, generic files first |
1079
+ | `<name>.lua` | a Pandoc Lua filter for that document |
1080
+
1081
+ **Only these names are picked up automatically.** An unrelated `.lua` or
1082
+ `.tex` file lying in the folder never changes a render. When several YAML
1083
+ files could apply and none is clearly meant, `pdfmd` stops and asks you to
1084
+ choose with `-y FILE`, rather than guessing. `-y` alone turns discovery
1085
+ off.
1086
+
1087
+ With a `metadata/` folder, a report's own folder can hold nothing but
1088
+ `report.md` and `report.pdf`. Relative paths inside the metadata (such as
1089
+ `bibliography: refs.bib`) resolve from `metadata/`, and image paths in the
1090
+ document still resolve from the document's own folder.
1091
+
1092
+ One shared metadata file can also be **symlinked** into many folders. A
1093
+ relative `bibliography: refs.bib` inside it then finds the `refs.bib`
1094
+ next to the file's real copy, so you never need an absolute path.
1095
+
1096
+ ## Build stamps and snapshots
1097
+
1098
+ Both are off by default.
1099
+
1100
+ - **`--stamp`** keeps a `BUILD NOTES` HTML comment at the end of the `.md`
1101
+ up to date. It records when the document was compiled and with which
1102
+ `pdfmd` version, plus the versions of any LaTeX packages you name with
1103
+ `--stamp-packages`. The comment is invisible in the PDF. `--stamp-mode history` also keeps a list of past compiles.
1104
+ - **`--backup`** saves a timestamped copy of the source into `backup/`
1105
+ after every successful build. A copy is skipped when nothing changed,
1106
+ and `keep: 30` limits how many are kept.
1107
+
1108
+ Both can be turned on for a whole folder from `metadata.yaml`:
1109
+
1110
+ ```yaml
1111
+ pdfmd-options:
1112
+ stamp: true
1113
+ backup: { dir: backup, keep: 30 }
1114
+ ```
1115
+
1116
+ Separately, when `pypdf` is installed, every PDF built by `pdfmd` gets two
1117
+ hidden metadata keys, `PdfmdVersions` and `PdfmdBuildDate`, which you can
1118
+ read with `pdfinfo -meta`. Turn this off with `--no-stamp-pdf-metadata`.
1119
+
1120
+ ## Reference
1121
+
1122
+ - [`docs/recipes.md`](https://github.com/aliperdehan/pdfmd/blob/main/docs/recipes.md) has short "how do I..." answers: a paper in several scripts, a scanned PDF to Markdown, a folder with a house style, a book from many files.
1123
+ - `pdfmd --help` lists every flag.
1124
+ - The docstring at the top of [`pdfmd.py`](https://github.com/aliperdehan/pdfmd/blob/main/pdfmd.py) is the full reference
1125
+ for each behaviour and its edge cases.
1126
+ - [`CHANGELOG.md`](https://github.com/aliperdehan/pdfmd/blob/main/CHANGELOG.md) records what changed in each version and
1127
+ why.
1128
+
1129
+ ## License
1130
+
1131
+ [MIT](https://github.com/aliperdehan/pdfmd/blob/main/LICENSE)