diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index cca6c69..c1dbc96 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -21,7 +21,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
- fetch-depth: 1
+ fetch-depth: 0
- name: Set up Poetry
run: |
pipx install poetry
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 8e2dd19..97d3410 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -8,8 +8,8 @@ env:
PYTHON_VERSION: '3.12'
jobs:
- lint:
- name: Lint source code
+ flake8:
+ name: Flake8
runs-on: ubuntu-latest
steps:
- name: Checkout repository
@@ -33,6 +33,6 @@ jobs:
run: |
pip install poetry
make install
- - name: Run linter
+ - name: Run Flake8
run: |
poetry run flake8
diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml
new file mode 100644
index 0000000..82314a5
--- /dev/null
+++ b/.github/workflows/security.yml
@@ -0,0 +1,38 @@
+name: Security
+
+on:
+ workflow_dispatch:
+ push:
+
+env:
+ PYTHON_VERSION: '3.12'
+
+jobs:
+ bandit:
+ name: Bandit
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 1
+ - name: Set up Poetry
+ run: |
+ pipx install poetry
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ env.PYTHON_VERSION }}
+ cache: poetry
+ - name: Set up Poetry environment
+ env:
+ PYTHON_VERSION: ${{ env.PYTHON_VERSION }}
+ run: |
+ poetry env use ${PYTHON_VERSION}
+ - name: Install Python dependencies
+ run: |
+ pip install poetry
+ make install
+ - name: Run Bandit
+ run: |
+ poetry run bandit -r mkdocs_exporter
diff --git a/Makefile b/Makefile
index 2646c34..0c77431 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,7 @@ ifeq ($(origin .RECIPEPREFIX), undefined)
endif
PDF = true
+PDF_AGGREGATOR = true
DEBUG = false
.RECIPEPREFIX = >
@@ -17,10 +18,10 @@ browser:
> poetry run playwright install $(if $(FORCE),--force,) --with-deps
build:
->@ MKDOCS_EXPORTER_PDF=$(PDF) poetry run mkdocs build
+>@ MKDOCS_EXPORTER_PDF=$(PDF) MKDOCS_EXPORTER_PDF_AGGREGATOR=$(PDF_AGGREGATOR) poetry run mkdocs build
serve:
->@ MKDOCS_EXPORTER_PDF=$(PDF) poetry run mkdocs serve
+>@ MKDOCS_EXPORTER_PDF=$(PDF) MKDOCS_EXPORTER_PDF_AGGREGATOR=$(PDF_AGGREGATOR) poetry run mkdocs serve
clean:
> $(RM) -rf dist/
diff --git a/docs/configuration/generating-pdf-documents.md b/docs/configuration/generating-pdf-documents.md
index 57136e4..4e09c03 100644
--- a/docs/configuration/generating-pdf-documents.md
+++ b/docs/configuration/generating-pdf-documents.md
@@ -251,4 +251,18 @@ plugins:
aggregator:
enabled: true
output: documentation.pdf
+ covers: all
```
+
+#### Configuring cover pages behavior
+
+When aggregating PDF documents, you have the flexibility to configure the behavior of cover pages to suit your needs.
+There are five available options for managing cover pages:
+
+- `all` (*default*): retains every cover page from all documents.
+- `none`: removes every cover page from the aggregated document, resulting in a compilation that includes only the main content of each PDF.
+- `limits`: retains the front cover of the first document and the back cover of the last document, while removing all other cover pages in between.
+- `front`: preserves every front cover page from all documents but removes all back cover pages.
+- `back`: preserves every back cover page from all documents but removes all front cover pages.
+
+Choose the behavior that best aligns with your document aggregation needs to ensure the final PDF meets your requirements.
diff --git a/docs/getting-started.md b/docs/getting-started.md
index c5dee31..d9db5f6 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -3,8 +3,6 @@ hide:
- navigation
---
-
-
# Getting started
## Introduction
@@ -39,4 +37,4 @@ plugins:
- exporter
```
-Check out the [configuration guides](configuration/generating-pdf-documents) for more details about how to use and configure the plugin.
+Check out the [configuration guides](../configuration/generating-pdf-documents) for more details about how to use and configure the plugin.
diff --git a/mkdocs.yml b/mkdocs.yml
index 4eb19fa..9d9f0c9 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -47,7 +47,6 @@ extra:
plugins:
- privacy:
- log_level: warn
- mkdocstrings:
handlers:
python:
@@ -76,6 +75,9 @@ plugins:
- social:
cards_layout_options:
background_color: '#EA2027'
+ - git-committers:
+ repository: adrienbrignon/mkdocs-exporter
+ branch: master
- exporter:
logging:
level: debug
@@ -87,13 +89,13 @@ plugins:
- resources/stylesheets/pdf.scss
covers:
front: resources/templates/covers/front.html.j2
+ back: resources/templates/covers/back.html.j2
browser:
debug: false
- headless: true
aggregator:
- enabled: true
+ enabled: !ENV [MKDOCS_EXPORTER_PDF_AGGREGATOR, true]
output: documentation.pdf
- covers: limits
+ covers: front
buttons:
- title: View as PDF
icon: material-file-move-outline
@@ -134,13 +136,13 @@ markdown_extensions:
extra_css:
- assets/stylesheets/custom.css
- - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.css
+ - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css
extra_javascript:
- assets/scripts/katex.js
- assets/scripts/mathjax.js
- assets/scripts/mkdocs-exporter.js
- - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.js
- - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/contrib/auto-render.min.js
+ - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.js
+ - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/contrib/auto-render.min.js
- https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.min.js
- - https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.6.1/mermaid.min.js
+ - https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.1/mermaid.min.js
diff --git a/mkdocs_exporter/formats/pdf/aggregator.py b/mkdocs_exporter/formats/pdf/aggregator.py
index 8d9da94..b9b897a 100644
--- a/mkdocs_exporter/formats/pdf/aggregator.py
+++ b/mkdocs_exporter/formats/pdf/aggregator.py
@@ -4,6 +4,7 @@
from pypdf import PdfWriter
+from mkdocs_exporter.page import Page
from mkdocs_exporter.formats.pdf.renderer import Renderer
from mkdocs_exporter.formats.pdf.preprocessor import Preprocessor
@@ -12,10 +13,11 @@ class Aggregator:
"""Aggregates PDF documents together."""
- def __init__(self, renderer: Renderer):
+ def __init__(self, renderer: Renderer, config: dict = {}) -> None:
"""The constructor."""
- self.total_pages = 0
+ self.pages = []
+ self.config = config
self.renderer = renderer
@@ -25,20 +27,50 @@ def open(self, path: str) -> Aggregator:
self.path = path
self.writer = PdfWriter()
+ return self
+
+
+ def set_pages(self, pages: list[Page]) -> Aggregator:
+ """Sets the pages."""
- def increment_total_pages(self, total_pages: int) -> Aggregator:
- """Increments the total pages count."""
+ self.pages = pages
+ covers = self.config.get('covers', [])
- self.total_pages = self.total_pages + total_pages
+ for index, page in enumerate(self.pages):
+ if covers == 'none':
+ self._skip(page, ['front', 'back'])
+ elif covers == 'front':
+ self._skip(page, ['back'])
+ elif covers == 'back':
+ self._skip(page, ['front'])
+ elif covers == 'limits':
+ if index == 0:
+ self._skip(page, ['back'])
+ elif index == (len(self.pages) - 1):
+ self._skip(page, ['front'])
+ else:
+ self._skip(page, ['front', 'back'])
+
+ return self
- def preprocess(self, html: str, page_number: int = 1) -> str:
+ def preprocess(self, page: Page) -> str:
"""Preprocesses the page."""
preprocessor = Preprocessor()
- preprocessor.preprocess(html)
- preprocessor.metadata({'page': page_number, 'pages': self.total_pages})
+ preprocessor.preprocess(self.renderer.preprocess(page, disable=['teleport']))
+
+ if 'front' not in page.formats['pdf']['covers']:
+ preprocessor.remove('div.mkdocs-exporter-front-cover')
+ if 'back' not in page.formats['pdf']['covers']:
+ preprocessor.remove('div.mkdocs-exporter-back-cover')
+
+ preprocessor.teleport()
+ preprocessor.metadata({
+ 'page': sum(page.formats['pdf']['pages'] - page.formats['pdf']['skipped_pages'] for page in self.pages[:page.index]),
+ 'pages': sum(page.formats['pdf']['pages'] - page.formats['pdf']['skipped_pages'] for page in self.pages),
+ })
return preprocessor.done()
@@ -63,3 +95,14 @@ def save(self, metadata={}) -> Aggregator:
self.writer = None
return self
+
+
+ def _skip(self, page: Page, covers: list[str]) -> Aggregator:
+ """Skip cover pages."""
+
+ for cover in covers:
+ if cover in page.formats['pdf']['covers']:
+ page.formats['pdf']['covers'].remove(cover)
+ page.formats['pdf']['skipped_pages'] = page.formats['pdf']['skipped_pages'] + 1
+
+ return self
diff --git a/mkdocs_exporter/formats/pdf/browser.py b/mkdocs_exporter/formats/pdf/browser.py
index b5cd103..96b9c93 100644
--- a/mkdocs_exporter/formats/pdf/browser.py
+++ b/mkdocs_exporter/formats/pdf/browser.py
@@ -15,7 +15,8 @@ class Browser:
args = [
'--disable-background-timer-throttling',
'--disable-renderer-backgrounding',
- '--allow-file-access-from-files'
+ '--allow-file-access-from-files',
+ '--font-render-hinting=none'
]
"""The browser's arguments..."""
@@ -38,6 +39,7 @@ def __init__(self, options: dict = {}):
self.debug = options.get('debug', False)
self.headless = options.get('headless', True)
self.timeout = options.get('timeout', 60_000)
+ self.args = self.args + options.get('args', [])
self.levels = {
'warn': 'warning',
'error': 'error',
@@ -100,13 +102,10 @@ async def print(self, html: str) -> tuple[bytes, int]:
pages = int(await context.locator('body').get_attribute('mkdocs-exporter-pages') or 0)
pdf = await context.pdf(prefer_css_page_size=True, print_background=True, display_header_footer=False)
- try:
- os.unlink(file)
- except Exception:
- pass
-
await context.close()
+ os.unlink(file.name)
+
return (pdf, pages)
diff --git a/mkdocs_exporter/formats/pdf/config.py b/mkdocs_exporter/formats/pdf/config.py
index 5c0c400..5d9b05d 100644
--- a/mkdocs_exporter/formats/pdf/config.py
+++ b/mkdocs_exporter/formats/pdf/config.py
@@ -14,6 +14,9 @@ class BrowserConfig(BaseConfig):
timeout = c.Type(int, default=60_000)
"""The timeout when waiting for the PDF to render."""
+ args = c.ListOfItems(c.Type(str), default=[])
+ """Extra arguments to pass to the browser."""
+
class CoversConfig(BaseConfig):
"""The cover's configuration."""
@@ -26,6 +29,7 @@ class CoversConfig(BaseConfig):
class AggregatorConfig(BaseConfig):
+ """The aggregator's configuration."""
enabled = c.Type(bool, default=False)
"""Is the aggregator enabled?"""
@@ -36,6 +40,9 @@ class AggregatorConfig(BaseConfig):
metadata = c.Type(dict, default={})
"""Some metadata to append to the PDF document."""
+ covers = c.Choice(['all', 'none', 'limits', 'front', 'back'], default='all')
+ """The behavior of cover pages."""
+
class Config(BaseConfig):
"""The plugin's configuration."""
diff --git a/mkdocs_exporter/formats/pdf/plugin.py b/mkdocs_exporter/formats/pdf/plugin.py
index 0ec422f..6109ae0 100644
--- a/mkdocs_exporter/formats/pdf/plugin.py
+++ b/mkdocs_exporter/formats/pdf/plugin.py
@@ -63,15 +63,20 @@ def on_page_markdown(self, markdown: str, page: Page, config: Config, **kwargs)
content = markdown
covers = {**self.config.covers, **{k: os.path.join(os.path.dirname(config['config_file_path']), v) for k, v in page.meta.get('covers', {}).items()}}
+ for path in [path for path in covers.values() if path is not None]:
+ self.watch.append(path)
+
if covers.get('front'):
+ page.formats['pdf']['covers'].append('front')
+
with open(covers['front'], 'r', encoding='utf-8') as file:
- content = self.renderer.cover(file.read()) + content
+ content = self.renderer.cover(file.read(), 'front') + content
+
if covers.get('back'):
- with open(covers['back'], 'r', encoding='utf-8') as file:
- content = content + self.renderer.cover(file.read())
+ page.formats['pdf']['covers'].append('back')
- for path in [path for path in covers.values() if path is not None]:
- self.watch.append(path)
+ with open(covers['back'], 'r', encoding='utf-8') as file:
+ content = content + self.renderer.cover(file.read(), 'back')
return content
@@ -85,11 +90,10 @@ def on_pre_build(self, **kwargs) -> None:
self.loop = asyncio.new_event_loop()
self.renderer = Renderer(options=self.config)
- if self.config.aggregator.get('enabled'):
- self.aggregator = Aggregator(renderer=self.renderer)
-
asyncio.set_event_loop(self.loop)
+ if self.config.aggregator.get('enabled'):
+ self.aggregator = Aggregator(renderer=self.renderer, config=self.config.get('aggregator'))
for stylesheet in self.config.stylesheets:
self.renderer.add_stylesheet(stylesheet)
for script in self.config.scripts:
@@ -108,7 +112,10 @@ def on_pre_page(self, page: Page, config: dict, **kwargs):
fullpath = os.path.join(directory, filename)
page.formats['pdf'] = {
+ 'pages': 0,
+ 'covers': [],
'path': fullpath,
+ 'skipped_pages': 0,
'url': os.path.relpath(fullpath, config['site_dir'])
}
@@ -132,9 +139,6 @@ async def render(page: Page) -> None:
with open(page.formats['pdf']['path'], 'wb+') as file:
file.write(pdf)
- if self.aggregator:
- self.aggregator.increment_total_pages(pages)
-
self.tasks.append(render(page))
return page.html
@@ -160,17 +164,19 @@ def _on_post_build_2(self, config: dict, **kwargs) -> None:
output = self.config['aggregator']['output']
self.pages = [page for page in self.pages if 'pdf' in page.formats]
- logger.info("[mkdocs-exporter.pdf] Aggregating %d pages from %d documents together as '%s'...", self.aggregator.total_pages, len(self.pages), output)
+ self.aggregator.set_pages(self.pages)
- async def render(page: Page, page_number: int) -> None:
- html = self.aggregator.preprocess(self.renderer.preprocess(page), page_number=page_number)
+ logger.info("[mkdocs-exporter.pdf] Aggregating pages to '%s'...", output)
+
+ async def render(page: Page) -> None:
+ html = self.aggregator.preprocess(page)
pdf, _ = await self.renderer.render(html)
with open(page.formats['pdf']['path'] + '.aggregate', 'wb+') as file:
file.write(pdf)
- for n, page in enumerate(self.pages):
- self.tasks.append(render(page, page_number=sum(page.formats['pdf']['pages'] for page in self.pages[:n])))
+ for page in self.pages:
+ self.tasks.append(render(page))
while self.tasks:
self.loop.run_until_complete(asyncio.gather(*concurrently(self.tasks, max(1, self.config.concurrency or 1))))
@@ -217,6 +223,9 @@ def flatten(items):
self.pages = flatten(nav)
+ for index, page in enumerate(self.pages):
+ page.index = index
+
def _enabled(self, page: Page = None) -> bool:
"""Is the plugin enabled for this page?"""
diff --git a/mkdocs_exporter/formats/pdf/renderer.py b/mkdocs_exporter/formats/pdf/renderer.py
index 51f715c..ee59593 100644
--- a/mkdocs_exporter/formats/pdf/renderer.py
+++ b/mkdocs_exporter/formats/pdf/renderer.py
@@ -41,15 +41,15 @@ def add_script(self, path: str) -> Renderer:
return self
- def cover(self, template: str) -> Renderer:
+ def cover(self, template: str, location: str) -> Renderer:
"""Renders a cover."""
content = template.strip('\n')
- return f'{content}
' + '\n'
+ return f'{content}
' + '\n'
- def preprocess(self, page: Page) -> str:
+ def preprocess(self, page: Page, disable: list = []) -> str:
"""Preprocesses a page, returning HTML that can be printed."""
preprocessor = Preprocessor(theme=page.theme)
@@ -67,9 +67,11 @@ def preprocess(self, page: Page) -> str:
with open(script, 'r', encoding='utf-8') as file:
preprocessor.script(file.read(), path=stylesheet)
+ if 'teleport' not in disable:
+ preprocessor.teleport()
+
preprocessor.script(importlib.resources.files(js).joinpath('pdf.js').read_text(encoding='utf-8'))
preprocessor.script(importlib.resources.files(js).joinpath('pagedjs.min.js').read_text(encoding='utf-8'))
- preprocessor.teleport()
preprocessor.update_links(base, root)
if self.options.get('url'):
diff --git a/mkdocs_exporter/formats/pdf/resources/js/pdf.js b/mkdocs_exporter/formats/pdf/resources/js/pdf.js
index e66328e..5cddf72 100644
--- a/mkdocs_exporter/formats/pdf/resources/js/pdf.js
+++ b/mkdocs_exporter/formats/pdf/resources/js/pdf.js
@@ -3,6 +3,7 @@ window.PagedConfig = {
/**
* The settings.
*/
+ auto: true,
settings: {
maxChars: 1e32
},
diff --git a/mkdocs_exporter/page.py b/mkdocs_exporter/page.py
index abdaeb2..a057c51 100644
--- a/mkdocs_exporter/page.py
+++ b/mkdocs_exporter/page.py
@@ -7,6 +7,9 @@
class Page(BasePage):
"""A page."""
+ index: int
+ """The page's index."""
+
html: Optional[str] = None
"""The page's HTML content."""
diff --git a/mkdocs_exporter/resources/css/material.css b/mkdocs_exporter/resources/css/material.css
new file mode 100644
index 0000000..4482822
--- /dev/null
+++ b/mkdocs_exporter/resources/css/material.css
@@ -0,0 +1,6 @@
+/**
+ * @see https://github.com/adrienbrignon/mkdocs-exporter/issues/26
+ */
+.highlight span.filename+pre {
+ margin-top: 0 !important;
+}
diff --git a/mkdocs_exporter/themes/material/theme.py b/mkdocs_exporter/themes/material/theme.py
index 6f75c49..4e22c9b 100644
--- a/mkdocs_exporter/themes/material/theme.py
+++ b/mkdocs_exporter/themes/material/theme.py
@@ -1,8 +1,11 @@
from __future__ import annotations
+import importlib
+
from bs4 import BeautifulSoup
+from mkdocs_exporter.resources import css
from mkdocs_exporter.theme import Theme as BaseTheme
from mkdocs_exporter.preprocessor import Preprocessor
from mkdocs_exporter.themes.material.icons import get_icon
@@ -19,6 +22,7 @@ def preprocess(self, preprocessor: Preprocessor) -> None:
"""Preprocesses the DOM before rendering a document."""
preprocessor.remove(['.md-sidebar.md-sidebar--primary', '.md-sidebar.md-sidebar--secondary', 'header.md-header', '.md-container > nav', 'nav.md-tags'])
+ preprocessor.stylesheet(importlib.resources.files(css).joinpath('material.css').read_text(encoding='utf-8'))
def button(self, preprocessor: Preprocessor, title: str, icon: str, attributes: dict = {}):
diff --git a/poetry.lock b/poetry.lock
index 1c86837..950b8e1 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -14,6 +14,30 @@ files = [
[package.extras]
dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
+[[package]]
+name = "bandit"
+version = "1.7.9"
+description = "Security oriented static analyser for python code."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec"},
+ {file = "bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61"},
+]
+
+[package.dependencies]
+colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""}
+PyYAML = ">=5.3.1"
+rich = "*"
+stevedore = ">=1.20.0"
+
+[package.extras]
+baseline = ["GitPython (>=3.1.30)"]
+sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"]
+test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"]
+toml = ["tomli (>=1.1.0)"]
+yaml = ["PyYAML"]
+
[[package]]
name = "beautifulsoup4"
version = "4.12.3"
@@ -48,13 +72,13 @@ files = [
[[package]]
name = "cairocffi"
-version = "1.7.0"
+version = "1.7.1"
description = "cffi-based cairo bindings for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "cairocffi-1.7.0-py3-none-any.whl", hash = "sha256:1f29a8d41dbda4090c0aa33bcdea64f3b493e95f74a43ea107c4a8a7b7f632ef"},
- {file = "cairocffi-1.7.0.tar.gz", hash = "sha256:7761863603894305f3160eca68452f373433ca8745ab7dd445bd2c6ce50dcab7"},
+ {file = "cairocffi-1.7.1-py3-none-any.whl", hash = "sha256:9803a0e11f6c962f3b0ae2ec8ba6ae45e957a146a004697a1ac1bbf16b073b3f"},
+ {file = "cairocffi-1.7.1.tar.gz", hash = "sha256:2e48ee864884ec4a3a34bfa8c9ab9999f688286eb714a15a43ec9d068c36557b"},
]
[package.dependencies]
@@ -328,18 +352,18 @@ files = [
[[package]]
name = "flake8"
-version = "7.0.0"
+version = "7.1.0"
description = "the modular source code checker: pep8 pyflakes and co"
optional = false
python-versions = ">=3.8.1"
files = [
- {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"},
- {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"},
+ {file = "flake8-7.1.0-py2.py3-none-any.whl", hash = "sha256:2e416edcc62471a64cea09353f4e7bdba32aeb079b6e360554c659a122b1bc6a"},
+ {file = "flake8-7.1.0.tar.gz", hash = "sha256:48a07b626b55236e0fb4784ee69a465fbf59d79eec1f5b4785c3d3bc57d17aa5"},
]
[package.dependencies]
mccabe = ">=0.7.0,<0.8.0"
-pycodestyle = ">=2.11.0,<2.12.0"
+pycodestyle = ">=2.12.0,<2.13.0"
pyflakes = ">=3.2.0,<3.3.0"
[[package]]
@@ -464,13 +488,13 @@ test = ["objgraph", "psutil"]
[[package]]
name = "griffe"
-version = "0.45.2"
+version = "0.47.0"
description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API."
optional = false
python-versions = ">=3.8"
files = [
- {file = "griffe-0.45.2-py3-none-any.whl", hash = "sha256:297ec8530d0c68e5b98ff86fb588ebc3aa3559bb5dc21f3caea8d9542a350133"},
- {file = "griffe-0.45.2.tar.gz", hash = "sha256:83ce7dcaafd8cb7f43cbf1a455155015a1eb624b1ffd93249e5e1c4a22b2fdb2"},
+ {file = "griffe-0.47.0-py3-none-any.whl", hash = "sha256:07a2fd6a8c3d21d0bbb0decf701d62042ccc8a576645c7f8799fe1f10de2b2de"},
+ {file = "griffe-0.47.0.tar.gz", hash = "sha256:95119a440a3c932b13293538bdbc405bee4c36428547553dc6b327e7e7d35e5a"},
]
[package.dependencies]
@@ -499,22 +523,22 @@ files = [
[[package]]
name = "importlib-metadata"
-version = "7.1.0"
+version = "7.2.0"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
files = [
- {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"},
- {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"},
+ {file = "importlib_metadata-7.2.0-py3-none-any.whl", hash = "sha256:04e4aad329b8b948a5711d394fa8759cb80f009225441b4f2a02bd4d8e5f426c"},
+ {file = "importlib_metadata-7.2.0.tar.gz", hash = "sha256:3ff4519071ed42740522d494d04819b666541b9752c43012f85afb2cc220fcc6"},
]
[package.dependencies]
zipp = ">=0.5"
[package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
+test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
[[package]]
name = "jinja2"
@@ -734,6 +758,30 @@ importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"]
testing = ["coverage", "pyyaml"]
+[[package]]
+name = "markdown-it-py"
+version = "3.0.0"
+description = "Python port of markdown-it. Markdown parsing, done right!"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
+ {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
+]
+
+[package.dependencies]
+mdurl = ">=0.1,<1.0"
+
+[package.extras]
+benchmarking = ["psutil", "pytest", "pytest-benchmark"]
+code-style = ["pre-commit (>=3.0,<4.0)"]
+compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
+linkify = ["linkify-it-py (>=1,<3)"]
+plugins = ["mdit-py-plugins"]
+profiling = ["gprof2dot"]
+rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
+testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
+
[[package]]
name = "markupsafe"
version = "2.1.5"
@@ -814,6 +862,17 @@ files = [
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+description = "Markdown URL utilities"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
+ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
+]
+
[[package]]
name = "mdx-truly-sane-lists"
version = "1.3"
@@ -920,18 +979,20 @@ platformdirs = ">=2.2.0"
pyyaml = ">=5.1"
[[package]]
-name = "mkdocs-git-authors-plugin"
-version = "0.7.2"
-description = "Mkdocs plugin to display git authors of a page"
+name = "mkdocs-git-committers-plugin-2"
+version = "2.3.0"
+description = "An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of GitHub or GitLab committers and other useful GIT info such as last modified date"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8,<4"
files = [
- {file = "mkdocs-git-authors-plugin-0.7.2.tar.gz", hash = "sha256:f541730e4cabdafa0ac758c94d28ba5e8ddca4c859e5de4c89f1226cb6ccd0ad"},
- {file = "mkdocs_git_authors_plugin-0.7.2-py3-none-any.whl", hash = "sha256:c8a2784a867db79ad3b477a96ee96875d17b09192b6d3be71f08df25afff76c4"},
+ {file = "mkdocs-git-committers-plugin-2-2.3.0.tar.gz", hash = "sha256:d6baca1ae04db8120640038eda8142f2d081c27b53f3b566c83c75717e4ed81a"},
+ {file = "mkdocs_git_committers_plugin_2-2.3.0-py3-none-any.whl", hash = "sha256:7b3434af3be525c12858eb3b44b4c6b695b7c7b7760482ea8de1c6e292e84f0f"},
]
[package.dependencies]
-mkdocs = ">=1.0"
+gitpython = "*"
+mkdocs = ">=1.0.3"
+requests = "*"
[[package]]
name = "mkdocs-git-revision-date-localized-plugin"
@@ -973,13 +1034,13 @@ test = ["mkdocs-include-markdown-plugin", "mkdocs-macros-test", "mkdocs-material
[[package]]
name = "mkdocs-material"
-version = "9.5.26"
+version = "9.5.27"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocs_material-9.5.26-py3-none-any.whl", hash = "sha256:5d01fb0aa1c7946a1e3ae8689aa2b11a030621ecb54894e35aabb74c21016312"},
- {file = "mkdocs_material-9.5.26.tar.gz", hash = "sha256:56aeb91d94cffa43b6296fa4fbf0eb7c840136e563eecfd12c2d9e92e50ba326"},
+ {file = "mkdocs_material-9.5.27-py3-none-any.whl", hash = "sha256:af8cc263fafa98bb79e9e15a8c966204abf15164987569bd1175fd66a7705182"},
+ {file = "mkdocs_material-9.5.27.tar.gz", hash = "sha256:a7d4a35f6d4a62b0c43a0cfe7e987da0980c13587b5bc3c26e690ad494427ec0"},
]
[package.dependencies]
@@ -1079,17 +1140,17 @@ python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"]
[[package]]
name = "mkdocstrings-python"
-version = "1.10.3"
+version = "1.10.5"
description = "A Python handler for mkdocstrings."
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocstrings_python-1.10.3-py3-none-any.whl", hash = "sha256:11ff6d21d3818fb03af82c3ea6225b1534837e17f790aa5f09626524171f949b"},
- {file = "mkdocstrings_python-1.10.3.tar.gz", hash = "sha256:321cf9c732907ab2b1fedaafa28765eaa089d89320f35f7206d00ea266889d03"},
+ {file = "mkdocstrings_python-1.10.5-py3-none-any.whl", hash = "sha256:92e3c588ef1b41151f55281d075de7558dd8092e422cb07a65b18ee2b0863ebb"},
+ {file = "mkdocstrings_python-1.10.5.tar.gz", hash = "sha256:acdc2a98cd9d46c7ece508193a16ca03ccabcb67520352b7449f84b57c162bdf"},
]
[package.dependencies]
-griffe = ">=0.44"
+griffe = ">=0.47"
mkdocstrings = ">=0.25"
[[package]]
@@ -1109,13 +1170,13 @@ icu = ["PyICU (>=1.0.0)"]
[[package]]
name = "packaging"
-version = "24.0"
+version = "24.1"
description = "Core utilities for Python packages"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
- {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
+ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
+ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
]
[[package]]
@@ -1139,6 +1200,17 @@ files = [
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
]
+[[package]]
+name = "pbr"
+version = "6.0.0"
+description = "Python Build Reasonableness"
+optional = false
+python-versions = ">=2.6"
+files = [
+ {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"},
+ {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"},
+]
+
[[package]]
name = "pillow"
version = "10.3.0"
@@ -1263,13 +1335,13 @@ pyee = "11.1.0"
[[package]]
name = "pycodestyle"
-version = "2.11.1"
+version = "2.12.0"
description = "Python style guide checker"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"},
- {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"},
+ {file = "pycodestyle-2.12.0-py2.py3-none-any.whl", hash = "sha256:949a39f6b86c3e1515ba1787c2022131d165a8ad271b11370a8819aa070269e4"},
+ {file = "pycodestyle-2.12.0.tar.gz", hash = "sha256:442f950141b4f43df752dd303511ffded3a04c2b6fb7f65980574f0c31e6e79c"},
]
[[package]]
@@ -1571,6 +1643,24 @@ urllib3 = ">=1.21.1,<3"
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
+[[package]]
+name = "rich"
+version = "13.7.1"
+description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
+optional = false
+python-versions = ">=3.7.0"
+files = [
+ {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"},
+ {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"},
+]
+
+[package.dependencies]
+markdown-it-py = ">=2.2.0"
+pygments = ">=2.13.0,<3.0.0"
+
+[package.extras]
+jupyter = ["ipywidgets (>=7.5.1,<9)"]
+
[[package]]
name = "six"
version = "1.16.0"
@@ -1604,6 +1694,20 @@ files = [
{file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
]
+[[package]]
+name = "stevedore"
+version = "5.2.0"
+description = "Manage dynamic plugins for Python applications"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"},
+ {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"},
+]
+
+[package.dependencies]
+pbr = ">=2.0.0,<2.1.0 || >2.1.0"
+
[[package]]
name = "termcolor"
version = "2.4.0"
@@ -1649,13 +1753,13 @@ files = [
[[package]]
name = "urllib3"
-version = "2.2.1"
+version = "2.2.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.8"
files = [
- {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
- {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
+ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
+ {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
]
[package.extras]
@@ -1750,5 +1854,5 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools",
[metadata]
lock-version = "2.0"
-python-versions = ">=3.9"
-content-hash = "2d93295f86f376ca2d37dd139753959bd4cab9ac10dbea82d089af8ea5ec9c48"
+python-versions = ">=3.9,<4"
+content-hash = "5906c8eea00f369c650f25795b5a29336846d1d09c8a6d4db01c0ff870281fa7"
diff --git a/pyproject.toml b/pyproject.toml
index b4d69ab..a002229 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "mkdocs-exporter"
-version = "6.0.0"
+version = "6.1.0"
repository = "https://github.com/adrienbrignon/mkdocs-exporter"
keywords = ["mkdocs", "pdf", "exporter"]
description = "A highly-configurable plugin for MkDocs that exports your pages to PDF files."
@@ -20,7 +20,7 @@ classifiers = [
]
[tool.poetry.dependencies]
-python = ">=3.9"
+python = ">=3.9,<4"
mkdocs = ">=1.4"
playwright = ">=1.33"
beautifulsoup4 = ">=4.12.2"
@@ -38,10 +38,11 @@ pypdf = ">=4.2.0"
flake8 = ">=7.0"
mkdocs-material = {version="^9.4.11", extras=["imaging"]}
mkdocs-git-revision-date-localized-plugin = "^1.2.0"
-mkdocs-git-authors-plugin = "^0.7.0"
mkdocs-awesome-pages-plugin = "^2.9.1"
mkdocs-macros-plugin = "^1.0.4"
mkdocs-minify-plugin = "^0.6.4"
mkdocs-redirects = "^1.2.0"
mdx-truly-sane-lists = "^1.3"
mkdocstrings = {extras = ["python"], version = "^0.25.1"}
+mkdocs-git-committers-plugin-2 = "^2.3.0"
+bandit = "^1.7.9"