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: