├── .python-version ├── docs ├── LICENSE.md ├── README.md ├── CHANGELOG.md ├── stylesheets │ └── extra.css ├── examples │ ├── buttons.md │ ├── cjk-fonts.md │ ├── blockquotes.md │ ├── annotations.md │ ├── content-tabs.md │ ├── footnotes.md │ ├── tooltips.md │ ├── icons-emojis.md │ ├── formatting.md │ ├── data-tables.md │ ├── code-blocks.md │ ├── grids.md │ ├── lists.md │ ├── basics.md │ ├── images.md │ └── admonitions.md ├── installation.md ├── usage.md └── stuff-to-migrate.md ├── src └── mkdocs_to_pdf │ ├── __init__.py │ ├── drivers │ ├── __init__.py │ ├── relaxedjs.py │ ├── headless_chrome.py │ └── event_hook.py │ ├── themes │ ├── __init__.py │ ├── mkdocs.scss │ ├── generic.py │ ├── material-polyfills.js │ ├── mkdocs.py │ ├── material.scss │ ├── material.py │ ├── material-polyfills.js.map │ └── material-polyfills.css.map │ ├── utils │ ├── __init__.py │ ├── soup_util.py │ ├── tabbed_set_util.py │ ├── section.py │ ├── layout_util.py │ ├── iframe_util.py │ ├── emoji_util.py │ └── image_util.py │ ├── templates │ ├── __init__.py │ ├── filters │ │ ├── datetime.py │ │ ├── __init__.py │ │ ├── barcode.py │ │ ├── url.py │ │ └── qrcode.py │ ├── default_back_cover.html.j2 │ ├── default_cover.html.j2 │ └── template.py │ ├── preprocessor │ ├── links │ │ ├── __init__.py │ │ ├── util.py │ │ └── transform.py │ └── __init__.py │ ├── styles │ ├── report-print.scss │ ├── _heading.scss │ ├── _fonts.scss │ ├── _toc.scss │ ├── __init__.py │ ├── _paging.scss │ └── cover.scss │ ├── cover.py │ ├── plugin.py │ ├── options.py │ └── toc.py ├── docker └── mkdocs-with-pdf │ ├── .gitignore │ ├── alpine │ ├── fonts │ │ ├── Kelmscott-Roman-NF │ │ │ ├── sharefonts.net.txt │ │ │ ├── KelmscottRomanNF.otf │ │ │ └── KelmscottRomanNF-Bold.otf │ │ ├── Gentium-Book-Basic │ │ │ ├── GenBkBasB.ttf │ │ │ ├── GenBkBasBI.ttf │ │ │ ├── GenBkBasI.ttf │ │ │ ├── GenBkBasR.ttf │ │ │ └── OFL.txt │ │ ├── Classico-URW-T-OT │ │ │ ├── Classico-URW-T-OT_10980.ttf │ │ │ ├── Classico-URW-T-OT-Bold_10976.ttf │ │ │ ├── Classico-URW-T-OT-Black_10974.ttf │ │ │ ├── Classico-URW-T-OT-Medium_10978.ttf │ │ │ ├── Classico-URW-T-OT-Bold-Italic_10975.ttf │ │ │ ├── Classico-URW-T-OT-Medium-Italic_10977.ttf │ │ │ └── Classico-URW-T-OT-Regular-Italic_10979.ttf │ │ └── MathJax │ │ │ └── convert.py │ └── Dockerfile │ ├── debian │ ├── fonts │ │ ├── Kelmscott-Roman-NF │ │ │ ├── sharefonts.net.txt │ │ │ ├── KelmscottRomanNF.otf │ │ │ └── KelmscottRomanNF-Bold.otf │ │ ├── Gentium-Book-Basic │ │ │ ├── GenBkBasB.ttf │ │ │ ├── GenBkBasBI.ttf │ │ │ ├── GenBkBasI.ttf │ │ │ ├── GenBkBasR.ttf │ │ │ └── OFL.txt │ │ └── Classico-URW-T-OT │ │ │ ├── Classico-URW-T-OT_10980.ttf │ │ │ ├── Classico-URW-T-OT-Bold_10976.ttf │ │ │ ├── Classico-URW-T-OT-Black_10974.ttf │ │ │ ├── Classico-URW-T-OT-Medium_10978.ttf │ │ │ ├── Classico-URW-T-OT-Bold-Italic_10975.ttf │ │ │ ├── Classico-URW-T-OT-Medium-Italic_10977.ttf │ │ │ └── Classico-URW-T-OT-Regular-Italic_10979.ttf │ └── Dockerfile │ ├── docker-compose.yml │ ├── tiny │ └── Dockerfile │ └── README.md ├── custom_style_src └── material-polyfills │ ├── .browserslistrc │ ├── .gitattributes │ ├── typings │ └── webpack-modules.d.ts │ ├── .prettierrc │ ├── dev-site │ └── assets │ │ ├── creating-your-site.png │ │ ├── search-highlighting.png │ │ ├── css │ │ └── extra.css │ │ └── stylesheets │ │ └── overrides.2b41d529.min.css │ ├── src │ ├── javascripts │ │ ├── index.ts │ │ └── converter │ │ │ └── twemoji.ts │ └── stylesheets │ │ ├── main │ │ └── _typeset.scss │ │ ├── polyfills.scss │ │ ├── extensions │ │ ├── pymdown │ │ │ ├── _details.scss │ │ │ └── _tasklist.scss │ │ ├── _footnotes.scss │ │ └── _admonition.scss │ │ ├── _config.scss │ │ └── utilities │ │ └── _convert.scss │ ├── tsconfig.json │ ├── .eslintrc.js │ ├── .stylelintrc │ ├── .gitignore │ ├── .editorconfig │ ├── package.json │ └── webpack.config.ts ├── .gitignore ├── .gitmodules ├── .readthedocs.yaml ├── .editorconfig ├── settings.sh ├── samples ├── mkdocs │ ├── mkdocs-yml.patch │ └── Makefile └── mkdocs-material │ ├── mkdocs-yml.patch │ └── Makefile ├── .gitattributes ├── shell.nix ├── .cspell.json ├── CONTRIBUTING.md ├── requirements.txt ├── LICENSE ├── README.md ├── requirements-dev.txt ├── .pre-commit-config.yaml ├── pyproject.toml ├── Makefile ├── mkdocs.yml └── CHANGELOG /.python-version: -------------------------------------------------------------------------------- 1 | 3.10 2 | -------------------------------------------------------------------------------- /docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ../CHANGELOG -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/drivers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/themes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/templates/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/.gitignore: -------------------------------------------------------------------------------- 1 | docs-src 2 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/preprocessor/links/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/.browserslistrc: -------------------------------------------------------------------------------- 1 | last 4 years 2 | -------------------------------------------------------------------------------- /docs/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | .youtube { 2 | color: #EE0F0F; 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv/ 2 | __pycache__/ 3 | gen/ 4 | site/ 5 | .aider* 6 | .token 7 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/typings/webpack-modules.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'webpack-fix-style-only-entries'; 2 | -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Kelmscott-Roman-NF/sharefonts.net.txt: -------------------------------------------------------------------------------- 1 | Free download fonts at http://sharefonts.net -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Kelmscott-Roman-NF/sharefonts.net.txt: -------------------------------------------------------------------------------- 1 | Free download fonts at http://sharefonts.net -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/styles/report-print.scss: -------------------------------------------------------------------------------- 1 | @import "_paging.scss"; 2 | @import "_heading.scss"; 3 | 4 | @import "_toc.scss"; 5 | 6 | @import "_fonts.scss"; 7 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "always", 4 | "singleQuote": true, 5 | "trailingComma": "none" 6 | } 7 | -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Gentium-Book-Basic/GenBkBasB.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Gentium-Book-Basic/GenBkBasB.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Gentium-Book-Basic/GenBkBasBI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Gentium-Book-Basic/GenBkBasBI.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Gentium-Book-Basic/GenBkBasI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Gentium-Book-Basic/GenBkBasI.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Gentium-Book-Basic/GenBkBasR.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Gentium-Book-Basic/GenBkBasR.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Gentium-Book-Basic/GenBkBasB.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Gentium-Book-Basic/GenBkBasB.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Gentium-Book-Basic/GenBkBasBI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Gentium-Book-Basic/GenBkBasBI.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Gentium-Book-Basic/GenBkBasI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Gentium-Book-Basic/GenBkBasI.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Gentium-Book-Basic/GenBkBasR.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Gentium-Book-Basic/GenBkBasR.ttf -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/dev-site/assets/creating-your-site.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/custom_style_src/material-polyfills/dev-site/assets/creating-your-site.png -------------------------------------------------------------------------------- /docs/examples/buttons.md: -------------------------------------------------------------------------------- 1 | # Buttons 2 | 3 | [Subscribe to our newsletter](https://example.com/){ .md-button } 4 | 5 | [Subscribe to our newsletter](https://example.com/){ .md-button .md-button--primary } 6 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/themes/mkdocs.scss: -------------------------------------------------------------------------------- 1 | @media print { 2 | body { 3 | min-width: 300px !important; // reset. TODO: 4 | } 5 | 6 | img { 7 | max-width: 100% !important; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/dev-site/assets/search-highlighting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/custom_style_src/material-polyfills/dev-site/assets/search-highlighting.png -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Kelmscott-Roman-NF/KelmscottRomanNF.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Kelmscott-Roman-NF/KelmscottRomanNF.otf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Kelmscott-Roman-NF/KelmscottRomanNF.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Kelmscott-Roman-NF/KelmscottRomanNF.otf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT_10980.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT_10980.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Kelmscott-Roman-NF/KelmscottRomanNF-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Kelmscott-Roman-NF/KelmscottRomanNF-Bold.otf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT_10980.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT_10980.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Kelmscott-Roman-NF/KelmscottRomanNF-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Kelmscott-Roman-NF/KelmscottRomanNF-Bold.otf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Bold_10976.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Bold_10976.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Bold_10976.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Bold_10976.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Black_10974.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Black_10974.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Medium_10978.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Medium_10978.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Black_10974.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Black_10974.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Medium_10978.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Medium_10978.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Bold-Italic_10975.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Bold-Italic_10975.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Bold-Italic_10975.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Bold-Italic_10975.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Medium-Italic_10977.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Medium-Italic_10977.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Regular-Italic_10979.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/alpine/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Regular-Italic_10979.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Medium-Italic_10977.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Medium-Italic_10977.ttf -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Regular-Italic_10979.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domWalters/mkdocs-to-pdf/HEAD/docker/mkdocs-with-pdf/debian/fonts/Classico-URW-T-OT/Classico-URW-T-OT-Regular-Italic_10979.ttf -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mkdocs"] 2 | path = samples/mkdocs/mkdocs 3 | url = https://github.com/mkdocs/mkdocs 4 | [submodule "mkdocs-material"] 5 | path = samples/mkdocs-material/mkdocs-material 6 | url = https://github.com/squidfunk/mkdocs-material 7 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | 4 | build: 5 | os: ubuntu-24.04 6 | tools: 7 | python: "3.13" 8 | 9 | mkdocs: 10 | configuration: mkdocs.yml 11 | 12 | python: 13 | install: 14 | - requirements: requirements-dev.txt 15 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/src/javascripts/index.ts: -------------------------------------------------------------------------------- 1 | // DOM modification for WeasyPrint. 2 | // 3 | // This script run on headless-chrome. 4 | // 5 | 6 | import './converter/twemoji' 7 | 8 | // and re-definition styles 9 | import '../stylesheets/polyfills.scss' 10 | -------------------------------------------------------------------------------- /docs/examples/cjk-fonts.md: -------------------------------------------------------------------------------- 1 | # CJK fonts 2 | 3 | ## Chinese 4 | 5 | ### Simplified Chinese 6 | 7 | 你好,世界! 谢谢 我爱你 8 | 9 | ### Traditional Chinese 10 | 11 | 你好,世界! 謝謝 我愛你 12 | 13 | ## Japanese 14 | 15 | こんにちは、世界! ありがとう 愛してます 16 | 17 | ## Korean 18 | 19 | 안녕하세요, 세상! 사랑해요 20 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/templates/filters/datetime.py: -------------------------------------------------------------------------------- 1 | from datetime import date, datetime 2 | 3 | 4 | def strptime(value: str, format) -> date: 5 | return datetime.strptime(value, format) 6 | 7 | 8 | def strftime(value, format) -> str: 9 | return value.strftime(format) 10 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "baseUrl": "src/javascripts", 5 | "lib": [ 6 | "dom", 7 | "es2019" 8 | ], 9 | "target": "es5" 10 | }, 11 | "exclude": [ 12 | "node_modules" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_size = 4 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.{js,ts,css,scss}] 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /settings.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=sh 2 | 3 | if [ "$(pwd)" != "$(git rev-parse --show-toplevel)" ]; then 4 | echo "$0 must be sourced from the directory that it is in" 5 | return 1 6 | fi 7 | 8 | if command -v nix-shell > /dev/null 2>&1; then 9 | nix-shell 10 | else 11 | . .venv/bin/activate 12 | fi 13 | -------------------------------------------------------------------------------- /samples/mkdocs/mkdocs-yml.patch: -------------------------------------------------------------------------------- 1 | diff --git a/mkdocs.yml b/mkdocs.yml 2 | index 7246e148..61d90bb9 100644 3 | --- a/mkdocs.yml 4 | +++ b/mkdocs.yml 5 | @@ -74,6 +74,7 @@ plugins: 6 | show_root_heading: true 7 | show_source: false 8 | show_signature_annotations: true 9 | + - to-pdf 10 | 11 | watch: 12 | - mkdocs 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | custom_style_src/* linguist-vendored 2 | docker/* linguist-vendored 3 | docs/* linguist-documentation 4 | samples/* linguist-documentation 5 | 6 | .gitattributes linguist-generated=true 7 | .gitignore linguist-generated=true 8 | requirements.txt linguist-generated=true 9 | requirements-dev.txt linguist-generated=true 10 | uv.lock linguist-generated=true 11 | -------------------------------------------------------------------------------- /docs/examples/blockquotes.md: -------------------------------------------------------------------------------- 1 | Hi John, 2 | 3 | Thanks for your email. 4 | 5 | > Could we schedule the meeting for next Tuesday at 10 AM? 6 | >> Yes, that time works for me. 7 | 8 | Great, see you then! 9 | 10 | > Also, please send me the presentation slides in advance. 11 | >> Sure, I'll send them over by tomorrow. 12 | 13 | Perfect, thanks! 14 | 15 | Best regards 16 | -------------------------------------------------------------------------------- /samples/mkdocs-material/mkdocs-yml.patch: -------------------------------------------------------------------------------- 1 | diff --git a/mkdocs.yml b/mkdocs.yml 2 | index 23cec8d57..96d3b5d02 100755 3 | --- a/mkdocs.yml 4 | +++ b/mkdocs.yml 5 | @@ -98,6 +98,7 @@ plugins: 6 | separator: '[\s\u200b\-_,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' 7 | - minify: 8 | minify_html: true 9 | + - to-pdf 10 | 11 | # Hooks 12 | hooks: 13 | -------------------------------------------------------------------------------- /docs/examples/annotations.md: -------------------------------------------------------------------------------- 1 | # Annotations 2 | 3 | Phasellus posuere in sem ut cursus (1) 4 | Lorem ipsum dolor sit amet, (2) consectetur adipiscing elit. Nulla et 5 | euismod nulla. Curabitur feugiat, tortor non consequat finibus, justo 6 | purus auctor massa, nec semper lorem quam in massa. 7 | { .annotate } 8 | 9 | 1. I'm an annotation! 10 | 2. I'm an annotation as well! 11 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/styles/_heading.scss: -------------------------------------------------------------------------------- 1 | article { 2 | h1 { 3 | border-bottom: 2px solid #f00; 4 | } 5 | 6 | h2 { 7 | border-bottom: 1px solid #f99; 8 | } 9 | 10 | h3 { 11 | border-bottom: 0.5px solid #eee; 12 | } 13 | 14 | h1>.pdf-order, 15 | h2>.pdf-order, 16 | h3>.pdf-order { 17 | padding-left: 6px; 18 | // padding-right: 0.8rem; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} }: 2 | 3 | (pkgs.buildFHSEnv { 4 | name = "mkdocs-to-pdf-env"; 5 | targetPkgs = pkgs: (with pkgs; [ 6 | fontconfig 7 | glib 8 | gnumake 9 | harfbuzz 10 | noto-fonts-cjk-sans 11 | pango 12 | python310 13 | uv 14 | ]); 15 | runScript = "bash --init-file <(cat $HOME/.bashrc; cat .venv/bin/activate) -i"; 16 | }).env 17 | -------------------------------------------------------------------------------- /docs/examples/content-tabs.md: -------------------------------------------------------------------------------- 1 | # Content tabs 2 | 3 | **XXX: In the PDF output, the content tab is displayed expanded.** 4 | 5 | === "Unordered list" 6 | 7 | * Sed sagittis eleifend rutrum 8 | * Donec vitae suscipit est 9 | * Nulla tempor lobortis orci 10 | 11 | === "Ordered list" 12 | 13 | 1. Sed sagittis eleifend rutrum 14 | 2. Donec vitae suscipit est 15 | 3. Nulla tempor lobortis orci 16 | -------------------------------------------------------------------------------- /docs/examples/footnotes.md: -------------------------------------------------------------------------------- 1 | # Footnotes 2 | 3 | ## Section 1 4 | 5 | Lorem ipsum[^1] dolor sit amet, consectetur adipiscing elit.[^2] 6 | 7 | [^1]: Lorem ipsum dolor sit amet, consectetur adipiscing elit. 8 | [^2]: 9 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod 10 | nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor 11 | massa, nec semper lorem quam in massa. 12 | 13 | ## Section 2 14 | 15 | ## Section 3 16 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/templates/default_back_cover.html.j2: -------------------------------------------------------------------------------- 1 |
2 |
3 | {%- set target_url = site_url or repo_url -%} 4 | {% if target_url is defined -%} 5 | 11 | {%- endif %} 12 |
13 |
14 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | serviceworker: true, 5 | browser: true, 6 | node: true 7 | }, 8 | parserOptions: { 9 | sourceType: 'module', 10 | ecmaVersion: 2015 11 | }, 12 | extends: ['standard', 'plugin:@typescript-eslint/recommended'], 13 | plugins: ['import', '@typescript-eslint'], 14 | // add your custom rules here 15 | rules: { 16 | 'unicorn/number-literal-case': 'off' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/examples/tooltips.md: -------------------------------------------------------------------------------- 1 | # Tooltips 2 | 3 | ## Tooltips 4 | 5 | **XXX: In the PDF output, it's just a link.** 6 | 7 | [Hover me](https://example.com "I'm a tooltip!") 8 | 9 | [Hover me][example] 10 | 11 | [example]: https://example.com "I'm a tooltip!" 12 | 13 | ## Abbreviations 14 | 15 | **XXX: In PDF output, abbreviations become just underlines.** 16 | 17 | The HTML specification is maintained by the W3C. 18 | 19 | *[HTML]: Hyper Text Markup Language 20 | *[W3C]: World Wide Web Consortium 21 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/templates/filters/__init__.py: -------------------------------------------------------------------------------- 1 | from mkdocs.config.base import Config 2 | 3 | 4 | class _FilterBase: 5 | def __init__(self, options: object, config: Config): 6 | self.__options = options 7 | self.__config = config 8 | 9 | @property 10 | def options(self): 11 | return self.__options 12 | 13 | @property 14 | def config(self): 15 | return self.__config 16 | 17 | def __call__(self, *args): 18 | raise "must be overridden" 19 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/src/stylesheets/main/_typeset.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | 3 | $md-typeset-kbd-border-color: #B8B8B8; 4 | 5 | .md-typeset { 6 | // Keyboard key 7 | kbd { 8 | vertical-align: middle; 9 | border-color: $md-typeset-kbd-border-color; 10 | border-style: outset; 11 | border-width: px2rem(1px) px2rem(1.5px) px2rem(2px) px2rem(1.5px); 12 | border-radius: px2rem(5px); 13 | box-shadow: none; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/utils/soup_util.py: -------------------------------------------------------------------------------- 1 | from bs4 import PageElement, Tag, NavigableString 2 | 3 | 4 | def clone_element(el: PageElement) -> PageElement: 5 | if isinstance(el, NavigableString): 6 | return type(el)(el) 7 | 8 | copy = Tag(None, el.builder, el.name, el.namespace, el.nsprefix) 9 | # work around bug where there is no builder set 10 | # https://bugs.launchpad.net/beautifulsoup/+bug/1307471 11 | copy.attrs = dict(el.attrs) 12 | for attr in ('can_be_empty_element', 'hidden'): 13 | setattr(copy, attr, getattr(el, attr)) 14 | for child in el.contents: 15 | copy.append(clone_element(child)) 16 | return copy 17 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/themes/generic.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from bs4 import BeautifulSoup 4 | from mkdocs.structure.pages import Page 5 | 6 | 7 | def get_stylesheet(debug_html: bool) -> str: 8 | return None 9 | 10 | 11 | def get_script_sources() -> list: 12 | return [] 13 | 14 | 15 | def inject_link(html: str, href: str, page: Page, logger: logging) -> str: 16 | 17 | soup = BeautifulSoup(html, 'html.parser') 18 | if soup.head: 19 | link = soup.new_tag('link', href=href, rel='alternate', 20 | title='PDF', type='application/pdf') 21 | soup.head.append(link) 22 | return str(soup) 23 | 24 | return html 25 | -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | tiny: 5 | build: tiny 6 | restart: always 7 | ports: 8 | - "8000:8000" 9 | volumes: 10 | - "./docs-src:/docs:rw" 11 | dns: 8.8.8.8 12 | network_mode: "bridge" 13 | 14 | alpine: 15 | build: alpine 16 | restart: always 17 | ports: 18 | - "8000:8000" 19 | volumes: 20 | - "./docs-src:/docs:rw" 21 | dns: 8.8.8.8 22 | network_mode: "bridge" 23 | 24 | debian: 25 | build: debian 26 | restart: always 27 | ports: 28 | - "8000:8000" 29 | volumes: 30 | - "./docs-src:/docs:rw" 31 | dns: 32 | - 8.8.8.8 33 | network_mode: "bridge" 34 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/utils/tabbed_set_util.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | from logging import Logger 3 | 4 | from bs4 import PageElement 5 | 6 | 7 | def wrap_tabbed_set_content(soup: PageElement, logger: Logger = None): 8 | for ts in soup.select('div.tabbed-set'): 9 | for radio in ts.select('input'): 10 | els = [i for i in itertools.takewhile( 11 | lambda x: x.name not in ['input'], 12 | radio.next_siblings)] 13 | wrapper = soup.new_tag('div', **{'class': 'tabbed-content--wrap'}) 14 | radio.wrap(wrapper) 15 | for tag in els: 16 | wrapper.append(tag) 17 | 18 | for d in soup.select('details'): 19 | d['open'] = '' 20 | -------------------------------------------------------------------------------- /docs/examples/icons-emojis.md: -------------------------------------------------------------------------------- 1 | # Icons & Emojis 2 | 3 | ## Emojis 4 | 5 | :smile: 6 | :+1: 7 | :-1: 8 | :heart: 9 | :poop: 10 | :zzz: 11 | :woman: 12 | :man: 13 | :cd: 14 | :office: 15 | :ok: 16 | 17 | 18 | ## Icons 19 | 20 | :fontawesome-brands-markdown: 21 | :fontawesome-regular-face-laugh-wink: 22 | :fontawesome-brands-html5: 23 | :fontawesome-brands-js: 24 | :fontawesome-brands-css3: 25 | :fontawesome-brands-internet-explorer: 26 | :material-format-font: 27 | :material-scale-balance: 28 | :material-clock-fast: 29 | :octicons-arrow-right-24: 30 | 31 | 32 | ### With colors 33 | 34 | !!! warning 35 | 36 | Applying CSS requires the `render_js` and `headless_chrome_path` options. 37 | 38 | :fontawesome-brands-youtube:{ .youtube } 39 | -------------------------------------------------------------------------------- /docs/examples/formatting.md: -------------------------------------------------------------------------------- 1 | # Formatting 2 | 3 | ## Highlighting changes 4 | 5 | Text can be {--deleted--} and replacement text {++added++}. This can also be 6 | combined into {~~one~>a single~~} operation. {==Highlighting==} is also 7 | possible {>>and comments can be added inline<<}. 8 | 9 | {== 10 | 11 | Formatting can also be applied to blocks by putting the opening and closing 12 | tags on separate lines and adding new lines between the tags and the content. 13 | 14 | ==} 15 | 16 | ## Highlighting text 17 | 18 | - ==This was marked (highlight)== 19 | - ^^This was inserted (underline)^^ 20 | - ~~This was deleted (strike-through)~~ 21 | 22 | ## Sub- and superscripts 23 | 24 | - H~2~O 25 | - A^T^A 26 | 27 | ## Adding keyboard keys 28 | 29 | ++ctrl+alt+del++ 30 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/src/stylesheets/polyfills.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Dependencies 3 | // ---------------------------------------------------------------------------- 4 | 5 | @import "modularscale"; 6 | @import "material-color"; 7 | 8 | // ---------------------------------------------------------------------------- 9 | // Local imports 10 | // ---------------------------------------------------------------------------- 11 | 12 | @import "utilities/convert"; 13 | 14 | @import "config"; 15 | 16 | @import "main/typeset"; 17 | 18 | @import "extensions/admonition"; 19 | @import "extensions/footnotes"; 20 | 21 | @import "extensions/pymdown/details"; 22 | @import "extensions/pymdown/tasklist"; 23 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/themes/material-polyfills.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";var e={999:function(e,t,r){r.r(t)},335:function(){var e;(e=document.querySelectorAll(".twemoji>svg"))&&0!=e.length&&e.forEach((function(e){var t=document.createElement("img"),r=window.getComputedStyle(e).color||"#333";e.setAttribute("style","fill:"+r+";");var o=(new XMLSerializer).serializeToString(e),n="data:image/svg+xml;base64,"+window.btoa(o);t.setAttribute("src",n),e.parentNode.replaceChild(t,e)}))}},t={};function r(o){var n=t[o];if(void 0!==n)return n.exports;var i=t[o]={exports:{}};return e[o](i,i.exports,r),i.exports}r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r(335),r(999)}(); 2 | //# sourceMappingURL=material-polyfills.js.map -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/styles/_fonts.scss: -------------------------------------------------------------------------------- 1 | @media print { 2 | html { 3 | font-size: 95%; 4 | font-family: 'Roboto', 'Noto Sans CJK JP', sans-serif; 5 | } 6 | 7 | body { 8 | font-size: 10.5pt; 9 | font-family: 'Roboto', 'Noto Sans CJK JP', sans-serif; 10 | } 11 | 12 | article { 13 | font-size: 0.8rem; 14 | line-height: 1.6; 15 | } 16 | 17 | h1, 18 | h2, 19 | h3, 20 | h4, 21 | h5, 22 | h6, 23 | label { 24 | font-family: 'Roboto', 'Noto Sans CJK JP', sans-serif; 25 | } 26 | 27 | pre, 28 | code, 29 | var, 30 | samp, 31 | kbd, 32 | tt { 33 | font-family: 'Roboto Mono', 'Noto Sans Mono CJK JP', monospace, sans-serif; 34 | font-size: 0.8em; 35 | } 36 | 37 | pre code, 38 | pre var, 39 | pre samp, 40 | pre kbd, 41 | pre tt { 42 | font-size: 100%; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "Makefile", 4 | "docs/**", 5 | "samples/**", 6 | "src/**", 7 | "tests/**" 8 | ], 9 | "ignorePaths": [ 10 | "*.css", 11 | "*.scss", 12 | "docs/examples/admonitions.md", 13 | "docs/examples/annotations.md", 14 | "docs/examples/content-tabs.md", 15 | "docs/examples/footnotes.md", 16 | "docs/examples/images.md", 17 | "docs/examples/lists.md" 18 | ], 19 | "language": "en", 20 | "version": "0.2", 21 | "words": [ 22 | "fontawesome", 23 | "headerlink", 24 | "hrefs", 25 | "htmlfile", 26 | "levelname", 27 | "levelno", 28 | "linenums", 29 | "mkdocs", 30 | "mypy", 31 | "nsprefix", 32 | "noqa", 33 | "pycache", 34 | "qrcode", 35 | "relaxedjs", 36 | "twemoji", 37 | "weasyprint", 38 | "venv" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/templates/default_cover.html.j2: -------------------------------------------------------------------------------- 1 |
2 | {% if cover_logo is defined %} 3 |
4 | 5 |
6 | {% else %} 7 |
8 | {% endif %} 9 |
10 |

