From 605474c71779f10c51b846079e0e73bfe644fedc Mon Sep 17 00:00:00 2001 From: Kristoffer Andersen Date: Fri, 12 Jan 2024 15:40:05 +0100 Subject: [PATCH] feat: Add optional navbar to html export (#16) --- MANIFEST.in | 2 +- src/paradoc/document.py | 6 +-- src/paradoc/io/html/exporter.py | 31 +++++++++++++- src/paradoc/io/html/js/navbar.js | 54 +++++++++++++++++++++++++ src/paradoc/resources/default_style.css | 27 ++++++++++++- 5 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 src/paradoc/io/html/js/navbar.js diff --git a/MANIFEST.in b/MANIFEST.in index b2f693c..f47c2c9 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,6 +3,6 @@ include README.md include src/paradoc/resources/default_style.css include src/paradoc/resources/template.docx include src/paradoc/resources/template_blank.docx -include src/paradoc/rules/resources/*.json +include src/paradoc/io/html/js/*.js include src/paradoc/svg/templates/*.svg include src/paradoc/svg/templates/titleblocks/*.svg \ No newline at end of file diff --git a/src/paradoc/document.py b/src/paradoc/document.py index 8eddfb2..e82d590 100644 --- a/src/paradoc/document.py +++ b/src/paradoc/document.py @@ -91,7 +91,7 @@ def __init__( self.md_files_main = [] self.md_files_app = [] self.metadata_file = None - self._use_default_html_style = use_default_html_style + self.use_default_html_style = use_default_html_style self._setup(create_dirs, clean_build_dir) def _iter_md_files(self) -> Iterable[pathlib.Path]: @@ -185,7 +185,7 @@ def compile(self, output_name, auto_open=False, metadata_file=None, export_forma if self.metadata_file.exists() is False: with open(self.metadata_file, "w") as f: f.write('linkReferences: true\nnameInLink: true\nfigPrefix: "Figure"\ntblPrefix: "Table"') - if self._use_default_html_style is True: + if self.use_default_html_style is True: f.write("\nstylesheet: style.css") css_style = self.source_dir / "style.css" if css_style.exists() is False: @@ -222,7 +222,7 @@ def compile(self, output_name, auto_open=False, metadata_file=None, export_forma self._perform_variable_substitution(False) html = HTMLExporter(self) - html.export(dest_file) + html.export(dest_file, include_navbar=kwargs.get("include_navbar", True)) else: raise NotImplementedError(f'Export format "{export_format}" is not yet supported') diff --git a/src/paradoc/io/html/exporter.py b/src/paradoc/io/html/exporter.py index e451738..823c182 100644 --- a/src/paradoc/io/html/exporter.py +++ b/src/paradoc/io/html/exporter.py @@ -1,3 +1,4 @@ +import pathlib import shutil import pypandoc @@ -5,12 +6,14 @@ from paradoc import OneDoc from paradoc.utils import copy_figures_to_dist +THIS_DIR = pathlib.Path(__file__).parent + class HTMLExporter: def __init__(self, one_doc: OneDoc): self.one_doc = one_doc - def export(self, dest_file): + def export(self, dest_file, include_navbar=True): one = self.one_doc md_main_str = "\n\n".join([md.read_built_file() for md in one.md_files_main]) @@ -19,7 +22,7 @@ def export(self, dest_file): app_str = """\n\n\\appendix\n\n""" - md_app_str = "\n".join([md.read_built_file() for md in one.md_files_app]) + md_app_str = "\n\n".join([md.read_built_file() for md in one.md_files_app]) combined_str = md_main_str + app_str + md_app_str html_str = pypandoc.convert_text( @@ -35,23 +38,47 @@ def export(self, dest_file): ], filters=["pandoc-crossref"], ) + + js_script = "" + if include_navbar: + js_script = "" + + app_head_text = "" + if len(one.md_files_app) > 0: + app_file1 = open(one.md_files_app[0].path, "r").read() + for line in app_file1.splitlines(): + if line.startswith("# "): + app_head_text = line[2:] + break + styled_html = f""" + + {js_script} +
{html_str} +
""" + # styled_html.format(__custom_js_navbar__=js_script) + with open(dest_file, "w", encoding="utf-8") as f: f.write(styled_html) style_css_file = one.source_dir / "style.css" if style_css_file.exists(): shutil.copy(style_css_file, dest_file.parent / "style.css") + else: + if self.one_doc.use_default_html_style: + from paradoc.common import MY_DEFAULT_HTML_CSS + + shutil.copy(MY_DEFAULT_HTML_CSS, dest_file.parent / "style.css") print(f'Successfully exported HTML to "{dest_file}"') diff --git a/src/paradoc/io/html/js/navbar.js b/src/paradoc/io/html/js/navbar.js new file mode 100644 index 0000000..431cacd --- /dev/null +++ b/src/paradoc/io/html/js/navbar.js @@ -0,0 +1,54 @@ +document.addEventListener("DOMContentLoaded", function() { + const navbar = document.createElement("div"); + navbar.className = "navbar"; + + const appendixStartText = document.querySelector('meta[name="data-appendix-start"]').getAttribute("content").replace(/\s+/g, ''); + + let counters = [0, 0, 0, 0, 0, 0]; + let currentAppendixLetter = 'A'; + let inAppendix = false; + const indentSize = 20; // Indent size in pixels + + const headers = document.querySelectorAll("h1, h2, h3, h4, h5, h6"); + + headers.forEach(function(header) { + const headerText = header.textContent.replace(/\s+/g, ''); + const level = parseInt(header.tagName.substring(1)) - 1; // Define level here + + if (headerText === appendixStartText) { + inAppendix = true; + counters = [0, 0, 0, 0, 0, 0]; + } + + let number; + if (inAppendix) { + if (level === 0) { // Increment letter for each h1 in appendix + if (counters[0] > 0) { // Increment letter after the first h1 + currentAppendixLetter = String.fromCharCode(currentAppendixLetter.charCodeAt(0) + 1); + } + counters = [0, 0, 0, 0, 0, 0]; + } + counters[level]++; + number = (level === 0 ? "Appendix " : "") + currentAppendixLetter + (level > 0 ? "." + counters.slice(1, level + 1).join(".") : ""); + } else { + counters[level]++; + number = counters.slice(0, level + 1).join("."); + } + // reset lower levels + for (let i = level + 1; i < counters.length; i++) { + counters[i] = 0; + } + + header.id = header.id || "heading" + number; + header.textContent = number + " " + header.textContent; + + const link = document.createElement("a"); + link.href = "#" + header.id; + link.textContent = header.textContent; + link.style.paddingLeft = `${indentSize * level}px`; + + navbar.appendChild(link); + }); + + document.body.prepend(navbar); + }); \ No newline at end of file diff --git a/src/paradoc/resources/default_style.css b/src/paradoc/resources/default_style.css index a9e3f84..e834a51 100644 --- a/src/paradoc/resources/default_style.css +++ b/src/paradoc/resources/default_style.css @@ -11,7 +11,7 @@ html { body { color: #444; - font-family: Georgia, Palatino, 'Palatino Linotype', Times, 'Times New Roman', serif; + font-family: Arial, serif; font-size: 12px; line-height: 1.7; padding: 1em; @@ -325,4 +325,29 @@ table td { h2, h3 { page-break-after: avoid; } +} + +/* Basic styling for the navigation bar */ +.navbar { + position: fixed; + top: 0; + left: 0; + width: 20%; + height: 100%; + overflow: auto; + background-color: #f0f0f0; + padding: 10px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); +} + +.navbar a { + display: block; + padding: 5px; + margin: 5px 0; + text-decoration: none; + color: black; +} + +.navbar a:hover { + background-color: #ddd; } \ No newline at end of file