diff --git a/README.md b/README.md index f4946b37..fda4a47a 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Here are the keywords you can put in, and a brief description of what they do: | link | Sets the unbreakable link to the content that follows. Links in the *content* should be prefixed with a double at-sign (@@) to tell the build system that the link is an internal one | | include | Tells the build system that the content lives in an external file; these normally live in the `include/` directory. Note that the filename should **not** be prefixed with `include/` | | exclude | Tells the `implode` and `explode` scripts that file referred to by the `include` keyword should be ignored. Note that the value of this keyword is ignored | -| pdf-exclude | Does not include the content in the generated PDF, but links to its online contents. The value is also ignored. | +| pdf-exclude | Does not include the content in the generated PDF or EPUB, but links to its online contents. The value is also ignored. | | style | Sets an alternate CSS stylesheet; the name should match the one referred to (sans the `.css` suffix) in the `source/css` directory | | uri | Sets an absolute URI where this page will go in the hierarchy of the created website. It does *not* change the document structure | @@ -119,6 +119,8 @@ The `build.py` script that builds the manual accepts the following options: (link, file name, URL) - '-p', or '--pdf', to automatically generate a PDF from the content (requires [WeasyPrint](https://weasyprint.org/) to run) +- '-e', or '--epub', to automatically generate an EPUB from the content +(requires [Pandoc](https://pandoc.org/) and the Python package [html5lib](https://pypi.org/project/html5lib/)) ### Redirects diff --git a/build.py b/build.py index 6a1ce3eb..80841b6f 100755 --- a/build.py +++ b/build.py @@ -14,7 +14,9 @@ import os import re import shutil +import sys import argparse +import subprocess import datetime # Global vars @@ -426,11 +428,13 @@ def CreateLinkSidebar(fs, pos, childList): parser.add_argument('-q', '--quiet', action='store_true', help='Suppress all output (overrides -v)') parser.add_argument('-d', '--devmode', action='store_true', help='Add content to pages to help developers debug them') parser.add_argument('-p', '--pdf', action='store_true', help='Automatically generate PDF from content') +parser.add_argument('-e', '--epub', action='store_true', help='Automatically generate EPUB from content') args = parser.parse_args() verbose = args.verbose noisy = not args.quiet devmode = args.devmode pdf = args.pdf +epub = args.epub # --quiet overrides --verbose, so tell it to shut up if user did both if not noisy: @@ -443,6 +447,7 @@ def CreateLinkSidebar(fs, pos, childList): page = '' onepage = '' pdfpage = '' +epubpage = '' toc = '' pageNumber = 0 @@ -476,12 +481,15 @@ def CreateLinkSidebar(fs, pos, childList): onepage = onepage.replace('{{page.bootstrap_path}}', global_bootstrap_path) onepage = onepage.replace('{{page.page_title}}', global_page_title) -if pdf: - # Same as above, but for the PDF version +if pdf or epub: temp = open(global_pdf_template) - pdfpage = temp.read() + base = temp.read() temp.close() - pdfpage = pdfpage.replace('{{page.page_title}}', global_page_title) + base = base.replace('{{page.page_title}}', global_page_title) + if pdf: + pdfpage = base + if epub: + epubpage = base # Parse out the master document's structure into a dictionary list fileStruct = GetFileStructure() @@ -644,6 +652,12 @@ def CreateLinkSidebar(fs, pos, childList): else: pdfpage = pdfpage.replace('{{ content }}', oph + '\n' + 'Please refer to the online manual.\n{{ content }}') + if epub: + if not 'pdf-exclude' in header: + epubpage = epubpage.replace('{{ content }}', oph + '\n' + opcontent + '\n{{ content }}') + else: + epubpage = epubpage.replace('{{ content }}', oph + '\n' + 'Please refer to the online manual.\n{{ content }}') + # ----- Normal version ----- # Fix up any internal links @@ -716,6 +730,17 @@ def CreateLinkSidebar(fs, pos, childList): pdfpage = pdfpage.replace('{{ today }}', global_today) pdfpage = pdfpage.replace('src="/images/', 'src="images/') # makes images links relative pdfpage = pdfpage.replace('url(\'/images/', 'url(\'images/') # CSS images links relative + + def drop_missing_images(html, site_dir): + def repl(m): + fpath = site_dir + 'images/' + m.group(1) + if os.path.isfile(fpath): + return m.group(0) + return '[Image: ' + m.group(1) + ' not found]' + return re.sub(r']*\ssrc="images/([^"]+)"[^>]*>', repl, html) + + pdfpage = drop_missing_images(pdfpage, global_site_dir) + # Write it to disk (optional, can be removed) pdfpageFile = open(global_site_dir + 'pdf.html', 'w') pdfpageFile.write(pdfpage) @@ -732,6 +757,42 @@ def CreateLinkSidebar(fs, pos, childList): doc = HTML(string = pdfpage, base_url = global_site_dir) doc.write_pdf(global_site_dir + 'manual.pdf', font_config = html_font_config) +if epub: + if noisy: + print('Generating the EPUB...') + epubpage = epubpage.replace('{% tree %}', opsidebar) + epubpage = epubpage.replace('{{ content }}', '') + epubpage = epubpage.replace('{{ today }}', global_today) + epubpage = epubpage.replace('src="/images/', 'src="images/') + epubpage = epubpage.replace('url(\'/images/', 'url(\'images/') + + try: + import html5lib + except ImportError: + sys.exit('EPUB build requires html5lib; install with: pip install html5lib') + + doc = html5lib.parse(epubpage) + walker = html5lib.getTreeWalker('etree') + stream = walker(doc) + serializer = html5lib.serializer.HTMLSerializer() + epubpage = ''.join(serializer.serialize(stream)) + + epub_html_path = global_site_dir + 'epub.html' + with open(epub_html_path, 'w') as f: + f.write(epubpage) + + result = subprocess.run( + ['pandoc', 'epub.html', '-o', 'manual.epub', '-f', 'html', '-t', 'epub3', + '--resource-path=.', + '--metadata', 'title=' + global_page_title, '--metadata', 'creator=The Ardour Team'], + cwd=global_site_dir, capture_output=True, text=True + ) + if result.returncode != 0: + if result.stderr: + print(result.stderr, file=sys.stderr) + raise subprocess.CalledProcessError(result.returncode, result.args, result.stdout, result.stderr) + os.remove(epub_html_path) + # URI redirects if os.path.exists(global_redirects_file): with open(global_redirects_file) as f: