markpublish 1.0.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 (99) hide show
  1. markpublish-1.0.0/LICENSE +22 -0
  2. markpublish-1.0.0/PKG-INFO +339 -0
  3. markpublish-1.0.0/README.md +291 -0
  4. markpublish-1.0.0/pyproject.toml +104 -0
  5. markpublish-1.0.0/setup.cfg +4 -0
  6. markpublish-1.0.0/src/markpublish/__init__.py +6 -0
  7. markpublish-1.0.0/src/markpublish/cli.py +759 -0
  8. markpublish-1.0.0/src/markpublish/config/loader.py +68 -0
  9. markpublish-1.0.0/src/markpublish/config/models.py +469 -0
  10. markpublish-1.0.0/src/markpublish/data/fonts.conf +68 -0
  11. markpublish-1.0.0/src/markpublish/docs/cheatsheet/de/01_config.md +66 -0
  12. markpublish-1.0.0/src/markpublish/docs/cheatsheet/de/02_templates.md +44 -0
  13. markpublish-1.0.0/src/markpublish/docs/cheatsheet/de/markpublish.yaml +32 -0
  14. markpublish-1.0.0/src/markpublish/docs/cheatsheet/en/01_config.md +66 -0
  15. markpublish-1.0.0/src/markpublish/docs/cheatsheet/en/02_templates.md +43 -0
  16. markpublish-1.0.0/src/markpublish/docs/cheatsheet/en/markpublish.yaml +29 -0
  17. markpublish-1.0.0/src/markpublish/docs/manual/de/chapters/01_introduction.md +24 -0
  18. markpublish-1.0.0/src/markpublish/docs/manual/de/chapters/02_quickstart.md +81 -0
  19. markpublish-1.0.0/src/markpublish/docs/manual/de/chapters/03_cli_reference.md +165 -0
  20. markpublish-1.0.0/src/markpublish/docs/manual/de/chapters/04_configuration_guide.md +95 -0
  21. markpublish-1.0.0/src/markpublish/docs/manual/de/chapters/05_template_system.md +350 -0
  22. markpublish-1.0.0/src/markpublish/docs/manual/de/chapters/06_markdown_features.md +59 -0
  23. markpublish-1.0.0/src/markpublish/docs/manual/de/chapters/07_appendix_yaml_spec.md +209 -0
  24. markpublish-1.0.0/src/markpublish/docs/manual/de/chapters/08_appendix_troubleshooting.md +12 -0
  25. markpublish-1.0.0/src/markpublish/docs/manual/de/markpublish.yaml +77 -0
  26. markpublish-1.0.0/src/markpublish/docs/manual/en/chapters/01_introduction.md +23 -0
  27. markpublish-1.0.0/src/markpublish/docs/manual/en/chapters/02_quickstart.md +74 -0
  28. markpublish-1.0.0/src/markpublish/docs/manual/en/chapters/03_cli_reference.md +135 -0
  29. markpublish-1.0.0/src/markpublish/docs/manual/en/chapters/04_configuration_guide.md +94 -0
  30. markpublish-1.0.0/src/markpublish/docs/manual/en/chapters/05_template_system.md +305 -0
  31. markpublish-1.0.0/src/markpublish/docs/manual/en/chapters/06_markdown_features.md +61 -0
  32. markpublish-1.0.0/src/markpublish/docs/manual/en/chapters/07_appendix_yaml_spec.md +177 -0
  33. markpublish-1.0.0/src/markpublish/docs/manual/en/chapters/08_appendix_troubleshooting.md +12 -0
  34. markpublish-1.0.0/src/markpublish/docs/manual/en/markpublish.yaml +76 -0
  35. markpublish-1.0.0/src/markpublish/i18n.py +518 -0
  36. markpublish-1.0.0/src/markpublish/i18n.yaml +73 -0
  37. markpublish-1.0.0/src/markpublish/markdown/__init__.py +4 -0
  38. markpublish-1.0.0/src/markpublish/markdown/alerts.py +106 -0
  39. markpublish-1.0.0/src/markpublish/markdown/assets.py +93 -0
  40. markpublish-1.0.0/src/markpublish/markdown/engine.py +453 -0
  41. markpublish-1.0.0/src/markpublish/markdown/toc.py +228 -0
  42. markpublish-1.0.0/src/markpublish/renderers/__init__.py +4 -0
  43. markpublish-1.0.0/src/markpublish/renderers/base.py +159 -0
  44. markpublish-1.0.0/src/markpublish/renderers/html.py +25 -0
  45. markpublish-1.0.0/src/markpublish/renderers/pdf.py +139 -0
  46. markpublish-1.0.0/src/markpublish/templates/__init__.py +4 -0
  47. markpublish-1.0.0/src/markpublish/templates/default/html/assets/icons/caution.svg +3 -0
  48. markpublish-1.0.0/src/markpublish/templates/default/html/assets/icons/checkbox-check.svg +3 -0
  49. markpublish-1.0.0/src/markpublish/templates/default/html/assets/icons/important.svg +3 -0
  50. markpublish-1.0.0/src/markpublish/templates/default/html/assets/icons/note.svg +3 -0
  51. markpublish-1.0.0/src/markpublish/templates/default/html/assets/icons/tip.svg +3 -0
  52. markpublish-1.0.0/src/markpublish/templates/default/html/assets/icons/warning.svg +3 -0
  53. markpublish-1.0.0/src/markpublish/templates/default/html/chapter_divider.html +30 -0
  54. markpublish-1.0.0/src/markpublish/templates/default/html/cover.html +33 -0
  55. markpublish-1.0.0/src/markpublish/templates/default/html/fonts/OFL.txt +93 -0
  56. markpublish-1.0.0/src/markpublish/templates/default/html/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  57. markpublish-1.0.0/src/markpublish/templates/default/html/fonts/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  58. markpublish-1.0.0/src/markpublish/templates/default/html/i18n.yaml +23 -0
  59. markpublish-1.0.0/src/markpublish/templates/default/html/layout.html +116 -0
  60. markpublish-1.0.0/src/markpublish/templates/default/html/part_divider.html +10 -0
  61. markpublish-1.0.0/src/markpublish/templates/default/html/styles.css +547 -0
  62. markpublish-1.0.0/src/markpublish/templates/default/html/toc.html +26 -0
  63. markpublish-1.0.0/src/markpublish/templates/default/i18n.yaml +40 -0
  64. markpublish-1.0.0/src/markpublish/templates/default/pdf/assets/icons/caution.svg +4 -0
  65. markpublish-1.0.0/src/markpublish/templates/default/pdf/assets/icons/checkbox-check.svg +3 -0
  66. markpublish-1.0.0/src/markpublish/templates/default/pdf/assets/icons/important.svg +4 -0
  67. markpublish-1.0.0/src/markpublish/templates/default/pdf/assets/icons/note.svg +4 -0
  68. markpublish-1.0.0/src/markpublish/templates/default/pdf/assets/icons/tip.svg +4 -0
  69. markpublish-1.0.0/src/markpublish/templates/default/pdf/assets/icons/warning.svg +4 -0
  70. markpublish-1.0.0/src/markpublish/templates/default/pdf/chapter_divider.html +33 -0
  71. markpublish-1.0.0/src/markpublish/templates/default/pdf/cover.html +51 -0
  72. markpublish-1.0.0/src/markpublish/templates/default/pdf/fonts/OFL.txt +93 -0
  73. markpublish-1.0.0/src/markpublish/templates/default/pdf/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  74. markpublish-1.0.0/src/markpublish/templates/default/pdf/fonts/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  75. markpublish-1.0.0/src/markpublish/templates/default/pdf/i18n.yaml +22 -0
  76. markpublish-1.0.0/src/markpublish/templates/default/pdf/layout.html +108 -0
  77. markpublish-1.0.0/src/markpublish/templates/default/pdf/part_divider.html +31 -0
  78. markpublish-1.0.0/src/markpublish/templates/default/pdf/styles.css +805 -0
  79. markpublish-1.0.0/src/markpublish/templates/default/pdf/toc.html +37 -0
  80. markpublish-1.0.0/src/markpublish/templates/resolver.py +184 -0
  81. markpublish-1.0.0/src/markpublish.egg-info/PKG-INFO +339 -0
  82. markpublish-1.0.0/src/markpublish.egg-info/SOURCES.txt +97 -0
  83. markpublish-1.0.0/src/markpublish.egg-info/dependency_links.txt +1 -0
  84. markpublish-1.0.0/src/markpublish.egg-info/entry_points.txt +3 -0
  85. markpublish-1.0.0/src/markpublish.egg-info/requires.txt +19 -0
  86. markpublish-1.0.0/src/markpublish.egg-info/top_level.txt +1 -0
  87. markpublish-1.0.0/tests/test_cli.py +298 -0
  88. markpublish-1.0.0/tests/test_config.py +166 -0
  89. markpublish-1.0.0/tests/test_fontconfig.py +72 -0
  90. markpublish-1.0.0/tests/test_header_footer.py +392 -0
  91. markpublish-1.0.0/tests/test_i18n.py +292 -0
  92. markpublish-1.0.0/tests/test_i18n_cascade.py +568 -0
  93. markpublish-1.0.0/tests/test_markdown.py +117 -0
  94. markpublish-1.0.0/tests/test_packaging.py +102 -0
  95. markpublish-1.0.0/tests/test_regressions.py +355 -0
  96. markpublish-1.0.0/tests/test_renderers.py +280 -0
  97. markpublish-1.0.0/tests/test_templates.py +67 -0
  98. markpublish-1.0.0/tests/test_theme_layout.py +472 -0
  99. markpublish-1.0.0/tests/test_toc.py +727 -0
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Frank Winter
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.
22
+
@@ -0,0 +1,339 @@
1
+ Metadata-Version: 2.4
2
+ Name: markpublish
3
+ Version: 1.0.0
4
+ Summary: Modern, modular, and extensible Markdown-to-PDF/HTML publishing tool powered by WeasyPrint
5
+ Author-email: Frank Winter <studio@frankwinter.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/fwdotcom/markpublish
8
+ Project-URL: Documentation, https://github.com/fwdotcom/markpublish#readme
9
+ Project-URL: Repository, https://github.com/fwdotcom/markpublish.git
10
+ Project-URL: Issues, https://github.com/fwdotcom/markpublish/issues
11
+ Project-URL: Changelog, https://github.com/fwdotcom/markpublish/blob/main/CHANGELOG.md
12
+ Keywords: markdown,pdf,html,weasyprint,publishing,converter,cli
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Information Technology
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Topic :: Documentation
26
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
27
+ Classifier: Topic :: Printing
28
+ Requires-Python: >=3.10
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ Requires-Dist: weasyprint>=60.0
32
+ Requires-Dist: jinja2>=3.1.0
33
+ Requires-Dist: markdown>=3.5.0
34
+ Requires-Dist: pymdown-extensions>=10.0
35
+ Requires-Dist: pygments>=2.15.0
36
+ Requires-Dist: pydantic>=2.0.0
37
+ Requires-Dist: pyyaml>=6.0.0
38
+ Requires-Dist: typer>=0.9.0
39
+ Requires-Dist: rich>=13.0.0
40
+ Requires-Dist: platformdirs>=3.0.0
41
+ Provides-Extra: dev
42
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
43
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
44
+ Requires-Dist: pypdfium2>=4.0.0; extra == "dev"
45
+ Requires-Dist: ruff>=0.6.0; extra == "dev"
46
+ Requires-Dist: tomli>=2.0.0; python_version < "3.11" and extra == "dev"
47
+ Dynamic: license-file
48
+
49
+ # markpublish
50
+
51
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
52
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
53
+
54
+ **markpublish** is a modern, modular, and extensible publishing tool for Markdown documents. It compiles structured Markdown chapters into print-ready **PDFs** (powered by [WeasyPrint](https://weasyprint.org/) and W3C CSS Paged Media) and standalone **HTML** previews, fully configured via a clean YAML manifest.
55
+
56
+ ---
57
+
58
+ ## Key Features
59
+
60
+ - 📄 **WeasyPrint PDF Engine**: Native support for CSS Paged Media (`@page`, `@top-left`, `@top-right`, `@bottom-left`, `@bottom-right`, page counters, leaders, target counters).
61
+ - 🌐 **Multi-Format Pipeline**: First-class support for **PDF** and **HTML** (extensible architecture for EPUB, DOCX).
62
+ - ⚙️ **Declarative YAML Configuration**: Configure metadata, covers, TOCs, headers, and footers in `markpublish.yaml`.
63
+ - 📁 **Modular Chapters & Parts**: Split your content into separate `.md` files, organize them hierarchically, and group them under overarching **Parts** (e.g. *Appendices*).
64
+ - 🎨 **Target-Based Template Hierarchy**:
65
+ - `templates/<theme>/pdf` and `templates/<theme>/html`
66
+ - 3-tier resolution: **User directory** > **Common/Project folder** > **Package built-ins**.
67
+ - 🔤 **Bundled font**: Open Sans ships with the theme as a variable font and is embedded into the PDF, so output does not depend on what is installed on the build machine.
68
+ - 📑 **Running Headers & Footers**: Real markup in the page margins via CSS running elements -- any number of lines, per-line styling, columns top-aligned.
69
+ - 🔢 **Autonumbering & TOC**:
70
+ - Global TOC reflecting chapter depth and heading levels.
71
+ - Per-chapter local TOCs with configurable `max_depth` (rendered on the PDF divider page).
72
+ - Configurable autonumbering schemes (`decimal`, `roman`, `legal`, `none`).
73
+ - 💻 **Cross-Platform**: Works seamlessly on Linux, macOS, and Windows.
74
+
75
+ ---
76
+
77
+ ## Showcase / Sample Output
78
+
79
+ The entire official user guide was written in Markdown and rendered directly with `markpublish` — two languages, both target formats, all four built by `python build_manuals.py`:
80
+
81
+ | Document | PDF | HTML |
82
+ | :--- | :--- | :--- |
83
+ | 🇩🇪 markpublish Benutzerhandbuch | **[view](https://github.com/fwdotcom/markpublish/blob/main/manual/markpublish_benutzerhandbuch.pdf)** | [download](https://raw.githubusercontent.com/fwdotcom/markpublish/main/manual/markpublish_benutzerhandbuch.html) |
84
+ | 🇬🇧 markpublish User Guide | **[view](https://github.com/fwdotcom/markpublish/blob/main/manual/markpublish_user_guide.pdf)** | [download](https://raw.githubusercontent.com/fwdotcom/markpublish/main/manual/markpublish_user_guide.html) |
85
+
86
+ The PDFs open straight in the browser. The HTML files are **single, self-contained
87
+ documents** — fonts and images are embedded as data URIs, so one file is the whole
88
+ publication, with nothing to unpack and no assets folder beside it. GitHub serves
89
+ them as plain text rather than rendering them, so save the file and open it locally.
90
+
91
+ Together they demonstrate the built-in capabilities in practice: cover layout,
92
+ multi-level global TOC, running multi-line headers and footers, GitHub callout
93
+ admonitions, Pygments syntax highlighting, task lists, and the part/chapter
94
+ hierarchy — and in the HTML case a sidebar TOC and a responsive layout instead of
95
+ the print furniture.
96
+
97
+ ---
98
+
99
+ ## Installation
100
+
101
+ ```bash
102
+ pip install markpublish
103
+ ```
104
+
105
+ For development:
106
+ ```bash
107
+ git clone https://github.com/fwdotcom/markpublish.git
108
+ cd markpublish
109
+ pip install -e ".[dev]"
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Quickstart
115
+
116
+ ### 1. Initialize a new project
117
+ ```bash
118
+ markpublish init my-book --title "My Architecture Guide"
119
+ cd my-book
120
+ ```
121
+
122
+ This creates a minimal stub: `markpublish.yaml` plus one chapter, both directly
123
+ in the directory. It is meant to be replaced by your own content.
124
+
125
+ ### 2. Look things up
126
+ ```bash
127
+ markpublish cheatsheet [--lang de|en] # two-page reference card
128
+ markpublish manual [--lang de|en] # the full user guide
129
+ ```
130
+
131
+ `cheatsheet` renders a two-page reference -- every key of `markpublish.yaml` on
132
+ page one, themes and templates on page two. `manual` renders the complete user
133
+ guide. Both ship in German and English, their sources inside the package, and
134
+ are rendered on demand -- so they describe the version you actually have rather
135
+ than whatever was current when someone last rebuilt a PDF, and a successful run
136
+ also confirms that the rendering toolchain works.
137
+
138
+ `--lang` selects the source, not just the labels: each translation carries its
139
+ own `language:` and pulls in the matching static texts by itself. Without it,
140
+ your system language decides; where no translation exists, English appears.
141
+
142
+ ### 3. Build PDF and HTML
143
+ ```bash
144
+ # Build PDF
145
+ markpublish build
146
+
147
+ # Build standalone HTML
148
+ markpublish build --target html
149
+
150
+ # Build both PDF and HTML
151
+ markpublish build --target all
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Configuration Reference (`markpublish.yaml`)
157
+
158
+ ```yaml
159
+ # Document Metadata & Layout
160
+ document:
161
+ title: "Cloud Architecture Guide"
162
+ subtitle: "Best Practices & Standards"
163
+ summary: "Comprehensive guide for modern enterprise cloud environments."
164
+ author: "Frank Mustermann"
165
+ date: "auto" # "auto" for current date, or "2026-08-31"
166
+ version: "1.0.0" # optional; omitted -> no version on the cover
167
+ language: "de" # Localization and hyphenation; omit it and
168
+ # your system language applies
169
+
170
+ # Layout Toggles
171
+ cover: true # Enable cover page
172
+ document_toc: 2 # Large TOC, two levels deep
173
+ autonum_style: "decimal" # "decimal" (1, 1.1), "roman", "legal", "none"
174
+ chapter_toc: 2 # Default for the chapters' divider-page TOC
175
+ header: true # Enable running header (laid out in the theme)
176
+ footer: true # Enable running footer (laid out in the theme)
177
+
178
+ # Template selection
179
+ theme: "default"
180
+ templates_dir: "./templates" # Optional: custom shared templates directory
181
+
182
+ # Parts and Chapters (hierarchical structure)
183
+ parts:
184
+ # Main part (no part divider page, chapters listed directly in TOC)
185
+ - title: "Main"
186
+ break_before: "none"
187
+ document_toc: "none"
188
+ chapters:
189
+ - file: "chapters/01_introduction.md"
190
+ title: "Introduction"
191
+ summary: "Scope and motivation."
192
+ break_before: "divider" # Dedicated divider/separator page
193
+ chapter_toc: "none"
194
+
195
+ - file: "chapters/02_architecture.md"
196
+ title: "Core Architecture"
197
+ summary: "System components and flows."
198
+ break_before: "divider"
199
+ chapter_toc: 2 # Local chapter TOC up to depth 2
200
+
201
+ # Overarching Part / Section (e.g. Appendices)
202
+ - title: "Appendices"
203
+ summary: "Glossary and reference tables."
204
+ break_before: "divider" # Dedicated Part separator page
205
+ autonum_from_level: 2 # Number sub-headings (A.1, A.2)
206
+ document_toc: 2 # Include down to depth 2 in front TOC
207
+ chapters:
208
+ - file: "chapters/appendix_a.md"
209
+ title: "Appendix A: Reference"
210
+ autonum_prefix: "A."
211
+ - file: "chapters/appendix_b.md"
212
+ title: "Appendix B: Troubleshooting"
213
+ autonum_prefix: "B."
214
+ ```
215
+
216
+ For simple documents (e.g. leaflets or short reports), the `parts:` layer is optional; you can also write `chapters: [...]` directly at the top level.
217
+
218
+ `break_before` decides how far a chapter is set off from the one before it, on a
219
+ single axis: `page` (the default) starts it at the top of a fresh page,
220
+ `divider` gives it a separator page of its own, and `none` lets it run on.
221
+
222
+ A document has two tables of contents and one key each, written the same way in
223
+ the `document` block and on a chapter: `document_toc` for the large one at the
224
+ front, `chapter_toc` for the small ones on the divider pages. Under `document`
225
+ they set the default, on a chapter or part they override it. Both take `none`, `full` or
226
+ a depth counted from the chapter's own heading, so `document_toc: 1` contributes
227
+ the chapter title and nothing below it.
228
+
229
+ `document_toc`, `autonum_style`, `autonum_from_level`, and `autonum_prefix` are inherited downwards from a part, which is why one line on the `Appendices` part configures every appendix at once. `chapter_toc` is not chained; it falls back to `document.chapter_toc`.
230
+
231
+ ---
232
+
233
+ ## Template System
234
+
235
+ Templates are organized by theme first, then by target format:
236
+ ```
237
+ templates/
238
+ └── default/
239
+ ├── pdf/
240
+ │ ├── layout.html # Jinja2 layout
241
+ │ ├── styles.css # CSS Paged Media (@page, @top-left, @bottom-right)
242
+ │ ├── cover.html # Cover page
243
+ │ ├── part_divider.html # Part separator page
244
+ │ ├── chapter_divider.html # Chapter separator page & local TOC (PDF only)
245
+ │ └── toc.html # Global TOC
246
+ └── html/
247
+ └── ...
248
+ ```
249
+
250
+ ### Template Resolution Order:
251
+ When rendering `pdf` with theme `default`:
252
+ 1. **User directory**: `~/.markpublish/templates/default/pdf/` (or OS config dir)
253
+ 2. **Common / Project directory**: `<templates_dir>/default/pdf/` (configured via `--templates-dir`, YAML `templates_dir`, `MARKPUBLISH_TEMPLATES_DIR`, or `./templates`)
254
+ 3. **Package built-ins**: Embedded inside `markpublish`.
255
+
256
+
257
+ ### Static texts and language
258
+
259
+ Fixed labels (table of contents heading, chapter tags, cover labels, page footer,
260
+ callout titles) live in `i18n.yaml` files rather than in the templates. The language
261
+ comes from `document.language`; `de` and `en` ship with the package, regional forms map
262
+ onto them (`de-AT` -> `de`), and an unknown language falls back to English. Leave the key
263
+ out and the system language applies -- `markpublish init` writes it down, so a document
264
+ builds the same on every machine.
265
+
266
+ *i18n* is the source data across all languages; *labels* is what it resolves to for one
267
+ document in one language, i.e. what a template sees as `{{ labels.chapter }}`.
268
+
269
+ Three levels, all built the same way -- language code, then key/text. Each level
270
+ overrides the one above it, and only for the keys it actually sets:
271
+
272
+ | Level | Location |
273
+ | :--- | :--- |
274
+ | 1 | `markpublish/i18n.yaml` (complete, `de` + `en`) |
275
+ | 2 | `<templates>/<theme>/i18n.yaml` |
276
+ | 3 | `<templates>/<theme>/<target>/i18n.yaml` |
277
+
278
+ Levels 2 and 3 always come from the **one** theme the template resolution picked
279
+ (user > project > package); a project theme does not inherit the texts of the
280
+ package theme of the same name. A document cannot override texts -- give it its
281
+ own theme (`markpublish export-template`) instead.
282
+
283
+ ```yaml
284
+ # any of the three levels
285
+ "*": # applies to every language
286
+ version: "Rev."
287
+ de:
288
+ part: "Abschnitt"
289
+ en:
290
+ part: "Section"
291
+ ```
292
+
293
+ A flat mapping without the language level is shorthand for `"*"`. Levels 2 and 3 apply
294
+ **only to the selected language**, so a theme's English block never leaks into German
295
+ output. Level 1 additionally layers English underneath the document language, so every
296
+ program text always resolves. A missing `i18n.yaml` is fine; a malformed one aborts the
297
+ build naming the file.
298
+
299
+ #### Free labels
300
+
301
+ A theme may define keys the program knows nothing about -- for the static texts of the
302
+ template itself -- and read them back with `{{ labels.imprint_title }}`. There is no
303
+ program default to fall back on for those, so the rule is strict: a label written in a
304
+ template must resolve in the cascade, otherwise the build **aborts** and names the key,
305
+ the file and line that used it, the document language, and the `i18n.yaml` files that
306
+ were searched. Keep every language of a free label filled in, or put it under `"*"`.
307
+ An empty string in a finished PDF goes unnoticed; an abort does not.
308
+
309
+ The bundled `default` theme ships all three template-side files as commented patterns,
310
+ and `markpublish export-template` copies them along with the templates.
311
+
312
+ Run `markpublish labels [--target html] [--overridden]` to see the resolved table and
313
+ which level supplied each value.
314
+
315
+ ### Exporting Templates for Customization:
316
+ ```bash
317
+ markpublish export-template default
318
+ ```
319
+
320
+ ---
321
+
322
+ ## CLI Commands
323
+
324
+ | Command | Description |
325
+ | :--- | :--- |
326
+ | `markpublish build [config.yaml]` | Builds PDF/HTML outputs (`-t pdf`, `-t html`, `-t all`) |
327
+ | `markpublish init [path]` | Scaffolds a new project with chapters and configuration |
328
+ | `markpublish cheatsheet` | Renders the two-page reference card (`--lang`, `--target`) |
329
+ | `markpublish manual` | Renders the full user guide (`--lang`, `--target`) |
330
+ | `markpublish templates` | Lists available templates across User, Common, and Package sources |
331
+ | `markpublish export-template [theme]` | Copies a built-in template to project directory |
332
+ | `markpublish labels` | Shows the resolved static texts and their cascade origin |
333
+
334
+ ---
335
+
336
+ ## License
337
+
338
+ MIT License. See [LICENSE](https://github.com/fwdotcom/markpublish/blob/main/LICENSE) for details.
339
+
@@ -0,0 +1,291 @@
1
+ # markpublish
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
4
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
5
+
6
+ **markpublish** is a modern, modular, and extensible publishing tool for Markdown documents. It compiles structured Markdown chapters into print-ready **PDFs** (powered by [WeasyPrint](https://weasyprint.org/) and W3C CSS Paged Media) and standalone **HTML** previews, fully configured via a clean YAML manifest.
7
+
8
+ ---
9
+
10
+ ## Key Features
11
+
12
+ - 📄 **WeasyPrint PDF Engine**: Native support for CSS Paged Media (`@page`, `@top-left`, `@top-right`, `@bottom-left`, `@bottom-right`, page counters, leaders, target counters).
13
+ - 🌐 **Multi-Format Pipeline**: First-class support for **PDF** and **HTML** (extensible architecture for EPUB, DOCX).
14
+ - ⚙️ **Declarative YAML Configuration**: Configure metadata, covers, TOCs, headers, and footers in `markpublish.yaml`.
15
+ - 📁 **Modular Chapters & Parts**: Split your content into separate `.md` files, organize them hierarchically, and group them under overarching **Parts** (e.g. *Appendices*).
16
+ - 🎨 **Target-Based Template Hierarchy**:
17
+ - `templates/<theme>/pdf` and `templates/<theme>/html`
18
+ - 3-tier resolution: **User directory** > **Common/Project folder** > **Package built-ins**.
19
+ - 🔤 **Bundled font**: Open Sans ships with the theme as a variable font and is embedded into the PDF, so output does not depend on what is installed on the build machine.
20
+ - 📑 **Running Headers & Footers**: Real markup in the page margins via CSS running elements -- any number of lines, per-line styling, columns top-aligned.
21
+ - 🔢 **Autonumbering & TOC**:
22
+ - Global TOC reflecting chapter depth and heading levels.
23
+ - Per-chapter local TOCs with configurable `max_depth` (rendered on the PDF divider page).
24
+ - Configurable autonumbering schemes (`decimal`, `roman`, `legal`, `none`).
25
+ - 💻 **Cross-Platform**: Works seamlessly on Linux, macOS, and Windows.
26
+
27
+ ---
28
+
29
+ ## Showcase / Sample Output
30
+
31
+ The entire official user guide was written in Markdown and rendered directly with `markpublish` — two languages, both target formats, all four built by `python build_manuals.py`:
32
+
33
+ | Document | PDF | HTML |
34
+ | :--- | :--- | :--- |
35
+ | 🇩🇪 markpublish Benutzerhandbuch | **[view](https://github.com/fwdotcom/markpublish/blob/main/manual/markpublish_benutzerhandbuch.pdf)** | [download](https://raw.githubusercontent.com/fwdotcom/markpublish/main/manual/markpublish_benutzerhandbuch.html) |
36
+ | 🇬🇧 markpublish User Guide | **[view](https://github.com/fwdotcom/markpublish/blob/main/manual/markpublish_user_guide.pdf)** | [download](https://raw.githubusercontent.com/fwdotcom/markpublish/main/manual/markpublish_user_guide.html) |
37
+
38
+ The PDFs open straight in the browser. The HTML files are **single, self-contained
39
+ documents** — fonts and images are embedded as data URIs, so one file is the whole
40
+ publication, with nothing to unpack and no assets folder beside it. GitHub serves
41
+ them as plain text rather than rendering them, so save the file and open it locally.
42
+
43
+ Together they demonstrate the built-in capabilities in practice: cover layout,
44
+ multi-level global TOC, running multi-line headers and footers, GitHub callout
45
+ admonitions, Pygments syntax highlighting, task lists, and the part/chapter
46
+ hierarchy — and in the HTML case a sidebar TOC and a responsive layout instead of
47
+ the print furniture.
48
+
49
+ ---
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ pip install markpublish
55
+ ```
56
+
57
+ For development:
58
+ ```bash
59
+ git clone https://github.com/fwdotcom/markpublish.git
60
+ cd markpublish
61
+ pip install -e ".[dev]"
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Quickstart
67
+
68
+ ### 1. Initialize a new project
69
+ ```bash
70
+ markpublish init my-book --title "My Architecture Guide"
71
+ cd my-book
72
+ ```
73
+
74
+ This creates a minimal stub: `markpublish.yaml` plus one chapter, both directly
75
+ in the directory. It is meant to be replaced by your own content.
76
+
77
+ ### 2. Look things up
78
+ ```bash
79
+ markpublish cheatsheet [--lang de|en] # two-page reference card
80
+ markpublish manual [--lang de|en] # the full user guide
81
+ ```
82
+
83
+ `cheatsheet` renders a two-page reference -- every key of `markpublish.yaml` on
84
+ page one, themes and templates on page two. `manual` renders the complete user
85
+ guide. Both ship in German and English, their sources inside the package, and
86
+ are rendered on demand -- so they describe the version you actually have rather
87
+ than whatever was current when someone last rebuilt a PDF, and a successful run
88
+ also confirms that the rendering toolchain works.
89
+
90
+ `--lang` selects the source, not just the labels: each translation carries its
91
+ own `language:` and pulls in the matching static texts by itself. Without it,
92
+ your system language decides; where no translation exists, English appears.
93
+
94
+ ### 3. Build PDF and HTML
95
+ ```bash
96
+ # Build PDF
97
+ markpublish build
98
+
99
+ # Build standalone HTML
100
+ markpublish build --target html
101
+
102
+ # Build both PDF and HTML
103
+ markpublish build --target all
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Configuration Reference (`markpublish.yaml`)
109
+
110
+ ```yaml
111
+ # Document Metadata & Layout
112
+ document:
113
+ title: "Cloud Architecture Guide"
114
+ subtitle: "Best Practices & Standards"
115
+ summary: "Comprehensive guide for modern enterprise cloud environments."
116
+ author: "Frank Mustermann"
117
+ date: "auto" # "auto" for current date, or "2026-08-31"
118
+ version: "1.0.0" # optional; omitted -> no version on the cover
119
+ language: "de" # Localization and hyphenation; omit it and
120
+ # your system language applies
121
+
122
+ # Layout Toggles
123
+ cover: true # Enable cover page
124
+ document_toc: 2 # Large TOC, two levels deep
125
+ autonum_style: "decimal" # "decimal" (1, 1.1), "roman", "legal", "none"
126
+ chapter_toc: 2 # Default for the chapters' divider-page TOC
127
+ header: true # Enable running header (laid out in the theme)
128
+ footer: true # Enable running footer (laid out in the theme)
129
+
130
+ # Template selection
131
+ theme: "default"
132
+ templates_dir: "./templates" # Optional: custom shared templates directory
133
+
134
+ # Parts and Chapters (hierarchical structure)
135
+ parts:
136
+ # Main part (no part divider page, chapters listed directly in TOC)
137
+ - title: "Main"
138
+ break_before: "none"
139
+ document_toc: "none"
140
+ chapters:
141
+ - file: "chapters/01_introduction.md"
142
+ title: "Introduction"
143
+ summary: "Scope and motivation."
144
+ break_before: "divider" # Dedicated divider/separator page
145
+ chapter_toc: "none"
146
+
147
+ - file: "chapters/02_architecture.md"
148
+ title: "Core Architecture"
149
+ summary: "System components and flows."
150
+ break_before: "divider"
151
+ chapter_toc: 2 # Local chapter TOC up to depth 2
152
+
153
+ # Overarching Part / Section (e.g. Appendices)
154
+ - title: "Appendices"
155
+ summary: "Glossary and reference tables."
156
+ break_before: "divider" # Dedicated Part separator page
157
+ autonum_from_level: 2 # Number sub-headings (A.1, A.2)
158
+ document_toc: 2 # Include down to depth 2 in front TOC
159
+ chapters:
160
+ - file: "chapters/appendix_a.md"
161
+ title: "Appendix A: Reference"
162
+ autonum_prefix: "A."
163
+ - file: "chapters/appendix_b.md"
164
+ title: "Appendix B: Troubleshooting"
165
+ autonum_prefix: "B."
166
+ ```
167
+
168
+ For simple documents (e.g. leaflets or short reports), the `parts:` layer is optional; you can also write `chapters: [...]` directly at the top level.
169
+
170
+ `break_before` decides how far a chapter is set off from the one before it, on a
171
+ single axis: `page` (the default) starts it at the top of a fresh page,
172
+ `divider` gives it a separator page of its own, and `none` lets it run on.
173
+
174
+ A document has two tables of contents and one key each, written the same way in
175
+ the `document` block and on a chapter: `document_toc` for the large one at the
176
+ front, `chapter_toc` for the small ones on the divider pages. Under `document`
177
+ they set the default, on a chapter or part they override it. Both take `none`, `full` or
178
+ a depth counted from the chapter's own heading, so `document_toc: 1` contributes
179
+ the chapter title and nothing below it.
180
+
181
+ `document_toc`, `autonum_style`, `autonum_from_level`, and `autonum_prefix` are inherited downwards from a part, which is why one line on the `Appendices` part configures every appendix at once. `chapter_toc` is not chained; it falls back to `document.chapter_toc`.
182
+
183
+ ---
184
+
185
+ ## Template System
186
+
187
+ Templates are organized by theme first, then by target format:
188
+ ```
189
+ templates/
190
+ └── default/
191
+ ├── pdf/
192
+ │ ├── layout.html # Jinja2 layout
193
+ │ ├── styles.css # CSS Paged Media (@page, @top-left, @bottom-right)
194
+ │ ├── cover.html # Cover page
195
+ │ ├── part_divider.html # Part separator page
196
+ │ ├── chapter_divider.html # Chapter separator page & local TOC (PDF only)
197
+ │ └── toc.html # Global TOC
198
+ └── html/
199
+ └── ...
200
+ ```
201
+
202
+ ### Template Resolution Order:
203
+ When rendering `pdf` with theme `default`:
204
+ 1. **User directory**: `~/.markpublish/templates/default/pdf/` (or OS config dir)
205
+ 2. **Common / Project directory**: `<templates_dir>/default/pdf/` (configured via `--templates-dir`, YAML `templates_dir`, `MARKPUBLISH_TEMPLATES_DIR`, or `./templates`)
206
+ 3. **Package built-ins**: Embedded inside `markpublish`.
207
+
208
+
209
+ ### Static texts and language
210
+
211
+ Fixed labels (table of contents heading, chapter tags, cover labels, page footer,
212
+ callout titles) live in `i18n.yaml` files rather than in the templates. The language
213
+ comes from `document.language`; `de` and `en` ship with the package, regional forms map
214
+ onto them (`de-AT` -> `de`), and an unknown language falls back to English. Leave the key
215
+ out and the system language applies -- `markpublish init` writes it down, so a document
216
+ builds the same on every machine.
217
+
218
+ *i18n* is the source data across all languages; *labels* is what it resolves to for one
219
+ document in one language, i.e. what a template sees as `{{ labels.chapter }}`.
220
+
221
+ Three levels, all built the same way -- language code, then key/text. Each level
222
+ overrides the one above it, and only for the keys it actually sets:
223
+
224
+ | Level | Location |
225
+ | :--- | :--- |
226
+ | 1 | `markpublish/i18n.yaml` (complete, `de` + `en`) |
227
+ | 2 | `<templates>/<theme>/i18n.yaml` |
228
+ | 3 | `<templates>/<theme>/<target>/i18n.yaml` |
229
+
230
+ Levels 2 and 3 always come from the **one** theme the template resolution picked
231
+ (user > project > package); a project theme does not inherit the texts of the
232
+ package theme of the same name. A document cannot override texts -- give it its
233
+ own theme (`markpublish export-template`) instead.
234
+
235
+ ```yaml
236
+ # any of the three levels
237
+ "*": # applies to every language
238
+ version: "Rev."
239
+ de:
240
+ part: "Abschnitt"
241
+ en:
242
+ part: "Section"
243
+ ```
244
+
245
+ A flat mapping without the language level is shorthand for `"*"`. Levels 2 and 3 apply
246
+ **only to the selected language**, so a theme's English block never leaks into German
247
+ output. Level 1 additionally layers English underneath the document language, so every
248
+ program text always resolves. A missing `i18n.yaml` is fine; a malformed one aborts the
249
+ build naming the file.
250
+
251
+ #### Free labels
252
+
253
+ A theme may define keys the program knows nothing about -- for the static texts of the
254
+ template itself -- and read them back with `{{ labels.imprint_title }}`. There is no
255
+ program default to fall back on for those, so the rule is strict: a label written in a
256
+ template must resolve in the cascade, otherwise the build **aborts** and names the key,
257
+ the file and line that used it, the document language, and the `i18n.yaml` files that
258
+ were searched. Keep every language of a free label filled in, or put it under `"*"`.
259
+ An empty string in a finished PDF goes unnoticed; an abort does not.
260
+
261
+ The bundled `default` theme ships all three template-side files as commented patterns,
262
+ and `markpublish export-template` copies them along with the templates.
263
+
264
+ Run `markpublish labels [--target html] [--overridden]` to see the resolved table and
265
+ which level supplied each value.
266
+
267
+ ### Exporting Templates for Customization:
268
+ ```bash
269
+ markpublish export-template default
270
+ ```
271
+
272
+ ---
273
+
274
+ ## CLI Commands
275
+
276
+ | Command | Description |
277
+ | :--- | :--- |
278
+ | `markpublish build [config.yaml]` | Builds PDF/HTML outputs (`-t pdf`, `-t html`, `-t all`) |
279
+ | `markpublish init [path]` | Scaffolds a new project with chapters and configuration |
280
+ | `markpublish cheatsheet` | Renders the two-page reference card (`--lang`, `--target`) |
281
+ | `markpublish manual` | Renders the full user guide (`--lang`, `--target`) |
282
+ | `markpublish templates` | Lists available templates across User, Common, and Package sources |
283
+ | `markpublish export-template [theme]` | Copies a built-in template to project directory |
284
+ | `markpublish labels` | Shows the resolved static texts and their cascade origin |
285
+
286
+ ---
287
+
288
+ ## License
289
+
290
+ MIT License. See [LICENSE](https://github.com/fwdotcom/markpublish/blob/main/LICENSE) for details.
291
+