From 4ae05dced52685a617c7006e950d3a2acdf7ffe9 Mon Sep 17 00:00:00 2001 From: Nathaniel Clark Date: Mon, 8 Jul 2024 14:10:30 -0400 Subject: [PATCH 01/10] Added option 'mermaid_args' to be able to specify arguments sent to mermaid-cli. Also expanded mermaid HTML element detection. I used PyMdown Extension SuperFences for my Mermaid diagrams in MkDocs, so my HTML is still the old `
` blocks. Therefore, I used regex to detect any HTML block with the `mermaid` or `language-mermaid` class with at most one child HTML element. --- mkdocs_with_pdf/drivers/headless_chrome.py | 38 ++++++++++++++-------- mkdocs_with_pdf/options.py | 3 +- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/mkdocs_with_pdf/drivers/headless_chrome.py b/mkdocs_with_pdf/drivers/headless_chrome.py index 6f4debbc..aed3259c 100644 --- a/mkdocs_with_pdf/drivers/headless_chrome.py +++ b/mkdocs_with_pdf/drivers/headless_chrome.py @@ -11,25 +11,29 @@ class HeadlessChromeDriver(object): """ 'Headless Chrome' executor """ @classmethod - def setup(self, program_path: str, logger: Logger): + def setup(self, program_path: str, mermaid_args: str, logger: Logger): if not which(program_path): raise RuntimeError( 'No such `Headless Chrome` program or not executable' + f': "{program_path}".') - return self(program_path, logger) + return self(program_path, mermaid_args, logger) - def __init__(self, program_path: str, logger: Logger): + def __init__(self, program_path: str, mermaid_args: str, logger: Logger): self._program_path = program_path + self.mermaid_args = mermaid_args self._logger = logger def render(self, html: str, temporary_directory: pathlib.Path) -> str: try: - mermaid_regex = r'
(.*?)
' - mermaid_matches = re.findall(mermaid_regex, html, flags=re.DOTALL) + mermaid_regex = re.compile(r'<(\w*?[^>]*)(><[^>]*?|[^>]*?)class="(language-)?mermaid">(<[^>]*?>)?(?P.*?)(<\/[^>]*?>)?<\/\1>', flags=re.DOTALL) + mermaid_matches = mermaid_regex.finditer(html) + i = 0 # Convert each Mermaid diagram to an image. - for i, mermaid_code in enumerate(mermaid_matches): + for mermaid_block in mermaid_matches: + i += 1 self._logger.info(f"Converting mermaid diagram {i}") + mermaid_code = mermaid_block.group("code") # Create a temporary file to hold the Mermaid code. mermaid_file_path = temporary_directory / f"diagram_{i + 1}.mmd" @@ -38,18 +42,26 @@ def render(self, html: str, temporary_directory: pathlib.Path) -> str: mermaid_file.write(mermaid_code_unescaped.encode("utf-8")) # Create a filename for the image. - image_file_path = temporary_directory / f"diagram_{i + 1}.png" + image_file_path = temporary_directory / f"diagram_{i}.png" # Convert the Mermaid diagram to an image using mmdc. - command = f"mmdc -i {mermaid_file_path} -o {image_file_path} -b transparent -t dark --scale 4 --quiet" + command = f"mmdc -i {mermaid_file_path} -o {image_file_path} {self.mermaid_args}" + + # suppress sub-process chatter when using '--quiet' + if self.mermaid_args.find('--quiet') > -1 or self.mermaid_args.find(' -q ') > -1 or self.mermaid_args.endswith(' -q'): + command += " >/dev/null 2>&1" os.system(command) - # Replace the Mermaid code with the image in the HTML string. - image_html = f'Mermaid diagram {i+1}' - html = html.replace(f'
{mermaid_code}
', image_html) + if not os.path.exists(image_file_path): + self._logger.warning(f"Error: Failed to generate mermaid diagram {i}") + else: + # Replace the Mermaid code with the image in the HTML string. + image_html = f'Mermaid diagram {i}' + html = html.replace(mermaid_block.group(0), + mermaid_block.group(0).replace(mermaid_code, image_html)) - self._logger.info(f"Post mermaid translation: {html}") + self._logger.debug(f"Post mermaid translation: {html}") with open(temporary_directory / "post_mermaid_translation.html", "wb") as temp: temp.write(html.encode('utf-8')) @@ -66,7 +78,7 @@ def render(self, html: str, temporary_directory: pathlib.Path) -> str: '--dump-dom', temp.name], stdout=PIPE) as chrome: chrome_output = chrome.stdout.read().decode('utf-8') - self._logger.info(f"Post chrome translation: {chrome_output}") + self._logger.debug(f"Post chrome translation: {chrome_output}") return chrome_output except Exception as e: diff --git a/mkdocs_with_pdf/options.py b/mkdocs_with_pdf/options.py index 8296786e..09a47fc4 100644 --- a/mkdocs_with_pdf/options.py +++ b/mkdocs_with_pdf/options.py @@ -42,6 +42,7 @@ class Options(object): ('two_columns_level', config_options.Type(int, default=0)), ('render_js', config_options.Type(bool, default=False)), + ('mermaid_args', config_options.Type(str, default="-b transparent -t dark --scale 4 --quiet")), ('headless_chrome_path', config_options.Type(str, default='chromium-browser')), ('relaxedjs_path', @@ -96,7 +97,7 @@ def __init__(self, local_config, config, logger: logging): self.js_renderer = None if local_config['render_js']: self.js_renderer = HeadlessChromeDriver.setup( - local_config['headless_chrome_path'], logger) + local_config['headless_chrome_path'], local_config['mermaid_args'], logger) self.relaxed_js = RelaxedJSRenderer.setup( local_config['relaxedjs_path'], logger) From d4dd1962a5ada9443a5de21508437508b710d762 Mon Sep 17 00:00:00 2001 From: Nathaniel Clark Date: Mon, 8 Jul 2024 14:40:01 -0400 Subject: [PATCH 02/10] Allow that `mermaid` may not be the only class assigned to the HTML element --- mkdocs_with_pdf/drivers/headless_chrome.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs_with_pdf/drivers/headless_chrome.py b/mkdocs_with_pdf/drivers/headless_chrome.py index aed3259c..f58951d0 100644 --- a/mkdocs_with_pdf/drivers/headless_chrome.py +++ b/mkdocs_with_pdf/drivers/headless_chrome.py @@ -25,7 +25,7 @@ def __init__(self, program_path: str, mermaid_args: str, logger: Logger): def render(self, html: str, temporary_directory: pathlib.Path) -> str: try: - mermaid_regex = re.compile(r'<(\w*?[^>]*)(><[^>]*?|[^>]*?)class="(language-)?mermaid">(<[^>]*?>)?(?P.*?)(<\/[^>]*?>)?<\/\1>', flags=re.DOTALL) + mermaid_regex = re.compile(r'<(\w*?[^>]*)(><[^>]*?|[^>]*?)class="[^>\"]*(language-)?mermaid[^>\"]*">(<[^>]*?>)?(?P.*?)(<\/[^>]*?>)?<\/\1>', flags=re.DOTALL) mermaid_matches = mermaid_regex.finditer(html) i = 0 From 8d77db0cb207f29b1eaeab03e4fe255816aa8095 Mon Sep 17 00:00:00 2001 From: Nathaniel Clark Date: Tue, 9 Jul 2024 09:07:12 -0400 Subject: [PATCH 03/10] Added option to reduce visual scale of diagrams mermaid_img_scale_reduction uses CSS height and width to scale down the visual size of rendered mermaid diagrams. This is a direct counter to puppeteer scale fed to mermaid-cli so that setting both to the same integer larger than 1 will result in a higher resolution diagram at the natural 'scale 1' visual size. --- mkdocs_with_pdf/drivers/headless_chrome.py | 35 ++++++++++++++++------ mkdocs_with_pdf/options.py | 6 +++- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/mkdocs_with_pdf/drivers/headless_chrome.py b/mkdocs_with_pdf/drivers/headless_chrome.py index f58951d0..e9112839 100644 --- a/mkdocs_with_pdf/drivers/headless_chrome.py +++ b/mkdocs_with_pdf/drivers/headless_chrome.py @@ -11,16 +11,27 @@ class HeadlessChromeDriver(object): """ 'Headless Chrome' executor """ @classmethod - def setup(self, program_path: str, mermaid_args: str, logger: Logger): + def setup(self, + program_path: str, + mermaid_args: str, + mermaid_img_scale_reduction: int, + logger: Logger): if not which(program_path): raise RuntimeError( 'No such `Headless Chrome` program or not executable' + f': "{program_path}".') - return self(program_path, mermaid_args, logger) - - def __init__(self, program_path: str, mermaid_args: str, logger: Logger): + return self(program_path, + mermaid_args, + mermaid_img_scale_reduction, + logger) + + def __init__(self, program_path: str, + mermaid_args: str, + mermaid_img_scale_reduction: int, + logger: Logger): self._program_path = program_path self.mermaid_args = mermaid_args + self.mermaid_img_scale_reduction = mermaid_img_scale_reduction self._logger = logger def render(self, html: str, temporary_directory: pathlib.Path) -> str: @@ -56,10 +67,16 @@ def render(self, html: str, temporary_directory: pathlib.Path) -> str: if not os.path.exists(image_file_path): self._logger.warning(f"Error: Failed to generate mermaid diagram {i}") else: - # Replace the Mermaid code with the image in the HTML string. - image_html = f'Mermaid diagram {i}' - html = html.replace(mermaid_block.group(0), - mermaid_block.group(0).replace(mermaid_code, image_html)) + from PIL import Image + + with Image.open(image_file_path) as im: + height = im.height // self.mermaid_img_scale_reduction + width = im.width // self.mermaid_img_scale_reduction + + # Replace the Mermaid code with the image in the HTML string. + image_html = f'Mermaid diagram {i}' + html = html.replace(mermaid_block.group(0), + mermaid_block.group(0).replace(mermaid_code, image_html)) self._logger.debug(f"Post mermaid translation: {html}") with open(temporary_directory / "post_mermaid_translation.html", "wb") as temp: @@ -78,7 +95,7 @@ def render(self, html: str, temporary_directory: pathlib.Path) -> str: '--dump-dom', temp.name], stdout=PIPE) as chrome: chrome_output = chrome.stdout.read().decode('utf-8') - self._logger.debug(f"Post chrome translation: {chrome_output}") + self._logger.info(f"Post chrome translation: {chrome_output}") return chrome_output except Exception as e: diff --git a/mkdocs_with_pdf/options.py b/mkdocs_with_pdf/options.py index 09a47fc4..aa41bfc1 100644 --- a/mkdocs_with_pdf/options.py +++ b/mkdocs_with_pdf/options.py @@ -43,6 +43,7 @@ class Options(object): ('render_js', config_options.Type(bool, default=False)), ('mermaid_args', config_options.Type(str, default="-b transparent -t dark --scale 4 --quiet")), + ('mermaid_img_scale_reduction', config_options.Type(int, default=1)), ('headless_chrome_path', config_options.Type(str, default='chromium-browser')), ('relaxedjs_path', @@ -97,7 +98,10 @@ def __init__(self, local_config, config, logger: logging): self.js_renderer = None if local_config['render_js']: self.js_renderer = HeadlessChromeDriver.setup( - local_config['headless_chrome_path'], local_config['mermaid_args'], logger) + local_config['headless_chrome_path'], + local_config['mermaid_args'], + local_config['mermaid_img_scale_reduction'], + logger) self.relaxed_js = RelaxedJSRenderer.setup( local_config['relaxedjs_path'], logger) From 5ad3170745e08a200ab2754ac7732fdff17c15a1 Mon Sep 17 00:00:00 2001 From: Nathaniel Clark Date: Tue, 9 Jul 2024 09:21:20 -0400 Subject: [PATCH 04/10] Documenting mermaid arguments in readme --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index ed3b65ea..78a8b25e 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,8 @@ plugins: # #render_js: true #headless_chrome_path: headless-chromium + #mermaid_args: '-b transparent -t dark --scale 4 --quiet' + #mermaid_img_scale_reduction: 4 # #output_path: any-place/document.pdf #enabled_if_env: ENABLE_PDF_EXPORT @@ -245,6 +247,17 @@ plugins: > > ``` +* `mermaid_args` + + Arguments to use when calling `mmdc` to generate mermaid diagrams + + **default**: '-b transparent -t dark --scale 4 --quiet' + +* `mermaid_img_scale_reduction` + + Visual scale to reduce visual size of diagrams when using `--scale` greater than 1 in `mermaid_args`. + This allows higher resolution diagram renders at the native visual size. + * `relaxedjs_path` Set the value to execute command of relaxed if you're using e.g. '[Mermaid](https://mermaid-js.github.io) diagrams and Headless Chrome is not working for you. From d91d96e8ea379b137740a1f8dd609d58963f4859 Mon Sep 17 00:00:00 2001 From: Nathaniel Clark Date: Wed, 10 Jul 2024 12:19:46 -0400 Subject: [PATCH 05/10] Adding Mermaid rendering to RelaxedJS --- mkdocs_with_pdf/drivers/headless_chrome.py | 58 ++++----------------- mkdocs_with_pdf/drivers/relaxedjs.py | 36 +++++++++++-- mkdocs_with_pdf/generator.py | 4 +- mkdocs_with_pdf/options.py | 7 ++- mkdocs_with_pdf/utils/mermaid_util.py | 60 ++++++++++++++++++++++ 5 files changed, 108 insertions(+), 57 deletions(-) create mode 100644 mkdocs_with_pdf/utils/mermaid_util.py diff --git a/mkdocs_with_pdf/drivers/headless_chrome.py b/mkdocs_with_pdf/drivers/headless_chrome.py index e9112839..c893e847 100644 --- a/mkdocs_with_pdf/drivers/headless_chrome.py +++ b/mkdocs_with_pdf/drivers/headless_chrome.py @@ -1,9 +1,7 @@ -import os -import html as html_lib +from ..utils.mermaid_util import render_mermaid from logging import Logger from shutil import which from subprocess import PIPE, Popen -import re import pathlib @@ -14,7 +12,7 @@ class HeadlessChromeDriver(object): def setup(self, program_path: str, mermaid_args: str, - mermaid_img_scale_reduction: int, + mermaid_img_scale_reduction: float, logger: Logger): if not which(program_path): raise RuntimeError( @@ -27,7 +25,7 @@ def setup(self, def __init__(self, program_path: str, mermaid_args: str, - mermaid_img_scale_reduction: int, + mermaid_img_scale_reduction: float, logger: Logger): self._program_path = program_path self.mermaid_args = mermaid_args @@ -36,47 +34,12 @@ def __init__(self, program_path: str, def render(self, html: str, temporary_directory: pathlib.Path) -> str: try: - mermaid_regex = re.compile(r'<(\w*?[^>]*)(><[^>]*?|[^>]*?)class="[^>\"]*(language-)?mermaid[^>\"]*">(<[^>]*?>)?(?P.*?)(<\/[^>]*?>)?<\/\1>', flags=re.DOTALL) - mermaid_matches = mermaid_regex.finditer(html) - - i = 0 - # Convert each Mermaid diagram to an image. - for mermaid_block in mermaid_matches: - i += 1 - self._logger.info(f"Converting mermaid diagram {i}") - mermaid_code = mermaid_block.group("code") - - # Create a temporary file to hold the Mermaid code. - mermaid_file_path = temporary_directory / f"diagram_{i + 1}.mmd" - with open(mermaid_file_path, "wb") as mermaid_file: - mermaid_code_unescaped = html_lib.unescape(mermaid_code) - mermaid_file.write(mermaid_code_unescaped.encode("utf-8")) - - # Create a filename for the image. - image_file_path = temporary_directory / f"diagram_{i}.png" - - # Convert the Mermaid diagram to an image using mmdc. - command = f"mmdc -i {mermaid_file_path} -o {image_file_path} {self.mermaid_args}" - - # suppress sub-process chatter when using '--quiet' - if self.mermaid_args.find('--quiet') > -1 or self.mermaid_args.find(' -q ') > -1 or self.mermaid_args.endswith(' -q'): - command += " >/dev/null 2>&1" - - os.system(command) - - if not os.path.exists(image_file_path): - self._logger.warning(f"Error: Failed to generate mermaid diagram {i}") - else: - from PIL import Image - - with Image.open(image_file_path) as im: - height = im.height // self.mermaid_img_scale_reduction - width = im.width // self.mermaid_img_scale_reduction - - # Replace the Mermaid code with the image in the HTML string. - image_html = f'Mermaid diagram {i}' - html = html.replace(mermaid_block.group(0), - mermaid_block.group(0).replace(mermaid_code, image_html)) + html = render_mermaid( + html, + temporary_directory, + self.mermaid_args, + self.mermaid_img_scale_reduction, + self._logger) self._logger.debug(f"Post mermaid translation: {html}") with open(temporary_directory / "post_mermaid_translation.html", "wb") as temp: @@ -88,14 +51,13 @@ def render(self, html: str, temporary_directory: pathlib.Path) -> str: '--no-sandbox', '--headless', '--disable-gpu', - '--disable-web-security', '-–allow-file-access-from-files', '--run-all-compositor-stages-before-draw', '--virtual-time-budget=10000', '--dump-dom', temp.name], stdout=PIPE) as chrome: chrome_output = chrome.stdout.read().decode('utf-8') - self._logger.info(f"Post chrome translation: {chrome_output}") + self._logger.debug(f"Post chrome translation: {chrome_output}") return chrome_output except Exception as e: diff --git a/mkdocs_with_pdf/drivers/relaxedjs.py b/mkdocs_with_pdf/drivers/relaxedjs.py index a0177ad2..780516fd 100644 --- a/mkdocs_with_pdf/drivers/relaxedjs.py +++ b/mkdocs_with_pdf/drivers/relaxedjs.py @@ -1,14 +1,21 @@ import os +from ..utils.mermaid_util import render_mermaid from logging import Logger +import shutil from shutil import which from subprocess import PIPE, Popen from tempfile import TemporaryDirectory +import pathlib class RelaxedJSRenderer(object): @classmethod - def setup(self, program_path: str, logger: Logger): + def setup(self, + program_path: str, + mermaid_args: str, + mermaid_img_scale_reduction: float, + logger: Logger): if not program_path: return None @@ -17,16 +24,33 @@ def setup(self, program_path: str, logger: Logger): 'No such `ReLaXed` program or not executable' + f': "{program_path}".') - return self(program_path, logger) + return self(program_path, + mermaid_args, + mermaid_img_scale_reduction, + logger) - def __init__(self, program_path: str, logger: Logger): + def __init__(self, program_path: str, + mermaid_args: str, + mermaid_img_scale_reduction: float, + logger: Logger): self._program_path = program_path + self.mermaid_args = mermaid_args + self.mermaid_img_scale_reduction = mermaid_img_scale_reduction self._logger = logger - def write_pdf(self, html_string: str, output: str): + def write_pdf(self, html_string: str, + output: str, + temporary_directory: pathlib.Path): self._logger.info(' Rendering with `ReLaXed JS`.') with TemporaryDirectory() as work_dir: + html_string = render_mermaid( + html_string, + work_dir, + self.mermaid_args, + self.mermaid_img_scale_reduction, + self._logger) + entry_point = os.path.join(work_dir, 'pdf_print.html') with open(entry_point, 'w+') as f: f.write(html_string) @@ -42,3 +66,7 @@ def write_pdf(self, html_string: str, output: str): self._logger.info(f" {log}") if proc.poll() is not None: break + # workaround for '--build-once' not working + if log.find("Now idle and waiting for file changes") > -1: + proc.kill() + break diff --git a/mkdocs_with_pdf/generator.py b/mkdocs_with_pdf/generator.py index 195528cf..40267411 100644 --- a/mkdocs_with_pdf/generator.py +++ b/mkdocs_with_pdf/generator.py @@ -5,7 +5,6 @@ from importlib import import_module from importlib.util import module_from_spec, spec_from_file_location import pathlib -import shutil from bs4 import BeautifulSoup, PageElement from weasyprint import HTML, urls @@ -163,7 +162,7 @@ def add_stylesheet(stylesheet: str): if self._options.relaxed_js: self._options.relaxed_js.write_pdf( - html_string, abs_pdf_path) + html_string, abs_pdf_path, temporary_directory) else: html = HTML(string=html_string) render = html.render() @@ -397,7 +396,6 @@ def _render_js(self, soup, temporary_directory: pathlib.Path): body.append(tag) for src in scripts: body.append(soup.new_tag('script', src=f'file://{src}')) - return self._options.js_renderer.render(str(soup), temporary_directory) def _scrap_scripts(self, soup): diff --git a/mkdocs_with_pdf/options.py b/mkdocs_with_pdf/options.py index aa41bfc1..f3381d59 100644 --- a/mkdocs_with_pdf/options.py +++ b/mkdocs_with_pdf/options.py @@ -43,7 +43,7 @@ class Options(object): ('render_js', config_options.Type(bool, default=False)), ('mermaid_args', config_options.Type(str, default="-b transparent -t dark --scale 4 --quiet")), - ('mermaid_img_scale_reduction', config_options.Type(int, default=1)), + ('mermaid_img_scale_reduction', config_options.Type((float, int), default=1)), ('headless_chrome_path', config_options.Type(str, default='chromium-browser')), ('relaxedjs_path', @@ -104,7 +104,10 @@ def __init__(self, local_config, config, logger: logging): logger) self.relaxed_js = RelaxedJSRenderer.setup( - local_config['relaxedjs_path'], logger) + local_config['relaxedjs_path'], + local_config['mermaid_args'], + local_config['mermaid_img_scale_reduction'], + logger) # Theming self.theme_name = config['theme'].name diff --git a/mkdocs_with_pdf/utils/mermaid_util.py b/mkdocs_with_pdf/utils/mermaid_util.py new file mode 100644 index 00000000..0123b0f4 --- /dev/null +++ b/mkdocs_with_pdf/utils/mermaid_util.py @@ -0,0 +1,60 @@ +import os +import re +import html as html_lib +import pathlib +from logging import Logger +from typing import Union + + +def render_mermaid(html: str, + temporary_directory: Union[str, pathlib.Path], + mermaid_args: str, + mermaid_img_scale_reduction: float, + logger: Logger): + + if (isinstance(temporary_directory, str)): + temporary_directory = pathlib.Path(temporary_directory) + + mermaid_regex = re.compile(r'<(\w*?[^>]*)(><[^>]*?|[^>]*?)class="[^>\"]*(language-)?mermaid[^>\"]*">(<[^>]*?>)?(?P.*?)(<\/[^>]*?>)?<\/\1>', flags=re.DOTALL) + mermaid_matches = mermaid_regex.finditer(html) + + i = 0 + # Convert each Mermaid diagram to an image. + for mermaid_block in mermaid_matches: + i += 1 + logger.info(f"Converting mermaid diagram {i}") + mermaid_code = mermaid_block.group("code") + + # Create a temporary file to hold the Mermaid code. + mermaid_file_path = temporary_directory / f"diagram_{i + 1}.mmd" + with open(mermaid_file_path, "wb") as mermaid_file: + mermaid_code_unescaped = html_lib.unescape(mermaid_code) + mermaid_file.write(mermaid_code_unescaped.encode("utf-8")) + + # Create a filename for the image. + image_file_path = temporary_directory / f"diagram_{i}.png" + + # Convert the Mermaid diagram to an image using mmdc. + command = f"mmdc -i {mermaid_file_path} -o {image_file_path} {mermaid_args}" + + # suppress sub-process chatter when using '--quiet' + if mermaid_args.find('--quiet') > -1 or mermaid_args.find(' -q ') > -1 or mermaid_args.endswith(' -q'): + command += " >/dev/null 2>&1" + + os.system(command) + + if not os.path.exists(image_file_path): + logger.warning(f"Error: Failed to generate mermaid diagram {i}") + else: + from PIL import Image + + with Image.open(image_file_path) as im: + height = im.height // mermaid_img_scale_reduction + width = im.width // mermaid_img_scale_reduction + + # Replace the Mermaid code with the image in the HTML string. + image_html = f'Mermaid diagram {i}' + html = html.replace(mermaid_block.group(0), + mermaid_block.group(0).replace(mermaid_code, image_html)) + + return html From 3faa947d4691e212b4e36410551ab1de803786e0 Mon Sep 17 00:00:00 2001 From: Nathaniel Clark Date: Wed, 10 Jul 2024 12:28:13 -0400 Subject: [PATCH 06/10] Restoring an import removed by accident --- mkdocs_with_pdf/generator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs_with_pdf/generator.py b/mkdocs_with_pdf/generator.py index 40267411..2e38de29 100644 --- a/mkdocs_with_pdf/generator.py +++ b/mkdocs_with_pdf/generator.py @@ -5,6 +5,7 @@ from importlib import import_module from importlib.util import module_from_spec, spec_from_file_location import pathlib +import shutil from bs4 import BeautifulSoup, PageElement from weasyprint import HTML, urls From d1b22969deb18bbcab7454df4306e207789197f9 Mon Sep 17 00:00:00 2001 From: Nathaniel Clark Date: Wed, 10 Jul 2024 14:23:39 -0400 Subject: [PATCH 07/10] Remove unused import --- mkdocs_with_pdf/drivers/relaxedjs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mkdocs_with_pdf/drivers/relaxedjs.py b/mkdocs_with_pdf/drivers/relaxedjs.py index 780516fd..5490ff3e 100644 --- a/mkdocs_with_pdf/drivers/relaxedjs.py +++ b/mkdocs_with_pdf/drivers/relaxedjs.py @@ -1,7 +1,6 @@ import os from ..utils.mermaid_util import render_mermaid from logging import Logger -import shutil from shutil import which from subprocess import PIPE, Popen from tempfile import TemporaryDirectory From ba01be765880214e4a61ed5019dd76bc33403d39 Mon Sep 17 00:00:00 2001 From: Nathaniel Clark Date: Wed, 10 Jul 2024 15:11:37 -0400 Subject: [PATCH 08/10] Changed height/width element CSS to use max-width/max-height for easier application of additional CSS rules like 'width=100%'. --- mkdocs_with_pdf/utils/mermaid_util.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/mkdocs_with_pdf/utils/mermaid_util.py b/mkdocs_with_pdf/utils/mermaid_util.py index 0123b0f4..d9585669 100644 --- a/mkdocs_with_pdf/utils/mermaid_util.py +++ b/mkdocs_with_pdf/utils/mermaid_util.py @@ -49,11 +49,14 @@ def render_mermaid(html: str, from PIL import Image with Image.open(image_file_path) as im: - height = im.height // mermaid_img_scale_reduction - width = im.width // mermaid_img_scale_reduction - # Replace the Mermaid code with the image in the HTML string. - image_html = f'Mermaid diagram {i}' + image_html = f'Mermaid diagram {i}' + + if mermaid_img_scale_reduction != 1: + height = im.height // mermaid_img_scale_reduction + width = im.width // mermaid_img_scale_reduction + image_html = image_html.replace('">', f'" style="max-width:{width}px; max-height:{height}px;">') + html = html.replace(mermaid_block.group(0), mermaid_block.group(0).replace(mermaid_code, image_html)) From 5d18daa4bd4c481be840f37626a4ebcf2318f554 Mon Sep 17 00:00:00 2001 From: Nathaniel Clark Date: Tue, 6 Aug 2024 14:43:54 -0400 Subject: [PATCH 09/10] Fix to fix_image_alignment failing to detect an image's style attribute. --- mkdocs_with_pdf/utils/image_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs_with_pdf/utils/image_util.py b/mkdocs_with_pdf/utils/image_util.py index 3e73506d..6a29ecdc 100644 --- a/mkdocs_with_pdf/utils/image_util.py +++ b/mkdocs_with_pdf/utils/image_util.py @@ -15,7 +15,7 @@ def fix_image_alignment(soup: PageElement, logger: Logger = None): if img.has_attr('class') and 'twemoji' in img['class']: continue - styles = _parse_style(getattr(img, 'style', '')) + styles = _parse_style(img.get('style', '')) logger.debug(f' | {img}') if img.has_attr('align'): From ea7771bc0daf1790e904004ac0891df7aeb0fa83 Mon Sep 17 00:00:00 2001 From: Nathaniel Clark Date: Tue, 6 Aug 2024 14:58:13 -0400 Subject: [PATCH 10/10] Added a guard test to prevent fix_image_alignment from altering an image's style attribute if the image's attributes don't need to be fixed. --- mkdocs_with_pdf/utils/image_util.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mkdocs_with_pdf/utils/image_util.py b/mkdocs_with_pdf/utils/image_util.py index 6a29ecdc..21d69b4d 100644 --- a/mkdocs_with_pdf/utils/image_util.py +++ b/mkdocs_with_pdf/utils/image_util.py @@ -15,6 +15,11 @@ def fix_image_alignment(soup: PageElement, logger: Logger = None): if img.has_attr('class') and 'twemoji' in img['class']: continue + if not (img.has_attr('align') + or img.has_attr('width') + or img.has_attr('height')): + continue + styles = _parse_style(img.get('style', '')) logger.debug(f' | {img}')