diff --git a/.gitignore b/.gitignore index 7c33115..07e5363 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ site report.css build venv +.*.sw[pon] diff --git a/mkpdfs_mkdocs/design/report.scss b/mkpdfs_mkdocs/design/report.scss index 0d3b41d..3df4db3 100644 --- a/mkpdfs_mkdocs/design/report.scss +++ b/mkpdfs_mkdocs/design/report.scss @@ -4,6 +4,7 @@ $bgColor: #2196f3; $bgTextColor: #fff; +$lightTextColor: #707070; @font-face { @@ -276,6 +277,9 @@ html body article { break-before: right; break-after: left; page: no-chapter; + h2, h3, h4, h5, h6 { + bookmark-level: none; + } h2 { font-size: 20pt; font-weight: 400; @@ -334,4 +338,11 @@ html body article { p { text-align: justify; } + blockquote { + margin-left: .2cm; + padding-left: .2cm; + color: $lightTextColor; + border-left: .1cm solid; + border-color: $lightTextColor; + } } diff --git a/mkpdfs_mkdocs/generator.py b/mkpdfs_mkdocs/generator.py index 9be961c..0a77306 100644 --- a/mkpdfs_mkdocs/generator.py +++ b/mkpdfs_mkdocs/generator.py @@ -10,6 +10,9 @@ from mkpdfs_mkdocs.utils import gen_address from .utils import is_external from mkpdfs_mkdocs.preprocessor import get_separate as prep_separate, get_combined as prep_combined +from mkpdfs_mkdocs.preprocessor import nest_heading_bookmarks +from mkpdfs_mkdocs.preprocessor import remove_header_links +from mkpdfs_mkdocs.preprocessor import remove_material_header_icons log = logging.getLogger(__name__) @@ -26,6 +29,7 @@ def __init__(self): self.generate = True self._articles = {} self._page_order = [] + self._page_nesting = {} self._base_urls = {} self._toc = None self.html = BeautifulSoup('\ @@ -67,17 +71,20 @@ def add_nav(self, nav): for p in nav: self.add_to_order(p) - def add_to_order(self, page): + def add_to_order(self, page, level=1): if page.is_page and page.meta and 'pdf' in page.meta and not page.meta['pdf']: return if page.is_page: + self._page_nesting[page.file.url] = level - 1 self._page_order.append(page.file.url) elif page.children: uuid = str(uuid4()) self._page_order.append(uuid) title = self.html.new_tag('h1', id='{}-title'.format(uuid), - **{'class': 'section_title'} + **{'class': 'section_title', + # See also nest_heading_bookmarks() + 'style': 'bookmark-level:{}'.format(level)} ) title.append(page.title) article = self.html.new_tag('article', @@ -87,7 +94,7 @@ def add_to_order(self, page): article.append(title) self._articles[uuid] = article for child in page.children: - self.add_to_order(child) + self.add_to_order(child, level=level + 1) def remove_from_order(self, item): return @@ -108,7 +115,13 @@ def add_article(self, content, page, base_url): if not article: self.generate = False return None + if self.mkdconfig['theme'].name == 'material': + article = remove_material_header_icons(article) article = prep_combined(article, base_url, page.file.url) + article = remove_header_links(article) + article = nest_heading_bookmarks( + article, self._page_nesting.get(page.file.url, 0) + ) if page.meta and 'pdf' in page.meta and not page.meta['pdf']: # print(page.meta) return self.get_path_to_pdf(page.file.dest_path) @@ -131,12 +144,6 @@ def add_head(self): self.html.head.clear() self.html.head.insert(0, head) - def get_path_to_pdf(self, start): - pdf_split = os.path.split(self.config['output_path']) - start_dir = os.path.split(start)[0] - return os.path.join(os.path.relpath(pdf_split[0], - start_dir), pdf_split[1]) - def add_tocs(self): title = self.html.new_tag('h1', id='toc-title') title.insert(0, self.config['toc_title']) @@ -178,10 +185,8 @@ def gen_articles(self): self.add_tocs() def get_path_to_pdf(self, start): - pdf_split = os.path.split(self.config['output_path']) - start_dir = os.path.split(start)[0] if os.path.split(start)[0] else '.' - return os.path.join(os.path.relpath(pdf_split[0], start_dir), - pdf_split[1]) + return os.path.relpath(self.config['output_path'], + os.path.dirname(start)) def _gen_toc_section(self, section): if section.children: # External Links do not have children @@ -189,6 +194,12 @@ def _gen_toc_section(self, section): if p.is_page and p.meta and 'pdf' \ in p.meta and not p.meta['pdf']: continue + if p.is_section: + h3 = self.html.new_tag('h3') + h3.insert(0, p.title) + self._toc.append(h3) + self._gen_toc_section(p) + continue if not hasattr(p, 'file'): # Skip external links continue diff --git a/mkpdfs_mkdocs/mkpdfs.py b/mkpdfs_mkdocs/mkpdfs.py index e88b3a3..0b80dba 100644 --- a/mkpdfs_mkdocs/mkpdfs.py +++ b/mkpdfs_mkdocs/mkpdfs.py @@ -6,7 +6,7 @@ from weasyprint import HTML, urls, CSS from mkpdfs_mkdocs.generator import Generator -from mkpdfs_mkdocs.utils import modify_html +from mkpdfs_mkdocs.utils import modify_html, modify_html_material log = logging.getLogger(__name__) @@ -27,6 +27,7 @@ def __init__(self): self.generator = Generator() self._skip_pdf = True if os.environ.get("SKIP_PDF") else False self._logger = logging.getLogger('mkdocs.mkpdfs') + self.theme = '' def on_serve(self, server, config, **kwargs): if self._skip_pdf: @@ -42,6 +43,7 @@ def on_config(self, config, **kwargs): return config self.config['output_path'] = os.path.join("pdf", "combined.pdf") if not self.config['output_path'] else self.config['output_path'] self.generator.set_config(self.config, config) + self.theme = config['theme'].name return config def on_nav(self, nav, config, **kwargs): @@ -65,7 +67,10 @@ def on_post_page(self, output_content, page, config, **kwargs): base_url = urls.path2url(os.path.join(path, filename)) pdf_url = self.generator.add_article(output_content, page, base_url) if self.config['pdf_links'] and pdf_url: - output_content = modify_html(output_content,pdf_url) + if self.theme == 'material': + output_content = modify_html_material(output_content, pdf_url) + else: + output_content = modify_html(output_content, pdf_url) return output_content def on_post_build(self, config): diff --git a/mkpdfs_mkdocs/preprocessor/__init__.py b/mkpdfs_mkdocs/preprocessor/__init__.py index 8fa23fa..20cddb6 100755 --- a/mkpdfs_mkdocs/preprocessor/__init__.py +++ b/mkpdfs_mkdocs/preprocessor/__init__.py @@ -1 +1,7 @@ -from .prep import get_combined, get_separate \ No newline at end of file +from .prep import ( + get_combined, + get_separate, + nest_heading_bookmarks, + remove_header_links, + remove_material_header_icons, +) diff --git a/mkpdfs_mkdocs/preprocessor/prep.py b/mkpdfs_mkdocs/preprocessor/prep.py index 45b8c83..a7a2c4f 100755 --- a/mkpdfs_mkdocs/preprocessor/prep.py +++ b/mkpdfs_mkdocs/preprocessor/prep.py @@ -28,3 +28,34 @@ def get_separate(soup: BeautifulSoup, base_url: str): soup = replace_asset_hrefs(soup, base_url) return soup + +def remove_material_header_icons(soup: BeautifulSoup): + """Removes links added to article headers by material theme such as the + page edit link/url (pencil icon).""" + # see https://github.com/squidfunk/mkdocs-material/issues/1920 + # for justification of why this CSS class is used + for a in soup.find_all('a', **{'class': 'md-content__button'}): + a.decompose() + return soup + +def remove_header_links(soup: BeautifulSoup): + for a in soup.find_all('a', **{'class': 'headerlink'}): + a.decompose() + return soup + +def nest_heading_bookmarks(soup: BeautifulSoup, inc: int): + """Ensure titles & subheadings of pages are properly nested as bookmarks. + + So that while a page's titles always starts as

