typoprint 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. typoprint-0.1.0/.github/workflows/check.yml +50 -0
  2. typoprint-0.1.0/.github/workflows/docs.yml +45 -0
  3. typoprint-0.1.0/.github/workflows/release.yml +69 -0
  4. typoprint-0.1.0/AGENTS.md +13 -0
  5. typoprint-0.1.0/CHANGELOG.md +13 -0
  6. typoprint-0.1.0/CODE_OF_CONDUCT.md +15 -0
  7. typoprint-0.1.0/CONTRIBUTING.md +41 -0
  8. typoprint-0.1.0/LICENSE +21 -0
  9. typoprint-0.1.0/MANIFEST.in +7 -0
  10. typoprint-0.1.0/PKG-INFO +263 -0
  11. typoprint-0.1.0/README.md +233 -0
  12. typoprint-0.1.0/SECURITY.md +20 -0
  13. typoprint-0.1.0/THIRD_PARTY_NOTICES.md +23 -0
  14. typoprint-0.1.0/docs/README.md +33 -0
  15. typoprint-0.1.0/docs/cli.md +275 -0
  16. typoprint-0.1.0/docs/extensions.md +394 -0
  17. typoprint-0.1.0/docs/install.md +55 -0
  18. typoprint-0.1.0/docs/python.md +286 -0
  19. typoprint-0.1.0/docs/releasing.md +42 -0
  20. typoprint-0.1.0/docs/verification.md +62 -0
  21. typoprint-0.1.0/examples/api.py +60 -0
  22. typoprint-0.1.0/examples/template/template.typ +9 -0
  23. typoprint-0.1.0/examples/templates/branded/brand.typ +4 -0
  24. typoprint-0.1.0/examples/templates/branded/logo.svg +4 -0
  25. typoprint-0.1.0/examples/templates/branded/template.typ +27 -0
  26. typoprint-0.1.0/examples/templates/confidential/template.typ +14 -0
  27. typoprint-0.1.0/examples/templates/memo/template.typ +35 -0
  28. typoprint-0.1.0/examples/worker.py +36 -0
  29. typoprint-0.1.0/mkdocs.yml +33 -0
  30. typoprint-0.1.0/pyproject.toml +59 -0
  31. typoprint-0.1.0/setup.cfg +4 -0
  32. typoprint-0.1.0/src/typoprint/__init__.py +40 -0
  33. typoprint-0.1.0/src/typoprint/__main__.py +3 -0
  34. typoprint-0.1.0/src/typoprint/_ordering.py +215 -0
  35. typoprint-0.1.0/src/typoprint/cli.py +242 -0
  36. typoprint-0.1.0/src/typoprint/config.py +45 -0
  37. typoprint-0.1.0/src/typoprint/models.py +139 -0
  38. typoprint-0.1.0/src/typoprint/output.py +28 -0
  39. typoprint-0.1.0/src/typoprint/planning.py +99 -0
  40. typoprint-0.1.0/src/typoprint/py.typed +0 -0
  41. typoprint-0.1.0/src/typoprint/rendering.py +358 -0
  42. typoprint-0.1.0/src/typoprint/resources/fonts/source-sans-3/NOTICE.txt +12 -0
  43. typoprint-0.1.0/src/typoprint/resources/fonts/source-sans-3/OFL.txt +93 -0
  44. typoprint-0.1.0/src/typoprint/resources/fonts/source-sans-3/SourceSans3-Bold.otf +0 -0
  45. typoprint-0.1.0/src/typoprint/resources/fonts/source-sans-3/SourceSans3-BoldIt.otf +0 -0
  46. typoprint-0.1.0/src/typoprint/resources/fonts/source-sans-3/SourceSans3-It.otf +0 -0
  47. typoprint-0.1.0/src/typoprint/resources/fonts/source-sans-3/SourceSans3-Regular.otf +0 -0
  48. typoprint-0.1.0/src/typoprint/resources/packages/local/typoprint/0.1.0/diagrams.typ +37 -0
  49. typoprint-0.1.0/src/typoprint/resources/packages/local/typoprint/0.1.0/editorial.typ +156 -0
  50. typoprint-0.1.0/src/typoprint/resources/packages/local/typoprint/0.1.0/foundations.typ +73 -0
  51. typoprint-0.1.0/src/typoprint/resources/packages/local/typoprint/0.1.0/lib.typ +225 -0
  52. typoprint-0.1.0/src/typoprint/resources/packages/local/typoprint/0.1.0/report.typ +130 -0
  53. typoprint-0.1.0/src/typoprint/resources/packages/local/typoprint/0.1.0/typst.toml +7 -0
  54. typoprint-0.1.0/src/typoprint/resources/packages/preview/cmarker/0.1.10/LICENSE +7 -0
  55. typoprint-0.1.0/src/typoprint/resources/packages/preview/cmarker/0.1.10/lib.typ +462 -0
  56. typoprint-0.1.0/src/typoprint/resources/packages/preview/cmarker/0.1.10/plugin.wasm +0 -0
  57. typoprint-0.1.0/src/typoprint/resources/packages/preview/cmarker/0.1.10/typst.toml +11 -0
  58. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/LICENSE +231 -0
  59. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/cose-base-v1/LICENSE +21 -0
  60. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/cose-base-v2/LICENSE +21 -0
  61. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/cytoscape/LICENSE +19 -0
  62. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/cytoscape-cose-bilkent/LICENSE +21 -0
  63. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/cytoscape-fcose/LICENSE +19 -0
  64. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/d3-shape/LICENSE +13 -0
  65. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/dagre/LICENSE +19 -0
  66. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/dompurify/LICENSE +202 -0
  67. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/eclipse-elk/LICENSE.md +264 -0
  68. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/elkjs/LICENSE.md +264 -0
  69. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/fmin/LICENSE +27 -0
  70. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/graphlib/LICENSE +19 -0
  71. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/layout-base-v1/LICENSE +21 -0
  72. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/layout-base-v2/LICENSE +21 -0
  73. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/mermaid/LICENSE +21 -0
  74. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/non-layered-tidy-tree-layout/LICENSE +21 -0
  75. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/rough-rs/LICENSE +21 -0
  76. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/roughjs/LICENSE +21 -0
  77. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/sanitize-url/LICENSE +21 -0
  78. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/unicode-linebreak/LICENSE +201 -0
  79. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/venn-js/LICENSE +22 -0
  80. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/wasm-minimal-protocol/LICENSE +24 -0
  81. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_LICENSES/zenuml-core/LICENSE +21 -0
  82. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/THIRD_PARTY_NOTICES.md +289 -0
  83. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/lib.typ +1 -0
  84. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/merman_typst_plugin.wasm +0 -0
  85. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/capabilities.typ +5 -0
  86. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/context.typ +1 -0
  87. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/errors.typ +33 -0
  88. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/exports.typ +5 -0
  89. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/figure.typ +143 -0
  90. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/image.typ +50 -0
  91. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/options.typ +566 -0
  92. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/plugin.typ +1 -0
  93. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/raw.typ +22 -0
  94. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/render.typ +137 -0
  95. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/source.typ +7 -0
  96. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/src/units.typ +51 -0
  97. typoprint-0.1.0/src/typoprint/resources/packages/preview/merman/0.5.0/typst.toml +12 -0
  98. typoprint-0.1.0/src/typoprint.egg-info/PKG-INFO +263 -0
  99. typoprint-0.1.0/src/typoprint.egg-info/SOURCES.txt +106 -0
  100. typoprint-0.1.0/src/typoprint.egg-info/dependency_links.txt +1 -0
  101. typoprint-0.1.0/src/typoprint.egg-info/entry_points.txt +2 -0
  102. typoprint-0.1.0/src/typoprint.egg-info/requires.txt +2 -0
  103. typoprint-0.1.0/src/typoprint.egg-info/top_level.txt +1 -0
  104. typoprint-0.1.0/tests/specimen.py +78 -0
  105. typoprint-0.1.0/tests/test_examples.py +107 -0
  106. typoprint-0.1.0/tests/test_typoprint.py +582 -0
  107. typoprint-0.1.0/tools/wheel_smoke.py +97 -0
  108. typoprint-0.1.0/uv.lock +902 -0
