This plugin will generate a single PDF file from your MkDocs repository. This plugin is inspired by MkDocs PDF Export Plugin.
- Cover and Table of Contents integrated in the PDF
- Automatically numbers on heading(h1-h3).
- Shift down sub-page headings level.
- using WeasyPrint.
- This package requires MkDocs version 1.0 or higher (0.17 works as well)
- Python 3.6 or higher
- WeasyPrint depends on cairo, Pango and GDK-PixBuf which need to be installed separately. Please follow the installation instructions for your platform carefully:
-
Install the package with pip:
pip install mkdocs-with-pdf
-
Enable the plugin in your
mkdocs.yml
:plugins: - with-pdf
More information about plugins in the MkDocs documentation.
When building your repository with mkdocs build
, you should now see the following message at the end of your build output:
Converting 10 articles to PDF took 7.1s
You may customize the plugin by passing options in mkdocs.yml
:
plugins:
- with-pdf:
#author: WHO
#copyright: ANY TEXT
#
#cover: false
#back_cover: true
#cover_title: TITLE TEXT
#cover_subtitle: SUBTITLE TEXT
#custom_template_path: TEMPLATES PATH
#
#toc_title: TOC TITLE TEXT
#heading_shift: false
#toc_level: 3
#ordered_chapter_level: 2
#excludes_children:
# - 'release-notes/:upgrading'
# - 'release-notes/:changelog'
#
#exclude_pages:
# - 'bugs/'
# - 'appendix/contribute/'
#convert_iframe:
# - src: IFRAME SRC
# img: POSTER IMAGE URL
# text: ALTERNATE TEXT
# - src: ...
#two_columns_level: 3
#
#render_js: true
#headless_chrome_path: headless-chromium
#
#output_path: any-place/document.pdf
#enabled_if_env: ENABLE_PDF_EXPORT
#
#debug_html: true
#show_anchors: true
#verbose: true
-
author
Set the author text.
default: usesite_author
in your projectmkdocs.yml
-
copyright
Set the author text.
default: usecopyright
in your projectmkdocs.yml
author
andcopyright
values are drawn in Cover, and you can use '@page' content.@page { @bottom-left { content: string(author) !important; } @bottom-right { content: string(copyright) !important; } }
-
cover
Set the value to
false
if you don't need a cover page.
default:true
-
back_cover
Set the value to
true
if you need a back cover page.
default:false
since:v0.8.0
You would be better to install the
qrcode
package:pip install qrcode
-
cover_title
Set the title text in cover page.
default: usesite_name
in your projectmkdocs.yml
-
cover_subtitle
Set the subtitle text in cover page.
default:None
-
cover_logo
Set the logo image in cover page. This value is URL or simply specify the relative path to the docs directory.
default:None
since:v0.8.0
-
toc_title
Set the title text of Table of Content.
default:Table of Content
since:v0.4.0
-
heading_shift
Set this value to
false
, disable shift heading in child page.
default:true
In this flags enable, heading move down one level in child page.
-
toc_level
Set the level of Table of Content. This value is enabled in the range of from
1
to3
.
default:3
-
ordered_chapter_level
Set the level of heading number addition. This value is enabled in the range of from
1
to3
.
default:3
-
excludes_children
Set the page
id
ofnav
url. If theid
matches in this list, it will be excluded from the heading number addition and table of contents.
default:[]
-
exclude_pages
Set the page
id
ofnav
url. If theid
matches in this list, it will be excluded page contents.
default:[]
since:v0.3.0
-
convert_iframe
List of
iframe
toa
conversions. Everyiframe
that matches asrc
in this list will be replace toa
contains eachimg
and/ortext
. it's using for such as embedded VIDEO.
default:[]
since:v0.6.0
-
two_columns_level
(Experimental)Set the heading level of Two Column Layout. Currently only
0
(disable) or3
is valid for this value. So slow processing, but a little nice.default:
0
since:v0.7.0
-
render_js
Set the value to
true
if you're using 'MathJax', 'Twemoji' or any more.
Require "Chrome" which has "headless" mode.default:
false
since:v0.7.0
-
headless_chrome_path
Set the "Headless Chrome" program path.
Ifrender_js
isfalse
, this value will be ignored.default:
chromium-browser
Check on your system:
$ <PROGRAM_PATH> --headless \ --disable-gpu \ --dump-dom \ <ANY_SITE_URL(eg. 'https://google.com')>
-
output_path
This option allows you to use a different destination for the PDF file.
default:pdf/document.pdf
-
custom_template_path
The path where your custom
cover.html
and/orstyles.scss
are located. default:templates
since:v0.8.0
-
enabled_if_env
Setting this option will enable the build only if there is an environment variable set to 1. This is useful to disable building the PDF files during development, since it can take a long time to export all files.
default:None
PDF generation can take significantly longer than HTML generation which can slow down mkdocs's built-in dev-server.
Adding
enabled_if_env: ENABLE_PDF_EXPORT
under- with-pdf:
disables PDF generation during development. Run the dev-server normally:$ mkdocs serve INFO - Browser Connected: http://127.0.0.1:8000/ INFO - Running task: builder (delay: None) INFO - Building documentation... WARNING - without generate PDF(set environment variable ENABLE_PDF_EXPORT to 1 to enable) ... 2 seconds later ... INFO - Reload 1 waiters: /.../index.md
and to build files to deploy specify
ENABLE_PDF_EXPORT=1
at the command line:$ ENABLE_PDF_EXPORT=1 mkdocs build ... INFO - Converting 10 articles to PDF took 7.1s INFO - Documentation built in 8.29 seconds
-
debug_html
Setting this to
true
will out HTML tostdout
on build time.
default:false
You can try this:
mkdocs build > for_pdf_print.html
...and browse output with Google Chrome. Chrome DevTools Into Print Preview Mode will you help.
Note: WeasyPrint and Google Chrome are not fully compatible.
-
show_anchors
Setting this to
true
will list out of anchor points provided during the build as info message.
default:false
since:v0.7.4
-
verbose
Setting this to
true
will show all WeasyPrint debug messages during the build.
default:false
It is possible to create a custom cover page for the document. You can also add a custom style sheet to modify the whole document.
To do so, add a templates
folder at the root level of your mkdocs
project and place a cover.html
and/or a styles.scss
inside.
Alternatively, you can specify a different location with the custom_template_path
option.
Using jinja2 syntax, you can access all data from your mkdocs.yml
.
To make template creation easier, you can use plugin_some_plugin
to access variables from plugins.
E.g. use {{ author }}
to get the author from your mkdocs.yml
that looks like:
plugins:
- with-pdf:
author: WHO
You can use custom variables extra:
in your mkdocs.yml
And, you can check it in the log if run with verbose
or debug_html
options.
Since your stylesheet is appended to the default ones, you can override every setting from them.
Tip: setting the debug_html
option to true
to get the generated html that is passed to weasyprint
can help you determine the html tags, classes or identifiers you want to modify in your stylesheet.
You can hook the PDF rendering process by creating a pdf_event_hook.py
(or pdf_event_hook/__init__.py
) in your working directory (usually the same directory as mkdocs.yml
).
since: v0.8.2
import logging
from bs4 import BeautifulSoup
from mkdocs.structure.pages import Page
def inject_link(html: str, href: str,
page: Page, logger: logging) -> str:
"""Adding PDF View button on navigation bar(using material theme)"""
def _pdf_icon():
_ICON = '''
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z" fill="#E2E5E7"/>
<path d="m384 128h96l-128-128v96c0 17.6 14.4 32 32 32z" fill="#B0B7BD"/>
<polygon points="480 224 384 128 480 128" fill="#CAD1D8"/>
<path d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16 V416z" fill="#F15642"/>
<g fill="#fff">
<path d="m101.74 303.15c0-4.224 3.328-8.832 8.688-8.832h29.552c16.64 0 31.616 11.136 31.616 32.48 0 20.224-14.976 31.488-31.616 31.488h-21.36v16.896c0 5.632-3.584 8.816-8.192 8.816-4.224 0-8.688-3.184-8.688-8.816v-72.032zm16.88 7.28v31.872h21.36c8.576 0 15.36-7.568 15.36-15.504 0-8.944-6.784-16.368-15.36-16.368h-21.36z"/>
<path d="m196.66 384c-4.224 0-8.832-2.304-8.832-7.92v-72.672c0-4.592 4.608-7.936 8.832-7.936h29.296c58.464 0 57.184 88.528 1.152 88.528h-30.448zm8.064-72.912v57.312h21.232c34.544 0 36.08-57.312 0-57.312h-21.232z"/>
<path d="m303.87 312.11v20.336h32.624c4.608 0 9.216 4.608 9.216 9.072 0 4.224-4.608 7.68-9.216 7.68h-32.624v26.864c0 4.48-3.184 7.92-7.664 7.92-5.632 0-9.072-3.44-9.072-7.92v-72.672c0-4.592 3.456-7.936 9.072-7.936h44.912c5.632 0 8.96 3.344 8.96 7.936 0 4.096-3.328 8.704-8.96 8.704h-37.248v0.016z"/>
</g>
<path d="m400 432h-304v16h304c8.8 0 16-7.2 16-16v-16c0 8.8-7.2 16-16 16z" fill="#CAD1D8"/>
</svg>
''' # noqa: E501
return BeautifulSoup(_ICON, 'html.parser')
logger.info(f'(hook on inject_link: {page.title})')
soup = BeautifulSoup(html, 'html.parser')
nav = soup.find(class_='md-header-nav')
if not nav:
# after 7.x
nav = soup.find('nav', class_='md-header__inner')
if nav:
a = soup.new_tag('a', href=href, title='PDF',
**{'class': 'md-header__button md-header-nav__button md-icon'})
a.append(_pdf_icon())
nav.append(a)
return str(soup)
return html
# def pre_js_render(soup: BeautifulSoup, logger: logging) -> BeautifulSoup:
# logger.info('(hook on pre_js_render)')
# return soup
# def pre_pdf_render(soup: BeautifulSoup, logger: logging) -> BeautifulSoup:
# logger.info('(hook on pre_pdf_render)')
# tag = soup.find(lambda tag: tag.name ==
# 'body' and 'data-md-color-scheme' in tag.attrs)
# if tag:
# tag['data-md-color-scheme'] = 'print'
# return soup
... and check log:
$ mkdocs build
INFO - Found PDF rendering event hook module.
INFO - Cleaning site directory
INFO - Building documentation to directory: /tmp/sample/site
INFO - (hook on inject_link: Home)
...
From reporting a bug to submitting a pull request: every contribution is appreciated and welcome. Report bugs, ask questions and request features using Github issues. If you want to contribute to the code of this project, please read the Contribution Guidelines.
- Terry Zhao the author of the MkDocs PDF Export Plugin the source of our inspiration. We've used some of his code in this project.