{{ cover_title | e }}

11 |

{{ cover_subtitle | e }}

12 |
13 |
14 |
15 | {% if author is defined %} 16 |

{{ author | e }}

17 | {% endif %} 18 | {% if copyright is defined %} 19 | 20 | {% endif %} 21 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/themes/mkdocs.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | import sass 5 | from bs4 import BeautifulSoup 6 | from mkdocs.structure.pages import Page 7 | 8 | 9 | def get_stylesheet(debug_html: bool) -> str: 10 | base_path = os.path.abspath(os.path.dirname(__file__)) 11 | filename = os.path.join(base_path, "mkdocs.scss") 12 | return sass.compile(filename=filename) 13 | 14 | 15 | def get_script_sources() -> list: 16 | return [] 17 | 18 | 19 | def inject_link(html: str, href: str, page: Page, logger: logging) -> str: 20 | 21 | soup = BeautifulSoup(html, 'html.parser') 22 | if soup.head: 23 | link = soup.new_tag('link', href=href, rel='alternate', 24 | title='PDF', type='application/pdf') 25 | soup.head.append(link) 26 | return str(soup) 27 | 28 | return html 29 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Thank you for considering to contribute to this project. These guidelines will help you get going with development and outline the most important rules to follow when submitting pull requests for this project. 4 | 5 | ## Submitting Changes 6 | 7 | To get changes merged, create a pull request. Here are a few things to pay attention to when doing so: 8 | 9 | ### Commit Messages 10 | 11 | The summary of a commit should be concise and worded in an imperative mood. 12 | ...a *what* mood? This should clear things up: *[How to Write a Git Commit Message][git-commit-message]* 13 | 14 | ### Code Style 15 | 16 | Make sure your code follows [PEP-8](https://www.python.org/dev/peps/pep-0008/) and keeps things consistent with the rest of the code. 17 | 18 | [git-commit-message]: https://chris.beams.io/posts/git-commit/ 19 | -------------------------------------------------------------------------------- /docs/examples/data-tables.md: -------------------------------------------------------------------------------- 1 | # Data tables 2 | 3 | | Method | Description | 4 | | ----------- | --------------- | 5 | | `GET` | Fetch resource | 6 | | `PUT` | Update resource | 7 | | `DELETE` | Delete resource | 8 | 9 | 10 | ## aligned to left 11 | 12 | | Method | Description | 13 | | :---------- | :---------------| 14 | | `GET` | Fetch resource | 15 | | `PUT` | Update resource | 16 | | `DELETE` | Delete resource | 17 | 18 | ## centered 19 | 20 | | Method | Description | 21 | | :---------: | :-------------: | 22 | | `GET` | Fetch resource | 23 | | `PUT` | Update resource | 24 | | `DELETE` | Delete resource | 25 | 26 | ## aligned to right 27 | 28 | | Method | Description | 29 | | ----------: | --------------: | 30 | | `GET` | Fetch resource | 31 | | `PUT` | Update resource | 32 | | `DELETE` | Delete resource | 33 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/utils/section.py: -------------------------------------------------------------------------------- 1 | from base64 import b32encode 2 | from uuid import uuid4 3 | 4 | 5 | def get_section_path(section) -> str: 6 | if not section.is_section: 7 | raise "not section" 8 | 9 | slugs = [] 10 | if len(section.ancestors): 11 | slugs = list(map(lambda sec: _section_slug(sec), section.ancestors)) 12 | slugs.reverse() 13 | slugs.append(_section_slug(section)) 14 | 15 | return '/'.join(slugs) + '/' 16 | 17 | 18 | def _section_slug(section) -> str: 19 | if not section.is_section: 20 | raise "not section" 21 | 22 | slug = getattr(section, 'pdf_slug', None) 23 | if slug: 24 | return slug 25 | 26 | title = str(section.title).strip() 27 | title = title if title else str(uuid4) 28 | slug = b32encode(title.encode('utf-8')).rstrip(b'=').decode() 29 | setattr(section, 'pdf_slug', slug) 30 | 31 | return slug 32 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/templates/filters/barcode.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import io 3 | 4 | import barcode 5 | 6 | from . import _FilterBase 7 | 8 | 9 | # 10 | # @see https://pypi.org/project/python-barcode/ 11 | # 12 | 13 | class Barcode(_FilterBase): 14 | """ Generate Barcode for given value and returns embedded image data. """ 15 | 16 | # noqa: cSpell:disable 17 | 18 | # Support: 19 | # ['code39', 'code128', 'ean', 'ean13', 'ean8', 20 | # 'gs1', 'gtin', 'isbn', 21 | # 'isbn10', 'isbn13', 'issn', 'jan', 22 | # 'pzn', 'upc', 'upca'] 23 | 24 | # noqa: cSpell:enable 25 | 26 | def __call__(self, value, kind, **kwargs): 27 | coder = barcode.get_barcode_class(kind) 28 | img = coder(value, writer=None, **kwargs) 29 | with io.BytesIO() as stream: 30 | img.write(stream, dict(compress=True)) 31 | data = base64.b64encode(stream.getvalue()) 32 | return 'data:image/svg+xml;charset=utf-8;base64,' + \ 33 | data.decode("ascii") 34 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Users 4 | 5 | `mkdocs-to-pdf` is available on [PyPi](https://pypi.io/project/mkdocs-to-pdf): 6 | 7 | ```bash 8 | pip install mkdocs-to-pdf 9 | ``` 10 | 11 | ## Developers 12 | 13 | There are two sets of extras: 14 | 15 | - `docs`: Extra packages to build the `docs` directory. 16 | - `samples`: Extra packages to build everything in the 17 | [`samples`](https://github.com/domWalters/mkdocs-to-pdf/tree/develop/samples) 18 | directory. 19 | 20 | There is also the `all` extra, which installs all of these. 21 | 22 | For example: 23 | 24 | ```bash 25 | pip install mkdocs-to-pdf[all] 26 | ``` 27 | 28 | ## Requirements 29 | 30 | ### WeasyPrint 31 | 32 | `mkdocs-to-pdf` depends on [`weasyprint`][weasyprint]. 33 | 34 | `weasyprint` has OS specific dependencies. Follow the guidance in the 35 | [`weasyprint` documentation][weasyprint-install]. 36 | 37 | [weasyprint]: http://weasyprint.org/ 38 | [weasyprint-install]: https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#installation 39 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/src/javascripts/converter/twemoji.ts: -------------------------------------------------------------------------------- 1 | // Convert from '.twemoji>svg' to embedded img 2 | 3 | const twemojiConverter = () => { 4 | const entries = document.querySelectorAll('.twemoji>svg') 5 | if (!entries || entries.length == 0) return 6 | entries.forEach((svgElement) => { 7 | const imgElement = document.createElement('img') 8 | 9 | const styles = window.getComputedStyle(svgElement) 10 | const fillColor = styles.color || '#333' 11 | 12 | svgElement.setAttribute('style', `fill:${fillColor};`) 13 | 14 | //imgElement.setAttribute('style', `width:${styles.width};height:${styles.height};`) 15 | //imgElement.setAttribute('style', `width:1.125rem;height:auto;`) 16 | 17 | const serializedSVG = new XMLSerializer().serializeToString(svgElement) 18 | const src = 'data:image/svg+xml;base64,' + window.btoa(serializedSVG) 19 | imgElement.setAttribute('src', src) 20 | 21 | svgElement.parentNode.replaceChild(imgElement, svgElement) 22 | }) 23 | } 24 | 25 | twemojiConverter() 26 | -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/tiny/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-alpine 2 | 3 | ENV PYTHONUNBUFFERED 1 4 | 5 | # 6 | # Runtimes for WeasyPrint 7 | # 8 | RUN apk update \ 9 | && apk --update --upgrade --no-cache add cairo-dev pango-dev gdk-pixbuf-dev \ 10 | && apk --update --upgrade --no-cache add libsass 11 | 12 | RUN set -ex \ 13 | && apk add --no-cache --virtual .build-deps \ 14 | musl-dev g++ jpeg-dev zlib-dev libffi-dev libsass-dev \ 15 | && SYSTEM_SASS=1 pip install --no-cache-dir \ 16 | mkdocs mkdocs-material mkdocs-to-pdf \ 17 | && apk del .build-deps 18 | 19 | RUN apk --update --upgrade --no-cache add fontconfig ttf-freefont terminus-font \ 20 | && fc-cache -f \ 21 | && fc-list | sort 22 | 23 | # Set working directory and User 24 | RUN addgroup -g 1000 -S mkdocs && \ 25 | adduser -u 1000 -S mkdocs -G mkdocs 26 | USER mkdocs 27 | WORKDIR /docs 28 | 29 | # Expose MkDocs development server port 30 | EXPOSE 8000 31 | 32 | # Start development server by default 33 | ENTRYPOINT ["mkdocs"] 34 | CMD ["serve"] 35 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by uv via the following command: 2 | # uv export --frozen --output-file=requirements.txt --no-hashes --no-emit-project 3 | beautifulsoup4==4.9.3 4 | brotli==1.1.0 ; platform_python_implementation == 'CPython' 5 | brotlicffi==1.1.0.0 ; platform_python_implementation != 'CPython' 6 | cffi==1.17.1 7 | click==8.1.8 8 | colorama==0.4.6 ; sys_platform == 'win32' 9 | cssselect2==0.8.0 10 | fonttools==4.56.0 11 | ghp-import==2.1.0 12 | html5lib==1.1 13 | importlib-metadata==8.6.1 ; python_full_version < '3.10' 14 | jinja2==3.1.6 15 | libsass==0.23.0 16 | markdown==3.7 17 | markupsafe==3.0.2 18 | mergedeep==1.3.4 19 | mkdocs==1.6.1 20 | mkdocs-get-deps==0.2.0 21 | packaging==24.2 22 | pathspec==0.12.1 23 | pillow==11.1.0 24 | platformdirs==4.3.6 25 | pycparser==2.22 26 | pydyf==0.10.0 27 | pyphen==0.17.2 28 | python-dateutil==2.9.0.post0 29 | pyyaml==6.0.2 30 | pyyaml-env-tag==0.1 31 | six==1.17.0 32 | soupsieve==2.6 33 | tinycss2==1.4.0 34 | watchdog==6.0.0 35 | weasyprint==62.3 36 | webencodings==0.5.1 37 | zipp==3.21.0 ; python_full_version < '3.10' 38 | zopfli==0.2.3.post1 39 | -------------------------------------------------------------------------------- /docs/examples/code-blocks.md: -------------------------------------------------------------------------------- 1 | # Code Blocks 2 | 3 | ``` py 4 | import tensorflow as tf 5 | ``` 6 | 7 | ## Adding a title 8 | 9 | ``` py title="bubble_sort.py" 10 | def bubble_sort(items): 11 | for i in range(len(items)): 12 | for j in range(len(items) - 1 - i): 13 | if items[j] > items[j + 1]: 14 | items[j], items[j + 1] = items[j + 1], items[j] 15 | ``` 16 | 17 | ## Adding line numbers 18 | 19 | ``` py linenums="1" 20 | def bubble_sort(items): 21 | for i in range(len(items)): 22 | for j in range(len(items) - 1 - i): 23 | if items[j] > items[j + 1]: 24 | items[j], items[j + 1] = items[j + 1], items[j] 25 | ``` 26 | 27 | ## Highlighting specific lines 28 | 29 | ``` py hl_lines="2 3" 30 | def bubble_sort(items): 31 | for i in range(len(items)): 32 | for j in range(len(items) - 1 - i): 33 | if items[j] > items[j + 1]: 34 | items[j], items[j + 1] = items[j + 1], items[j] 35 | ``` 36 | 37 | ## Highlighting inline code blocks 38 | 39 | The `#!python range()` function is used to generate a sequence of numbers. 40 | 41 | -------------------------------------------------------------------------------- /docs/examples/grids.md: -------------------------------------------------------------------------------- 1 | # Grids 2 | 3 | **XXX: PDF output results in a row-based layout.** 4 | 5 | ## List syntax 6 | 7 |
8 | 9 | - __HTML__ for content and structure 10 | - __JavaScript__ for interactivity 11 | - __CSS__ for text running out of boxes 12 | - __Internet Explorer__ ... huh? 13 | 14 |
15 | 16 | ## Block syntax 17 | 18 |
19 | 20 | - __Set up in 5 minutes__ 21 | 22 | --- 23 | 24 | Install [`mkdocs-material`](#) with [`pip`](#) and get up 25 | and running in minutes 26 | 27 | [Getting started](#) 28 | 29 | - __It's just Markdown__ 30 | 31 | --- 32 | 33 | Focus on your content and generate a responsive and searchable static site 34 | 35 | [Reference](#) 36 | 37 | - __Made to measure__ 38 | 39 | --- 40 | 41 | Change the colors, fonts, language, icons, logo and more with a few lines 42 | 43 | [Customization](#) 44 | 45 | - __Open Source, MIT__ 46 | 47 | --- 48 | 49 | Material for MkDocs is licensed under MIT and available on [GitHub] 50 | 51 | [License](#) 52 | 53 |
54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, orzih 2 | 3 | Copyright (c) 2024-2025, Dominic Adam Walters 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 7 | deal in the Software without restriction, including without limitation the 8 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | sell 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 13 | all 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 NON-INFRINGEMENT. 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 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/README.md: -------------------------------------------------------------------------------- 1 | # _Templates_ for Docker 2 | 3 | This sample uses [`docker-compose`](https://docs.docker.com/compose/), but it's not required. 4 | 5 | ## Build docker images 6 | 7 | ```sh 8 | docker-compose build 9 | ``` 10 | 11 | ## How to use 12 | 13 | * _Create_ a new mkdocs project. 14 | 15 | ```sh 16 | docker-compose run tiny new . 17 | ``` 18 | 19 | * on _Debug(Edit)_ the mkdocs project. 20 | 21 | ```sh 22 | docker-compose up tiny 23 | ``` 24 | 25 | or 26 | 27 | ```sh 28 | docker-compose up alpine 29 | ``` 30 | 31 | or 32 | 33 | ```sh 34 | docker-compose up debian 35 | ``` 36 | 37 | and access to `http://localhost:8000` from any browser. 38 | 39 | * Building the site. 40 | 41 | ```sh 42 | docker-compose run tiny build 43 | ``` 44 | 45 | or 46 | 47 | ```sh 48 | docker-compose run alpine build 49 | ``` 50 | 51 | or 52 | 53 | ```sh 54 | docker-compose run debian build 55 | ``` 56 | 57 | see `docs-src` directory. 58 | 59 | ## TODO 60 | 61 | * `tiny` 62 | * `alpine` 63 | * [ ] MathJax font. 64 | * `debian` 65 | * [ ] Slim image. 66 | -------------------------------------------------------------------------------- /samples/mkdocs/Makefile: -------------------------------------------------------------------------------- 1 | at ?= @ 2 | makefile_dir := $(realpath $(patsubst %/,%,$(dir $(firstword $(MAKEFILE_LIST))))) 3 | 4 | .PHONY: help 5 | help: 6 | @echo "Usage:" 7 | @echo " build : build a PDF" 8 | @echo " clean : delete the site directory" 9 | @echo " distclean : return to factory settings" 10 | @echo " help : show this message" 11 | 12 | .PHONY: clean 13 | clean: 14 | $(at)find $(makefile_dir) -type d -name "site" -exec rm -rf {} + 15 | 16 | .PHONY: distclean 17 | distclean: clean 18 | $(at)if [ -f $(makefile_dir)/gen/patched ]; then \ 19 | patch -R $(makefile_dir)/mkdocs/mkdocs.yml < $(makefile_dir)/mkdocs-yml.patch; \ 20 | rm -rf $(makefile_dir)/gen; \ 21 | fi 22 | 23 | mkdocs ?= $(abspath $(makefile_dir)/../../.venv/bin/mkdocs) 24 | 25 | $(makefile_dir)/gen/patched: 26 | $(at)mkdir -p $(makefile_dir)/gen 27 | $(at)patch $(makefile_dir)/mkdocs/mkdocs.yml < $(makefile_dir)/mkdocs-yml.patch 28 | $(at)touch $@ 29 | 30 | .PHONY: build 31 | build: $(makefile_dir)/gen/patched 32 | $(at)if [ ! -f $(mkdocs) ]; then \ 33 | echo "$(mkdocs) is not available"; \ 34 | exit 2; \ 35 | fi; 36 | $(at)cd $(makefile_dir)/mkdocs && $(mkdocs) build 37 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/dev-site/assets/css/extra.css: -------------------------------------------------------------------------------- 1 | @media print { 2 | .md-typeset { 3 | line-height: 1.4; 4 | } 5 | .md-typeset ul li, 6 | .md-typeset ol li { 7 | margin-bottom: .25rem; 8 | } 9 | 10 | #doc-toc ul li>a>span, 11 | .md-typeset h1>span, 12 | .md-typeset h2>span, 13 | .md-typeset h3>span { 14 | display: inline-block; 15 | } 16 | 17 | .md-typeset h1>span, 18 | .md-typeset h2>span, 19 | .md-typeset h3>span { 20 | padding-right: 0.5rem; 21 | } 22 | 23 | /** workaround [[ */ 24 | .md-typeset h2>span.twemoji, 25 | .md-typeset h2>span.twemoji>img { 26 | height: 1.363em; 27 | } 28 | .md-typeset h2>span.twemoji>img { 29 | margin-top: -0.3em; 30 | } 31 | #doc-toc span.twemoji, 32 | #doc-toc span.twemoji img { 33 | height: 1.125rem; 34 | vertical-align: middle; 35 | } 36 | /* ]] **/ 37 | 38 | .md-typeset .tx-content__footer hr { 39 | background-color: transparent; 40 | border-bottom: 0.5px solid #e91e63; 41 | } 42 | 43 | section.md-typeset[data-url="/changelog/"] h4+ul { 44 | margin: 0; 45 | } 46 | section.md-typeset[data-url="/changelog/"] ul>li { 47 | margin-bottom: 0; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/utils/layout_util.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | from logging import Logger 3 | 4 | from bs4 import PageElement 5 | 6 | from .image_util import images_size_to_half_in 7 | 8 | 9 | def convert_for_two_columns(soup: PageElement, 10 | level: int, 11 | logger: Logger = None): 12 | if level == 0: 13 | return 14 | elif level != 3: 15 | if logger: 16 | logger.warning('`two_columns_level` is only support `3` yet.') 17 | return 18 | 19 | if logger: 20 | logger.info('Converting for two-column layout(heading level 3).') 21 | 22 | ignored = [] 23 | for el in soup.find_all('h3'): 24 | if el in ignored: 25 | continue 26 | els = [i for i in itertools.takewhile( 27 | lambda x: x.name not in ['h1', 'h2'], 28 | el.next_siblings)] 29 | section = soup.new_tag( 30 | 'section', **{'class': 'md-typeset two-columns'}) 31 | el.wrap(section) 32 | for tag in els: 33 | section.append(tag) 34 | if tag.name == 'h3': 35 | ignored.append(tag) 36 | images_size_to_half_in(section) 37 | -------------------------------------------------------------------------------- /samples/mkdocs-material/Makefile: -------------------------------------------------------------------------------- 1 | at ?= @ 2 | makefile_dir := $(realpath $(patsubst %/,%,$(dir $(firstword $(MAKEFILE_LIST))))) 3 | 4 | .PHONY: help 5 | help: 6 | @echo "Usage:" 7 | @echo " build : build a PDF" 8 | @echo " clean : delete the site directory" 9 | @echo " distclean : return to factory settings" 10 | @echo " help : show this message" 11 | 12 | .PHONY: clean 13 | clean: 14 | $(at)find $(makefile_dir) -type d -name "site" -exec rm -rf {} + 15 | 16 | .PHONY: distclean 17 | distclean: clean 18 | $(at)if [ -f $(makefile_dir)/gen/patched ]; then \ 19 | patch -R $(makefile_dir)/mkdocs-material/mkdocs.yml < $(makefile_dir)/mkdocs-yml.patch; \ 20 | rm -rf $(makefile_dir)/gen; \ 21 | fi 22 | 23 | mkdocs ?= $(abspath $(makefile_dir)/../../.venv/bin/mkdocs) 24 | 25 | $(makefile_dir)/gen/patched: 26 | $(at)mkdir -p $(makefile_dir)/gen 27 | $(at)patch $(makefile_dir)/mkdocs-material/mkdocs.yml < $(makefile_dir)/mkdocs-yml.patch 28 | $(at)touch $@ 29 | 30 | .PHONY: build 31 | build: $(makefile_dir)/gen/patched 32 | $(at)if [ ! -f $(mkdocs) ]; then \ 33 | echo "$(mkdocs) is not available"; \ 34 | exit 2; \ 35 | fi; 36 | $(at)cd $(makefile_dir)/mkdocs-material && $(mkdocs) build 37 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/preprocessor/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from bs4 import PageElement 4 | from weasyprint import urls 5 | 6 | from .links.transform import transform_href, transform_id 7 | from .links.util import get_body_id, rel_pdf_href, replace_asset_hrefs 8 | 9 | 10 | def get_combined( 11 | soup: PageElement, 12 | base_url: str, 13 | rel_url: str) -> PageElement: 14 | """ transforms all relative hrefs pointing to other html docs 15 | into relative pdf hrefs 16 | """ 17 | 18 | for element in soup.find_all(id=True): 19 | element['id'] = transform_id(element['id'], rel_url) 20 | 21 | for a in soup.find_all('a', href=True): 22 | if urls.url_is_absolute(a['href']) or os.path.isabs(a['href']): 23 | continue 24 | 25 | a['href'] = transform_href(a['href'], rel_url) 26 | 27 | soup.body['id'] = get_body_id(rel_url) 28 | soup = replace_asset_hrefs(soup, base_url) 29 | 30 | return soup 31 | 32 | 33 | def get_separate(soup: PageElement, base_url: str) -> PageElement: 34 | for a in soup.find_all('a', href=True): 35 | a['href'] = rel_pdf_href(a['href']) 36 | 37 | soup = replace_asset_hrefs(soup, base_url) 38 | return soup 39 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/templates/filters/url.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from urllib.parse import urlparse 4 | 5 | from . import _FilterBase 6 | 7 | 8 | class URLFilter(_FilterBase): 9 | """ Finds a matching filename in some directories and returns its URL. """ 10 | 11 | def __call__(self, pathname: str) -> str: 12 | if not pathname: 13 | return '' 14 | 15 | # Check for URL(eg. 'https://...') 16 | target_url = urlparse(pathname) 17 | if target_url.scheme or target_url.netloc: 18 | return pathname 19 | 20 | # Search image file in below directories: 21 | dirs = [ 22 | self.options.custom_template_path, 23 | getattr(self.config['theme'], 'custom_dir', None), 24 | self.config['docs_dir'], 25 | '.' 26 | ] 27 | 28 | for d in dirs: 29 | if not d: 30 | continue 31 | path = os.path.abspath(os.path.join(d, pathname)) 32 | if os.path.isfile(path): 33 | return 'file:///' + path.replace("\\", "/") \ 34 | if sys.platform == 'win32' \ 35 | else 'file://' + path 36 | 37 | # not found? 38 | return pathname 39 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/styles/_toc.scss: -------------------------------------------------------------------------------- 1 | // TOC page 2 | article#doc-toc { 3 | 4 | margin: 0 .8rem 1.2rem; 5 | padding-top: .6rem; 6 | font-size: 12pt; 7 | line-height: 1.6; 8 | 9 | &>h1 { 10 | margin: 0 0 2rem; 11 | color: rgba(0, 0, 0, .54); 12 | font-size: 1.2rem; 13 | font-weight: 300; 14 | letter-spacing: -.01em; 15 | page-break-after: auto; 16 | page-break-inside: auto; 17 | bookmark-level: none; 18 | 19 | &::before { 20 | display: block; 21 | margin-top: -9px; 22 | padding-top: 9px; 23 | content: ""; 24 | } 25 | } 26 | 27 | ul { 28 | list-style: none; 29 | padding-left: 0; 30 | margin-left: 1em; 31 | font-size: 8pt; 32 | page-break-inside: auto !important; 33 | } 34 | 35 | li { 36 | list-style: none; 37 | &>a { 38 | color: inherit; 39 | text-decoration: none; 40 | 41 | padding-top: 0.4rem; 42 | border-bottom: 1px dotted #ddd; 43 | display: inline-block; 44 | width: 100%; 45 | 46 | &>.pdf-order { 47 | padding-right: 0.5em; 48 | } 49 | 50 | &::after { 51 | // not support leader yet. 52 | float: right; 53 | content: target-counter(attr(href), page); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /docker/mkdocs-with-pdf/alpine/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-alpine 2 | 3 | ENV PYTHONUNBUFFERED 1 4 | 5 | # 6 | # Runtimes for WeasyPrint 7 | # 8 | RUN apk update \ 9 | && apk --update --upgrade --no-cache add cairo-dev pango-dev gdk-pixbuf-dev \ 10 | && apk --update --upgrade --no-cache add libsass 11 | 12 | RUN set -ex \ 13 | && apk add --no-cache --virtual .build-deps \ 14 | musl-dev g++ jpeg-dev zlib-dev libffi-dev libsass-dev \ 15 | && SYSTEM_SASS=1 pip install --no-cache-dir \ 16 | mkdocs mkdocs-material \ 17 | mdx-gh-links mkdocs-redirects mkdocs-minify-plugin \ 18 | mkdocs-to-pdf \ 19 | && apk del .build-deps 20 | 21 | # Headless Chrome 22 | RUN apk --update --upgrade --no-cache add udev chromium \ 23 | && chromium-browser --version 24 | 25 | # Additional font 26 | COPY fonts /usr/share/fonts/Additional 27 | RUN apk --update --upgrade --no-cache add fontconfig ttf-freefont font-noto terminus-font \ 28 | && fc-cache -f \ 29 | && fc-list | sort 30 | 31 | # Set working directory and User 32 | RUN addgroup -g 1000 -S mkdocs && \ 33 | adduser -u 1000 -S mkdocs -G mkdocs 34 | USER mkdocs 35 | WORKDIR /docs 36 | 37 | # Expose MkDocs development server port 38 | EXPOSE 8000 39 | 40 | # Start development server by default 41 | ENTRYPOINT ["mkdocs"] 42 | CMD ["serve"] 43 | -------------------------------------------------------------------------------- /custom_style_src/material-polyfills/.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "stylelint-config-recommended" 4 | ], 5 | "plugins": [ 6 | "stylelint-order", 7 | "stylelint-scss" 8 | ], 9 | "rules": { 10 | "at-rule-empty-line-before": null, 11 | "at-rule-no-unknown": null, 12 | "at-rule-no-vendor-prefix": true, 13 | "block-opening-brace-space-before": null, 14 | "block-closing-brace-newline-after": ["always", { 15 | "ignoreAtRules": [ 16 | "if", 17 | "else", 18 | "elseif" 19 | ] 20 | }], 21 | "color-hex-case": "upper", 22 | "color-hex-length": "long", 23 | "color-named": "never", 24 | "comment-empty-line-before": ["always", { 25 | "ignore": ["stylelint-commands"] 26 | }], 27 | "font-family-name-quotes": "always-where-recommended", 28 | "font-weight-notation": "numeric", 29 | "function-url-quotes": "always", 30 | "no-descending-specificity": null, 31 | "no-unknown-animations": true, 32 | "property-no-vendor-prefix": true, 33 | "selector-class-pattern": "^[a-z0-9]+(-[a-z0-9]+)*(__[a-z]+)?(--[a-z]+)?$", 34 | "selector-descendant-combinator-no-non-space": null, 35 | "string-quotes": "double", 36 | "unit-allowed-list": ["px", "em", "deg", "ms", "%", "mm", "vh", "dppx", "rem"], 37 | "value-keyword-case": "lower", 38 | "value-no-vendor-prefix": true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/styles/__init__.py: -------------------------------------------------------------------------------- 1 | import html 2 | import os 3 | 4 | import sass 5 | 6 | from ..options import Options 7 | 8 | 9 | def _css_escape(text: str) -> str: 10 | """ @see https://developer.mozilla.org/en-US/docs/Web/CSS/string """ 11 | 12 | if not text: 13 | return '' 14 | 15 | text = html.unescape(text) 16 | 17 | # -- probably not needed. 18 | # text = text.encode('unicode-escape').decode('ascii').replace('\\u', '\\') 19 | 20 | return text.replace("'", '\\27') 21 | 22 | 23 | def style_for_print(options: Options) -> str: 24 | scss = f""" 25 | :root {{ 26 | string-set: author '{_css_escape(options.author)}', 27 | copyright '{_css_escape(options.copyright)}', 28 | title '{_css_escape(options.cover_title)}'; 29 | }} 30 | h1, h2, h3 {{ 31 | string-set: chapter content(); 32 | }} 33 | """ 34 | css = sass.compile(string=scss) 35 | 36 | base_path = os.path.abspath(os.path.dirname(__file__)) 37 | 38 | filename = os.path.join(base_path, "report-print.scss") 39 | css += sass.compile(filename=filename) 40 | 41 | if options.cover or options.back_cover: 42 | filename = os.path.join(base_path, "cover.scss") 43 | css += sass.compile(filename=filename) 44 | 45 | filename = os.path.join(options.custom_template_path, 'styles.scss') 46 | if os.path.exists(filename): 47 | css += sass.compile(filename=filename) 48 | 49 | return css 50 | -------------------------------------------------------------------------------- /docs/examples/lists.md: -------------------------------------------------------------------------------- 1 | # Lists 2 | 3 | ## Unordered lists 4 | 5 | - Nulla et rhoncus turpis. Mauris ultricies elementum leo. Duis efficitur 6 | accumsan nibh eu mattis. Vivamus tempus velit eros, porttitor placerat nibh 7 | lacinia sed. Aenean in finibus diam. 8 | 9 | * Duis mollis est eget nibh volutpat, fermentum aliquet dui mollis. 10 | * Nam vulputate tincidunt fringilla. 11 | * Nullam dignissim ultrices urna non auctor. 12 | 13 | ## Ordered lists 14 | 15 | 1. Vivamus id mi enim. Integer id turpis sapien. Ut condimentum lobortis 16 | sagittis. Aliquam purus tellus, faucibus eget urna at, iaculis venenatis 17 | nulla. Vivamus a pharetra leo. 18 | 19 | 1. Vivamus venenatis porttitor tortor sit amet rutrum. Pellentesque aliquet 20 | quam enim, eu volutpat urna rutrum a. Nam vehicula nunc mauris, a 21 | ultricies libero efficitur sed. 22 | 23 | 2. Morbi eget dapibus felis. Vivamus venenatis porttitor tortor sit amet 24 | rutrum. Pellentesque aliquet quam enim, eu volutpat urna rutrum a. 25 | 26 | 1. Mauris dictum mi lacus 27 | 2. Ut sit amet placerat ante 28 | 3. Suspendisse ac eros arcu 29 | 30 | ## task lists 31 | 32 | - [x] Lorem ipsum dolor sit amet, consectetur adipiscing elit 33 | - [ ] Vestibulum convallis sit amet nisi a tincidunt 34 | * [x] In hac habitasse platea dictumst 35 | * [x] In scelerisque nibh non dolor mollis congue sed et metus 36 | * [ ] Praesent sed risus massa 37 | - [ ] Aenean pretium efficitur erat, donec pharetra, ligula non scelerisque 38 | -------------------------------------------------------------------------------- /src/mkdocs_to_pdf/utils/iframe_util.py: -------------------------------------------------------------------------------- 1 | from logging import Logger 2 | from bs4 import PageElement 3 | 4 | 5 | def convert_iframe(soup: PageElement, entries: list, logger: Logger = None): 6 | """Replace iFrame to a(anchor) 7 | 8 | e.g: 9 | ```html "before:" 10 |