Skip to content

Commit

Permalink
feat: overall enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
adrienbrignon committed May 18, 2023
1 parent bcda4a5 commit 85bca98
Show file tree
Hide file tree
Showing 21 changed files with 289 additions and 52 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ A highly-configurable plugin for [*MkDocs*](https://github.com/mkdocs/mkdocs) th
## Prerequisites

- Python `>= 3.7`
- MkDocs `>= 1.1`
- MkDocs `>= 1.4`

## Installation

Expand Down
1 change: 0 additions & 1 deletion docs/.pages
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
nav:
- getting-started.md
- setup
- reference.md
21 changes: 16 additions & 5 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
---
hide:
- navigation
---

# Getting started

## Introduction

[MkDocs Exporter](/) is a plugin for [MkDocs](https://www.mkdocs.org/), it allows you to export your documentation to various formats such as PDF. If you're familiar with Python, you can install the plugin with `pip` (or your favourite package manager).

???+ tip "Did you know?"

This documentation website features the plugin, meaning that you can download this page as a PDF document and read it offline!

Try this out by clicking the download button at the top of this page (or you can directly head [here](/getting-started/getting-started.pdf){:target="_blank"}).
Try this out by clicking the download button at the top of this page (or you can directly head [here](./getting-started.pdf){:target="_blank"}).



## Installation

Expand All @@ -16,9 +25,11 @@ You can start by installing the plugin with the package manager of your choice:
pip install mkdocs-exporter
```

If you plan on using this plugin to generate PDF documents, you'll also need to install a browser and its dependencies.
As this project uses [Playwright](https://github.com/microsoft/playwright) under the hood, the installation is a breeze:
You can now register the plugin in your configuration file:

```yaml
plugins:
- mkdocs/exporter
```
playwright install --with-deps
```
Check out the [setup guides](/setup) for more details about how to use and configure the plugin.
Empty file removed docs/index.md
Empty file.
6 changes: 0 additions & 6 deletions docs/reference.md

This file was deleted.

4 changes: 4 additions & 0 deletions docs/setup/.pages
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
nav:
- setting-up-documents.md
- setting-up-buttons.md
- ...
1 change: 0 additions & 1 deletion docs/setup/index.md

This file was deleted.

90 changes: 90 additions & 0 deletions docs/setup/setting-up-buttons.md
Original file line number Diff line number Diff line change
@@ -1 +1,91 @@
---
buttons:
- title: I'm Feeling Lucky
href: https://www.youtube.com/watch?v=dQw4w9WgXcQ
icon: material-star-outline
target: _blank
---

# Setting up buttons

You can define custom buttons at the top of your pages.

## Configuration

As this feature is provided by the `mkdocs/exporter/extras` plugin, you'll need to add it to your list of plugins:

```yaml
plugins:
- mkdocs/exporter
- mkdocs/exporter/extras
```
## Usage
### Adding a download button
This example will add a download button at the top of all pages that have a corresponding PDF document:
```yaml
plugins:
- mkdocs/exporter/extras:
buttons:
- title: Download as PDF
icon: material-file-download-outline
enabled: !!python/name:mkdocs_exporter.plugins.pdf.button.enabled
href: !!python/name:mkdocs_exporter.plugins.pdf.button.href
download: !!python/name:mkdocs_exporter.plugins.pdf.button.download
```
The functions referenced in this configuration are provided by the **MkDocs Exporter** plugin.
### Defining a dynamic button
As you've seen in the previous example, you can use Python functions to resolve the attributes of a button dynamically.
Let's write a button that when clicked, it starts a search on Google with the current page's title as query.
First of all, let's write the function that will resolve to the `href` attribute's of the button:

```python
from urllib.parse import urlencode
from mkdocs_exporter.page import Page
def href(page: Page) -> str:
"""The button's 'href' attribute."""
return 'https://google.com/search' + urlencode({q: page.title})
```

Then, we can define a button and specify the previously defined function (assuming it has been saved to `my_module/button.py`):

```yaml
plugins:
- mkdocs/exporter/extras:
buttons:
- title: Search on Google
icon: material-google
href: !!python/name:my_module.button.href
```

Rinse and repeat, you can use this method for any property of a button.

### Adding a button on a page

You can also use `meta` tags to define buttons on a per-page basis.
Here's the configuration used by this page:

```yaml
---
{% set button = page.meta.buttons[0] -%}
buttons:
- title: {{ button.title }}
href: {{ button.href }}
icon: {{ button.icon }}
target: {{ button.target }}
---
# {{ page.title }}
[...]
```
1 change: 0 additions & 1 deletion docs/setup/setting-up-cover-pages.md

This file was deleted.

86 changes: 86 additions & 0 deletions docs/setup/setting-up-documents.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Setting up documents

## Configuration

First of all, you'll need to register the `mkdocs/exporter/pdf` plugin (**after** the `mkdocs/exporter` one) to your configuration:

```yaml
plugins:
- mkdocs/exporter
- mkdocs/exporter/pdf
```
???+ question "Why an additional plugin?"
**MkDocs Exporter** comes with various plugins in a single package.
This architecture was chosen to reduce code duplication and maintain a generic base that can be used
to export your pages to formats other than PDF (although this is the only format currently supported).
Basically, the `mkdocs/exporter` must always be registered first as it provides a common ground for
other plugins to use.

## Usage

### Toggle documents generation

The documents generation can be enabled or disabled at any time.
This feature is especially useful during your development process, when you don't want to slow down your iterations because of document generation.

```yaml
plugins:
- mkdocs/exporter/pdf:
enabled: ![MKDOCS_EXPORTER_ENABLED, true]
```

You can now use the `MKDOCS_EXPORTER_ENABLED` environment variable to toggle the PDF generation.

### Increase concurrency

PDF are, by default, generated concurrently which greatly reduces build time.
You may want to override the default value of **4**, based on your current hardware.

```yaml
plugins:
- mkdocs/exporter/pdf:
concurrency: 16
```

With this configuration, up to **16** PDF documents can be generated concurrently.
As you've guessed, a value of **1** will build PDF documents sequentially.

### Exclude some pages

Sometimes, you may want to prevent a page from being converted to a PDF document.
You can use the `pdf` meta tag on your page to do so:

```yaml
---
pdf: false
---
# Lorem ipsum dolor sit amet
[...]
```

If you exclude more pages than you include, you can take the problem the other way around and explicitly define the pages for which PDF documents should be generated.
We call that the `explicit` mode, it can be enabled in your configuration file:

```yaml
plugins:
- mkdocs/exporter/pdf:
explicit: true
```

Only pages with a truthy value in the `pdf` meta tag will have a PDF document generated.

```yaml
---
pdf: true
---
# Lorem ipsum dolor sit amet
[...]
```
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ plugins:
- mkdocs/exporter/extras:
buttons:
- title: Download as PDF
icon: material-file-download-outline
enabled: !!python/name:mkdocs_exporter.plugins.pdf.button.enabled
icon: !!python/name:mkdocs_exporter.plugins.pdf.button.icon
href: !!python/name:mkdocs_exporter.plugins.pdf.button.href
download: !!python/name:mkdocs_exporter.plugins.pdf.button.download
- search:
Expand Down
3 changes: 3 additions & 0 deletions mkdocs_exporter/plugins/extras/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ class ButtonConfig(BaseConfig):
enabled = c.Type((bool, Callable), default=True)
"""Is the button enabled?"""

id = c.Optional(c.Type(str, Callable))
"""The button's identifier."""

title = c.Type((str, Callable))
"""The button's title."""

Expand Down
40 changes: 40 additions & 0 deletions mkdocs_exporter/plugins/extras/icon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from materialx.emoji import twemoji, to_svg


class HTMLStash:
"""Markdown HTML stash stub."""

def store(self, str):
return str


class MarkdownEmoji:
"""Markdown Emojij stub."""

emoji_index = twemoji({}, None)


class Markdown:
"""Markdown stub."""

htmlStash = HTMLStash()
inlinePatterns = {
'emoji': MarkdownEmoji()
}


def get_svg_icon(name):
"""Gets an icon by its name."""

if not name.startswith(':'):
name = ':' + name
if not name.endswith(':'):
name = name + ':'

try:
icon = to_svg('twemoji', name, None, None, None, None, None, {}, Markdown())

if icon is not None:
return icon.text
except KeyError:
return None
11 changes: 4 additions & 7 deletions mkdocs_exporter/plugins/extras/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from mkdocs.plugins import BasePlugin
from mkdocs_exporter.page import Page
from mkdocs.plugins import event_priority
from mkdocs_exporter.preprocessor import Preprocessor
from mkdocs_exporter.plugins.extras.config import Config
from mkdocs_exporter.plugins.extras.preprocessor import Preprocessor


class Plugin(BasePlugin[Config]):
Expand All @@ -15,17 +15,14 @@ def on_post_page(self, html: str, page: Page, **kwargs) -> Optional[str]:
"""Invoked after a page has been built."""

def resolve(value):
if callable(value):
return value(page)

return value
return value(page) if callable(value) else value

preprocessor = Preprocessor()

preprocessor.preprocess(html)

for button in self.config.buttons:
if resolve(button['enabled']):
for button in [*self.config.buttons, *page.meta.get('buttons', [])]:
if 'enabled' not in button or resolve(button['enabled']):
preprocessor.button(**{k: resolve(v) for k, v in button.items()})

return preprocessor.done()
26 changes: 26 additions & 0 deletions mkdocs_exporter/plugins/extras/preprocessor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from __future__ import annotations

from bs4 import BeautifulSoup
from mkdocs_exporter.plugins.extras.icon import get_svg_icon
from mkdocs_exporter.preprocessor import Preprocessor as BasePreprocessor


class Preprocessor(BasePreprocessor):
"""An extended preprocessor."""


def button(self, title: str, href: str, icon: str, **kwargs) -> Preprocessor:
"""Adds a button at the top of the page."""

tags = self.html.find('nav', {'class': 'md-tags'})
button = self.html.new_tag('a', title=title, href=href, **kwargs, attrs={'class': 'md-content__button md-icon'})
svg = BeautifulSoup(get_svg_icon(icon) or get_svg_icon('material-progress-question'), 'lxml')

button.append(svg)

if tags:
tags.insert_after(button)
else:
self.html.find('article', {'class': 'md-content__inner'}).insert(0, button)

return self
7 changes: 1 addition & 6 deletions mkdocs_exporter/plugins/pdf/button.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import os

from textwrap import dedent
from mkdocs_exporter.page import Page


Expand All @@ -25,8 +24,4 @@ def download(page: Page) -> str:
def icon(page: Page) -> str:
"""The button's icon."""

return dedent('''
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M14,2L20,8V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V4A2,2 0 0,1 6,2H14M18,20V9H13V4H6V20H18M12,19L8,15H10.5V12H13.5V15H16L12,19Z" />
</svg>
''')
return page.meta.get('pdf-icon', 'material-file-download-outline')
4 changes: 3 additions & 1 deletion mkdocs_exporter/plugins/pdf/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,13 @@ def on_pre_build(self, **kwargs) -> None:
def on_pre_page(self, page: Page, config: dict, **kwargs):
"""Invoked before building the page."""

if not hasattr(page, 'html'):
raise Exception('Missing `mkdocs/exporter` plugin or your plugins are not ordered properly!')
if not self._enabled():
return

directory = os.path.dirname(page.file.abs_dest_path)
filename = os.path.splitext(os.path.basename(page.file.abs_src_path))[0] + '.pdf'
filename = os.path.splitext(os.path.basename(page.file.abs_dest_path))[0] + '.pdf'
fullpath = os.path.join(directory, filename)

page.formats['pdf'] = os.path.relpath(fullpath, config['site_dir'])
Expand Down
Loading

0 comments on commit 85bca98

Please sign in to comment.