, + when seen in the PDF index, all page headings will be nested according + to the page's nesting under sections & subsections. + """ + if not inc: + return soup + assert isinstance(inc, int) and inc > 0 + for i in range(6, 0, -1): + # For each level of heading, add an inline CSS style that sets the + # bookmark-level to the heading level + `inc`. + for h in soup.find_all('h{}'.format(i)): + h['style'] = 'bookmark-level:{}'.format(i + inc) + return soup diff --git a/mkpdfs_mkdocs/utils.py b/mkpdfs_mkdocs/utils.py index 3c200c8..ea5293c 100644 --- a/mkpdfs_mkdocs/utils.py +++ b/mkpdfs_mkdocs/utils.py @@ -22,6 +22,17 @@ def modify_html(html: str, href: str) -> str: return str(soup) +def modify_html_material(html: str, href: str) -> str: + # Taken from mkdocs-pdf-export-plugin for material theme. + # https://github.com/zhaoterryy/mkdocs-pdf-export-plugin/blob/46af862318c92996913a81cab07cc998a871c2cb/mkdocs_pdf_export_plugin/themes/material.py#L65-L71 + a_tag = "" % href + icon = '' + button_tag = a_tag + icon + "" + insert_point = "
" + html = html.replace(insert_point, insert_point + button_tag) + return html + + def gen_address(config): soup = BeautifulSoup('', 'html5lib'