@@ -0,0 +1,50 @@
1
+ name: Check and package
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ check:
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ os: [ubuntu-latest, windows-latest, macos-latest]
17
+ python: ["3.13", "3.14"]
18
+ runs-on: ${{ matrix.os }}
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: astral-sh/setup-uv@v7
22
+ with:
23
+ python-version: ${{ matrix.python }}
24
+ enable-cache: true
25
+ - run: uv sync --locked
26
+ - run: uv run --no-sync ruff format --check .
27
+ - run: uv run --no-sync ruff check .
28
+ - run: uv run --no-sync ty check
29
+ - run: uv run --no-sync pytest
30
+ - run: uv build
31
+ - name: Install and inspect a clean wheel
32
+ shell: bash
33
+ run: |
34
+ uv venv .cache/wheel-smoke
35
+ if [ "$RUNNER_OS" = "Windows" ]; then
36
+ python_path=".cache/wheel-smoke/Scripts/python.exe"
37
+ else
38
+ python_path=".cache/wheel-smoke/bin/python"
39
+ fi
40
+ uv run --no-project --python "$python_path" --with pip python -m pip download --dest .cache/wheelhouse dist/*.whl
41
+ uv pip install --offline --no-index --find-links .cache/wheelhouse --python "$python_path" dist/*.whl
42
+ "$python_path" tools/wheel_smoke.py
43
+ - name: Verify Linux rendering with network disabled
44
+ if: matrix.os == 'ubuntu-latest' && matrix.python == '3.13'
45
+ shell: bash
46
+ run: |
47
+ docker run --rm -v "$PWD:/work" python:3.13-slim \
48
+ sh -c 'python -m pip download --dest /work/.cache/linux-wheelhouse /work/dist/*.whl'
49
+ docker run --rm --network none -v "$PWD:/work:ro" python:3.13-slim sh -c \
50
+ 'python -m venv /tmp/smoke && /tmp/smoke/bin/pip install --no-index --find-links /work/.cache/linux-wheelhouse /work/dist/*.whl && cd /tmp && /tmp/smoke/bin/python /work/tools/wheel_smoke.py'
@@ -0,0 +1,45 @@
1
+ name: Documentation
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths: ["docs/**", "mkdocs.yml", "pyproject.toml", "uv.lock", ".github/workflows/docs.yml"]
7
+ pull_request:
8
+ paths: ["docs/**", "mkdocs.yml", ".github/workflows/docs.yml"]
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ concurrency:
15
+ group: pages
16
+ cancel-in-progress: false
17
+
18
+ jobs:
19
+ build:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - uses: astral-sh/setup-uv@v7
24
+ with:
25
+ python-version: "3.13"
26
+ enable-cache: true
27
+ - run: uv sync --locked --only-group docs
28
+ - run: uv run --no-sync mkdocs build --strict
29
+ - uses: actions/upload-pages-artifact@v3
30
+ with:
31
+ path: site
32
+
33
+ deploy:
34
+ if: github.event_name != 'pull_request'
35
+ needs: build
36
+ runs-on: ubuntu-latest
37
+ permissions:
38
+ pages: write
39
+ id-token: write
40
+ environment:
41
+ name: github-pages
42
+ url: ${{ steps.deployment.outputs.page_url }}
43
+ steps:
44
+ - id: deployment
45
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,69 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: astral-sh/setup-uv@v7
16
+ with:
17
+ python-version: "3.13"
18
+ - name: Check the tag matches the package version
19
+ run: |
20
+ version=$(uv run --no-project python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
21
+ if [ "${GITHUB_REF_NAME}" != "v${version}" ]; then
22
+ echo "Release tag ${GITHUB_REF_NAME} does not match pyproject version v${version}" >&2
23
+ exit 1
24
+ fi
25
+ - run: uv sync --locked
26
+ - run: uv run --no-sync ruff format --check .
27
+ - run: uv run --no-sync ruff check .
28
+ - run: uv run --no-sync ty check
29
+ - run: uv run --no-sync pytest
30
+ - run: uv build
31
+ - name: Install and inspect a clean wheel
32
+ run: |
33
+ uv venv .cache/wheel-smoke
34
+ uv pip install --python .cache/wheel-smoke/bin/python dist/*.whl
35
+ .cache/wheel-smoke/bin/python tools/wheel_smoke.py
36
+ - uses: actions/upload-artifact@v4
37
+ with:
38
+ name: dist
39
+ path: dist/
40
+ if-no-files-found: error
41
+
42
+ publish-pypi:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment:
46
+ name: pypi
47
+ url: https://pypi.org/project/typoprint/
48
+ permissions:
49
+ id-token: write
50
+ steps:
51
+ - uses: actions/download-artifact@v4
52
+ with:
53
+ name: dist
54
+ path: dist/
55
+ - uses: pypa/gh-action-pypi-publish@release/v1
56
+
57
+ attach-to-release:
58
+ needs: publish-pypi
59
+ runs-on: ubuntu-latest
60
+ permissions:
61
+ contents: write
62
+ steps:
63
+ - uses: actions/download-artifact@v4
64
+ with:
65
+ name: dist
66
+ path: dist/
67
+ - env:
68
+ GH_TOKEN: ${{ github.token }}
69
+ run: gh release upload "${GITHUB_REF_NAME}" dist/* --repo "${GITHUB_REPOSITORY}"
@@ -0,0 +1,13 @@
1
+ # Typoprint contributor instructions
2
+
3
+ Keep the library and CLI thin and share the rendering pipeline. Rendering is
4
+ synchronous, offline with bundled resources, and isolated per invocation. Never
5
+ rewrite source Markdown, execute embedded Typst from Markdown, or discover Python
6
+ plugins from TOML. Templates and explicit callbacks are trusted developer code.
7
+
8
+ Python 3.13+; use `uv sync --locked`. Runtime dependencies are Typst and Pydantic.
9
+ Changes must pass `uv run ruff format --check .`, `uv run ruff check .`,
10
+ `uv run ty check`, and `uv run pytest`. Use real temporary input files and real
11
+ Typst for behavioral tests. Keep dependency/font licenses beside bundled assets.
12
+ Update user docs with behavior changes. Build with `uv build` and verify the
13
+ installed wheel when changing resources or packaging. Do not publish automatically.
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ All notable changes are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## Unreleased
8
+
9
+ ## 0.1.0
10
+
11
+ - First public release: offline Markdown-to-PDF rendering through a Python library
12
+ and CLI, with bundled Typst, fonts, Mermaid support, and `editorial` and `report`
13
+ layouts.
@@ -0,0 +1,15 @@
1
+ # Code of conduct
2
+
3
+ Typoprint is a welcoming, harassment-free project for everyone. Be kind, assume good
4
+ faith, give constructive feedback, and keep discussion about the work.
5
+
6
+ Unacceptable behavior includes harassment, insults, discriminatory language,
7
+ personal attacks, and publishing others' private information.
8
+
9
+ ## Reporting
10
+
11
+ Use GitHub's own tools: report an issue, comment, or pull request from its **⋯ menu →
12
+ Report content**, or block and report a user from their profile. Maintainers may
13
+ edit, hide, or remove contributions and restrict participants who break these rules.
14
+
15
+ For security problems, see [SECURITY.md](SECURITY.md) instead.
@@ -0,0 +1,41 @@
1
+ # Contributing to Typoprint
2
+
3
+ Thanks for helping. Typoprint is MIT licensed and welcomes issues and pull requests.
4
+
5
+ ## Set up
6
+
7
+ Python 3.13+ and [uv](https://docs.astral.sh/uv/) are required.
8
+
9
+ ```bash
10
+ git clone https://github.com/esandorfi/typoprint.git
11
+ cd typoprint
12
+ uv sync --locked
13
+ ```
14
+
15
+ ## Before opening a pull request
16
+
17
+ ```bash
18
+ uv run ruff format --check .
19
+ uv run ruff check .
20
+ uv run ty check
21
+ uv run pytest
22
+ ```
23
+
24
+ Tests use real temporary input files and the real Typst compiler. Update the user
25
+ documentation in `docs/` when behavior changes, and add an entry under
26
+ *Unreleased* in `CHANGELOG.md`. Preview the docs with
27
+ `uv sync --group docs && uv run mkdocs serve`.
28
+
29
+ ## Design rules
30
+
31
+ - Keep the library and CLI thin and share one rendering pipeline.
32
+ - Rendering stays synchronous, offline with bundled resources, and isolated per
33
+ invocation.
34
+ - Never rewrite source Markdown, execute embedded Typst from Markdown, or discover
35
+ Python plugins from TOML.
36
+ - Keep dependency and font licenses beside bundled assets.
37
+
38
+ Please follow the [code of conduct](CODE_OF_CONDUCT.md).
39
+
40
+ See [AGENTS.md](AGENTS.md) for the full contributor rules and
41
+ [docs/releasing.md](docs/releasing.md) for how releases are made.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright 2026 Emmanuel Sandorfi and Typoprint contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ include AGENTS.md CHANGELOG.md CONTRIBUTING.md SECURITY.md CODE_OF_CONDUCT.md uv.lock mkdocs.yml
2
+ recursive-include .github/workflows *.yml
3
+ recursive-include docs *.md
4
+ recursive-include examples *.py *.typ *.svg
5
+ recursive-include tests *.py
6
+ recursive-include tools *.py
7
+ recursive-include src/typoprint/resources *
@@ -0,0 +1,263 @@
1
+ Metadata-Version: 2.4
2
+ Name: typoprint
3
+ Version: 0.1.0
4
+ Summary: Markdown in. Beautiful PDFs out. Offline Markdown-to-PDF printing with Python, a CLI, and Typst layouts
5
+ Author: Emmanuel Sandorfi
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/esandorfi/typoprint
8
+ Project-URL: Documentation, https://esandorfi.github.io/typoprint/
9
+ Project-URL: Source, https://github.com/esandorfi/typoprint
10
+ Project-URL: Issues, https://github.com/esandorfi/typoprint/issues
11
+ Project-URL: Changelog, https://github.com/esandorfi/typoprint/blob/main/CHANGELOG.md
12
+ Keywords: markdown,pdf,typst,typesetting,mermaid,cli
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Printing
21
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.13
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ License-File: THIRD_PARTY_NOTICES.md
27
+ Requires-Dist: typst==0.15.0
28
+ Requires-Dist: pydantic<3,>=2.12
29
+ Dynamic: license-file
30
+
31
+ # Typoprint
32
+
33
+ **Markdown in. Beautiful PDFs out.**
34
+
35
+ Write in Markdown. Print like a publisher.
36
+
37
+ [![CI](https://github.com/esandorfi/typoprint/actions/workflows/check.yml/badge.svg)](https://github.com/esandorfi/typoprint/actions/workflows/check.yml)
38
+ [![Docs](https://img.shields.io/badge/docs-esandorfi.github.io-blue)](https://esandorfi.github.io/typoprint/)
39
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
40
+
41
+ Typoprint is a small Python library and CLI for offline Markdown printing, powered by
42
+ Typst. Use it in a terminal, API, worker, or script. No browser, TeX installation,
43
+ external compiler process, or network connection is needed to render.
44
+
45
+ Python **3.13+** is required. Rendering resources, Mermaid support, and fonts ship
46
+ with the package. Windows, Linux with glibc, and macOS are supported; Alpine/musl is
47
+ outside the tested wheel platforms.
48
+
49
+ ## Install
50
+
51
+ From PyPI:
52
+
53
+ ```bash
54
+ uv tool install typoprint # CLI in its own environment (or: pipx install typoprint)
55
+ pip install typoprint # library in the current environment (or: uv add typoprint)
56
+ ```
57
+
58
+ From a checkout:
59
+
60
+ ```bash
61
+ uv sync --locked
62
+ uv run typoprint --help
63
+ ```
64
+
65
+ Documentation: <https://esandorfi.github.io/typoprint/> (Python API in `docs/python.md`). Contributions are welcome;
66
+ see [CONTRIBUTING.md](CONTRIBUTING.md). Releases are published by maintainers; see
67
+ [releasing](docs/releasing.md).
68
+
69
+ ## CLI
70
+
71
+ ```bash
72
+ typoprint notes.md -o notes.pdf
73
+ typoprint docs/ --recursive --cover -o handbook.pdf
74
+ typoprint --order docs/README.md --strict --dry-run
75
+ typoprint docs/ --config typoprint.toml --preset handbook
76
+ typoprint notes.md --template report --lang en --paper us-letter
77
+ typoprint - -o - < notes.md > notes.pdf
78
+ ```
79
+
80
+ The last example uses a POSIX shell or `cmd.exe` for byte-preserving redirection.
81
+ In PowerShell, use file inputs and `-o notes.pdf`; PowerShell versions differ in
82
+ how pipelines handle native binary output. Stdin is UTF-8; `-o -` writes only PDF
83
+ bytes to stdout. Paths, warnings, and errors go to stderr. Errors return code 2.
84
+
85
+ Each file becomes a chapter. A directory contributes `README.md`, then other
86
+ Markdown files in natural order (`chapter-2` before `chapter-10`). `--recursive`
87
+ adds subfolders. `--exclude` accepts repeatable relative globs; an explicitly named
88
+ file is always included. `--by-folder` opens parts at folder changes.
89
+
90
+ `--order CONTENTS.md` reads local Markdown links under each `##` heading as chapter
91
+ order and part names. Folder links expand recursively. Code, images, web links,
92
+ and links outside the contents folder do not place pages. Unplaced pages follow
93
+ with a warning; `--strict` refuses them. The contents file itself prints first
94
+ unless excluded. This preserves the source feature's inline-link parser; reference
95
+ links do not place pages. See [CLI behavior](docs/cli.md).
96
+
97
+ ## Python
98
+
99
+ ```python
100
+ from typoprint import Settings, render, render_files, write_pdf
101
+
102
+ result = render("# Hello\n\nReady for print.")
103
+ write_pdf(result, "hello.pdf")
104
+
105
+ result = render_files(
106
+ ["docs"],
107
+ recursive=True,
108
+ exclude=["drafts"],
109
+ settings=Settings(template="report", lang="en"),
110
+ )
111
+ ```
112
+
113
+ `RenderResult.content` is PDF bytes; `RenderResult.warnings` is a tuple of strings.
114
+ Rendering never writes an output file. `write_pdf` replaces the destination
115
+ atomically after flushing the complete PDF. It requires an existing output folder.
116
+
117
+ For multiple in-memory chapters and explicitly supplied images:
118
+
119
+ ```python
120
+ from pathlib import Path
121
+ from typoprint import Chapter, Document, render
122
+
123
+ document = Document(
124
+ chapters=(
125
+ Chapter(name="intro.md", markdown="# Intro\n\n![Chart](images/chart.png)"),
126
+ Chapter(name="results.md", markdown="# Results\n\nSee [intro](intro.md)."),
127
+ )
128
+ )
129
+ result = render(document, assets={"images/chart.png": Path("chart.png").read_bytes()})
130
+ ```
131
+
132
+ Asset names are relative POSIX paths. Images resolve relative to each chapter's
133
+ name. In-memory rendering sees only explicitly supplied assets, never paths on
134
+ the host. Filesystem rendering resolves images inside the common input root,
135
+ rejects symlinks escaping it, and snapshots referenced images before compilation.
136
+ Remote images are refused. Mermaid blocks render locally; invalid or unreadable
137
+ diagrams fail explicitly.
138
+
139
+ The public API exports `Chapter`, `Document`, `Settings`, `PrintPlan`, `PlannedFile`,
140
+ `RenderResult`, `Transform`, `Validator`, `TypoprintError`, `ConfigurationError`,
141
+ `RenderError`, `RuleError`, and the five main functions. `python -m typoprint`
142
+ provides the same CLI. See runnable [HTTP API](examples/api.py) and
143
+ [process worker](examples/worker.py) examples.
144
+
145
+ ## Smart settings
146
+
147
+ Create `typoprint.toml`:
148
+
149
+ ```toml
150
+ [defaults]
151
+ template = "editorial"
152
+ lang = "fr"
153
+ paper = "a4"
154
+
155
+ [presets.handbook]
156
+ cover = true
157
+ toc = true
158
+ toc_depth = 2
159
+ title = "Engineering handbook"
160
+
161
+ [presets.brief]
162
+ template = "report"
163
+ cover = false
164
+ toc = false
165
+ ```
166
+
167
+ Precedence is explicit overrides → selected preset → defaults → supported
168
+ front-matter metadata → built-in defaults. An explicit `false` stays false.
169
+ Unspecified fields inherit; `None` for optional metadata also inherits.
170
+
171
+ The CLI looks only for `typoprint.toml` in its working directory. `--config` selects
172
+ another file; `--no-config` disables discovery. Library calls never discover a file:
173
+
174
+ ```python
175
+ from typoprint import Settings, load_settings, render
176
+
177
+ settings = load_settings("typoprint.toml", preset="handbook")
178
+ result = render("# Introduction", settings=settings, overrides=Settings(cover=False))
179
+ ```
180
+
181
+ Defaults are `editorial`, French, A4, no cover, contents for multiple chapters,
182
+ and contents depth 2. Titles use front matter, then the first H1, then the filename.
183
+ Front matter may supply `title`, `subtitle`, `author`, `date`, and `version`; chapter
184
+ dates and versions remain local to each chapter. Dates are never inferred from
185
+ the clock, filesystem timestamps, or Git.
186
+
187
+ Configuration can also specify `link_base`, `font_paths`, and `template_options`.
188
+ Paths in TOML belong to the TOML file's folder. Unknown keys and presets fail.
189
+ Nested `template_options` replace as a unit when layering settings. Dry runs emit
190
+ JSON containing input order, parts, effective metadata/settings, and warnings.
191
+
192
+ ## Extend
193
+
194
+ Use built-in `editorial` for book-style reading or `report` for compact sans-serif
195
+ reports. Supply a template directory with `template.typ` to define another layout.
196
+ It exports `document(meta, body)`, `chapter(meta, body)`, and `diagram(body)`.
197
+ Built-in layouts and foundations are reusable through the versioned local Typst
198
+ package. Custom options live in `meta.template_options`.
199
+
200
+ Python callers may also supply explicit transformations and validators:
201
+
202
+ ```python
203
+ from typoprint import RuleError, render
204
+
205
+
206
+ def require_summary(markdown: str) -> None:
207
+ if "## Summary" not in markdown:
208
+ raise RuleError("A Summary section is required")
209
+
210
+
211
+ result = render(
212
+ "# Draft\n\n## Summary\n\n{{product}} is ready.",
213
+ transforms=[lambda text: text.replace("{{product}}", "Typoprint")],
214
+ validators=[require_summary],
215
+ )
216
+ ```
217
+
218
+ All transforms run before validators for each chapter. Sources remain unchanged.
219
+ Callbacks and templates are trusted developer code, supplied by the application;
220
+ TOML and Markdown never import Python callbacks. Markdown cannot execute embedded
221
+ Typst. See the [extension guide](docs/extensions.md) and
222
+ [starter template](examples/template/template.typ).
223
+
224
+ ## Reproducibility and embedding
225
+
226
+ With identical inputs, settings, fonts, templates, and pinned dependencies,
227
+ repeated renders produce identical PDF bytes. System fonts are ignored. French
228
+ spacing, local links with page references, footnotes, tables, code highlighting,
229
+ and Mermaid share one pipeline across layouts. PDF accessibility tags remain
230
+ enabled; this does not certify every document against an accessibility standard.
231
+
232
+ Library calls are synchronous. An async API should offload rendering from its event
233
+ loop. A worker can use separate processes for CPU parallelism and caller-managed
234
+ timeouts. Each call owns its compiler and temporary files; callback state remains
235
+ the caller's responsibility. Compiler errors and warnings can quote Markdown, so
236
+ HTTP adapters should return generic messages and keep detailed diagnostics private.
237
+ Custom templates can execute Typst and custom Python callbacks have ordinary Python
238
+ privileges; only deploy extensions you trust. Bundle dependencies locally before
239
+ offline use. Automatic content-aware layout selection, LaTeX math rendering,
240
+ arbitrary HTML/CSS, remote asset fetching, a hosted service, and TypeScript bindings
241
+ are outside this release.
242
+
243
+ ## Develop
244
+
245
+ ```bash
246
+ uv sync --locked
247
+ uv run ruff format --check .
248
+ uv run ruff check .
249
+ uv run ty check
250
+ uv run pytest
251
+ uv build
252
+ ```
253
+
254
+ [Verification](docs/verification.md) describes clean-wheel and offline checks.
255
+ MIT covers first-party code. Bundled packages and fonts retain their own licenses;
256
+ see [third-party notices](THIRD_PARTY_NOTICES.md).
257
+
258
+ ## Authors and maintainers
259
+
260
+ Created and maintained by [Emmanuel Sandorfi](https://github.com/esandorfi), with
261
+ help from [contributors](https://github.com/esandorfi/typoprint/graphs/contributors).
262
+ Issues and pull requests are welcome; see [CONTRIBUTING.md](CONTRIBUTING.md) and the
263
+ [security policy](SECURITY.md).