diff --git a/docs/options.md b/docs/options.md index 128dd0d..61bccff 100644 --- a/docs/options.md +++ b/docs/options.md @@ -8,6 +8,7 @@ plugins: add_to_navigation: false print_page_title: 'Print Site' print_page_basename: 'print_page' + print_by_section: false add_print_site_banner: false # Table of contents add_table_of_contents: true @@ -34,6 +35,11 @@ plugins: `print_page_basename` : Default is `'print_page'`. Can be used to cutomized the path to the print page in the URL. +`print_by_section` +: Default is `false`. Used to generate a print page per level one section. The section print page will be generated at the root level of the site, with the `print_page_basename` followed by an `_` and the section title cleansed from all special characters and punctuation. + + For example, a section named "Bob's list" will be availlable as `print_page_bobslist/` + `add_table_of_contents` : Default is `true`. Adds a table of contents section at the beginning of the print page (in print version, the HTML version has a different sidebar ToC). diff --git a/src/mkdocs_print_site_plugin/plugin.py b/src/mkdocs_print_site_plugin/plugin.py index 020170f..7525362 100644 --- a/src/mkdocs_print_site_plugin/plugin.py +++ b/src/mkdocs_print_site_plugin/plugin.py @@ -16,6 +16,7 @@ from mkdocs_print_site_plugin.renderer import Renderer from mkdocs_print_site_plugin.urls import is_external from mkdocs_print_site_plugin.utils import get_theme_name +from mkdocs_print_site_plugin.utils import generate_link_from logger = logging.getLogger("mkdocs.plugins") @@ -46,6 +47,7 @@ class PrintSitePlugin(BasePlugin): ("include_css", config_options.Type(bool, default=True)), ("enabled", config_options.Type(bool, default=True)), ("exclude", config_options.Type(list, default=[])), + ("print_by_section", config_options.Type(bool, default=False)), ) def on_config(self, config, **kwargs): @@ -186,12 +188,34 @@ def on_nav(self, nav, config, files, **kwargs): # Save the (order of) pages and sections in the navigation before adding the print page self.renderer.items = nav.items + self.section_print_pages = [] # Optionally add the print page to the site navigation if self.config.get("add_to_navigation"): nav.items.append(self.print_page) nav.pages.append(self.print_page) + if self.config.get("print_by_section"): + for item in nav.items: + if hasattr(item, "children") and item.children: + # This is a section + section_print_file = File( + path=f"{self.config.get('print_page_basename')}_{generate_link_from(item.title)}.md", + src_dir="", + dest_dir=config["site_dir"], + use_directory_urls=config.get("use_directory_urls"), + ) + section_print_page = Page( + title=f"{self.config.get('print_page_title')} - {item.title}", + file=section_print_file, + config=config, + ) + section_print_page.edit_url = None + + # Add section print page to the navigation + nav.pages.append(section_print_page) + self.section_print_pages.append(section_print_page) + return nav def on_page_content(self, html, page, config, files, **kwargs): @@ -303,15 +327,42 @@ def on_post_build(self, config, **kwargs): if css_file in os.listdir(os.path.join(HERE, "css")): css_file_path = os.path.join(css_output_base_path, css_file) copy_file(os.path.join(os.path.join(HERE, "css"), css_file), css_file_path) - + + self._render_print_page(self.print_page, self.renderer, config) + + if self.config.get("print_by_section"): + for section_page in self.section_print_pages: + #self.js_loaded_for_section = False + # Find the corresponding section item from nav + section_title = section_page.title.split(" - ")[-1] + section_item = None + for item in self.renderer.items: # self.renderer.items has all nav items + if hasattr(item, "title") and item.title == section_title: + section_item = item + break + + if section_item: + section_renderer = Renderer( + plugin_config=self.config, + mkdocs_config=config, + cover_page_template_path=self.cover_page_template_path, + banner_template_path=self.banner_template_path, + print_page=section_page, + ) + section_renderer.items = [section_item] + self._render_print_page(section_page, section_renderer, config) + + def _render_print_page(self, page, renderer, config): + """ + Renders a given page to a print page. + """ # Combine the HTML of all pages present in the navigation - self.print_page.content, self.print_page.toc = self.renderer.write_combined() - + page.content, page.toc = renderer.write_combined() # Get the info for MkDocs to be able to apply a theme template on our print page env = config["theme"].get_env() # env.list_templates() template = env.get_template("main.html") - self.context["page"] = self.print_page + self.context["page"] = page # Render the theme template for the print page html = template.render(self.context) @@ -323,7 +374,7 @@ def on_post_build(self, config, **kwargs): # As this plugin adds some javascript to every page # It should be included in the print site also if config.get("plugins", {}).get("charts"): - html = config.get("plugins", {}).get("charts").add_javascript_variables(html, self.print_page, config) + html = config.get("plugins", {}).get("charts").add_javascript_variables(html, page, config) # Compatibility with mkdocs-drawio # As this plugin adds renderer html for every drawio diagram @@ -331,7 +382,7 @@ def on_post_build(self, config, **kwargs): # in the on_post_page event, which is skipped by this plugin # therefore we need to manual execute the drawio plugin renderer here. if config.get("plugins", {}).get("drawio"): - html = config.get("plugins", {}).get("drawio").render_drawio_diagrams(html, self.print_page) + html = config.get("plugins", {}).get("drawio").render_drawio_diagrams(html, page) # Compatibility with mkdocs-autorefs # As this plugin processes cross-references in the on_env event, @@ -447,4 +498,4 @@ def print_page_url_mapper(identifier, from_url=None): html = html.replace("", print_site_js + "") # Write the print_page file to the output folder - write_file(html.encode("utf-8", errors="xmlcharrefreplace"), self.print_page.file.abs_dest_path) + write_file(html.encode("utf-8", errors="xmlcharrefreplace"), page.file.abs_dest_path) diff --git a/src/mkdocs_print_site_plugin/utils.py b/src/mkdocs_print_site_plugin/utils.py index 39dec52..ec1bfc9 100644 --- a/src/mkdocs_print_site_plugin/utils.py +++ b/src/mkdocs_print_site_plugin/utils.py @@ -1,4 +1,5 @@ import os +import unicodedata def get_theme_name(config) -> str: @@ -28,3 +29,16 @@ def get_theme_name(config) -> str: def get_section_id(section_number: str) -> str: return f"section-{section_number.replace('.', '-')}" + + +def generate_link_from(title: str) -> str: + """ + Generates a link from the provided title. + """ + ndf_string = unicodedata.normalize('NFD', title.lower()) + is_not_accent = lambda char: unicodedata.category(char) != 'Mn' + is_punctuation = lambda char: unicodedata.category(char).startswith('P') + is_separator = lambda char: unicodedata.category(char).startswith('Z') + return ''.join( + char for char in ndf_string if is_not_accent(char) and not is_punctuation(char) and not is_separator(char) + ) diff --git a/tests/fixtures/projects/nested_sections/docs/index.md b/tests/fixtures/projects/nested_sections/docs/index.md index 3534183..3677ab5 100644 --- a/tests/fixtures/projects/nested_sections/docs/index.md +++ b/tests/fixtures/projects/nested_sections/docs/index.md @@ -2,4 +2,6 @@ Goal of this site is to show a very nested section navigation. -See [mkdocs-print-site-plugin#27](https://github.com/timvink/mkdocs-print-site-plugin/issues/27) \ No newline at end of file +See [mkdocs-print-site-plugin#27](https://github.com/timvink/mkdocs-print-site-plugin/issues/27) + +GFp6NVqU7S diff --git a/tests/fixtures/projects/nested_sections/docs/page1.md b/tests/fixtures/projects/nested_sections/docs/page1.md index 498cbe3..76a843f 100644 --- a/tests/fixtures/projects/nested_sections/docs/page1.md +++ b/tests/fixtures/projects/nested_sections/docs/page1.md @@ -1,3 +1,5 @@ # page1 -hello \ No newline at end of file +hello + +lRLw8LcChv diff --git a/tests/fixtures/projects/nested_sections/mkdocs_print_by_section.yml b/tests/fixtures/projects/nested_sections/mkdocs_print_by_section.yml new file mode 100644 index 0000000..02f5cd0 --- /dev/null +++ b/tests/fixtures/projects/nested_sections/mkdocs_print_by_section.yml @@ -0,0 +1,35 @@ +site_name: Test + +theme: + name: material + palette: + - scheme: default + toggle: + icon: material/toggle-switch-off-outline + name: Switch to dark mode + - scheme: slate + toggle: + icon: material/toggle-switch + name: Switch to light mode + +plugins: + - print-site: + enumerate_headings: false + print_by_section: true + +nav: + - index.md + - Section: + - page1.md + - page5.md + - Subsection: + - page2.md + - Another subsection: + - page3.md + - And another: + - page6.md + - page7.md + - page4.md + - page8.md + - info.md + diff --git a/tests/test_building.py b/tests/test_building.py index e17f6c8..f88c1fe 100644 --- a/tests/test_building.py +++ b/tests/test_building.py @@ -330,3 +330,15 @@ def test_basic_build99(tmp_path): ) assert text_in_page(prj_path, "print_page/index.html", '

A') assert text_in_page(prj_path, "print_page/index.html", '

Z') + +def test_print_by_section(tmp_path): + """ + Check if print_section works. + """ + prj_path = check_build(tmp_path, "nested_sections/mkdocs_print_by_section.yml") + + # Check that page1 content is included in print page section + assert text_in_page(prj_path, "print_page_section/index.html", "lRLw8LcChv") + + # Check that index content is not included in print page section + assert not text_in_page(prj_path, "print_page_section/index.html", "GFp6NVqU7S") diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..14414fc --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,25 @@ +import pytest + +from mkdocs_print_site_plugin.utils import ( + generate_link_from, +) + +def test_generate_link_from(): + """ + Test generate_link_from. + """ + + title = '' + assert generate_link_from(title) == '' + + title = 'thisshouldstayunmodified' + assert generate_link_from(title) == 'thisshouldstayunmodified' + + title = 'Separa-tors not in url' + assert generate_link_from(title) == 'separatorsnotinurl' + + title = "Punctuation.not'in;url" + assert generate_link_from(title) == 'punctuationnotinurl' + + title = 'AccéntèdChärsNotinUrl' + assert generate_link_from(title) == 'accentedcharsnotinurl'