diff --git a/GNUmakefile b/GNUmakefile
index 6a53603d6..278a36e43 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -4,19 +4,27 @@ PROJECT=hpmor
TAG := $(shell git describe --tags)
VERSION := $(shell echo $(TAG) | sed -e 's/^v//')
+EBOOKS = ebook/output/$(PROJECT).epub ebook/output/$(PROJECT).mobi
ZIPFILE = $(PROJECT)-$(VERSION).zip
-all:
+all: ebooks pdf
+
+pdf:
latexmk
-zip:
- latexmk -g && \
+ebooks: pdf
+ cd ebook && ./1_latex2html.py && ./2_html2epub.sh
+
+zip: pdf ebooks
rm -f $(ZIPFILE) && \
- zip $(ZIPFILE) *.pdf
+ zip $(ZIPFILE) *.pdf $(EBOOKS)
# To make a release: git tag vx.y && git push --tags && make release
# Needs woger from https://github.com/rrthomas/woger/
release: zip
git diff --exit-code && \
- woger github package=$(PROJECT) version=$(VERSION) dist_type=zip
- hub release edit $(TAG) --attach $(PROJECT).pdf#$(PROJECT)-$(VERSION).pdf
+ woger github package=$(PROJECT) version=$(VERSION) dist_type=zip && \
+ for file in $(PROJECT).pdf $(EBOOKS); do \
+ suffix=$${file##*.}; \
+ hub release edit $(TAG) --attach $$file#$(PROJECT)-$(VERSION).$$suffix; \
+ done
diff --git a/ebook/.gitignore b/ebook/.gitignore
new file mode 100644
index 000000000..5a1d98e09
--- /dev/null
+++ b/ebook/.gitignore
@@ -0,0 +1,2 @@
+tmp/
+output/
diff --git a/ebook/1_latex2html.py b/ebook/1_latex2html.py
new file mode 100755
index 000000000..98fdc3d80
--- /dev/null
+++ b/ebook/1_latex2html.py
@@ -0,0 +1,737 @@
+#!/usr/bin/env python3
+
+# by Torben Menke https://entorb.net
+
+"""
+converter script:
+reads latex code in ../chapters
+converts to html
+as preparation for conversion into epub format
+output dir: output/
+
+run from within ebook dir via
+python3 1_latex2html.py
+"""
+
+import os
+import re
+import glob
+from datetime import date
+
+# Notes
+# footnotes are converted to inline text
+# these intro parts are skipped:
+# Based on the characters of J. K. ROWLING
+# CONTENT WARNINGS
+# Omake chapters are included in-place, not at appendix
+
+today = date.today()
+
+dir_tex_source = "../chapters/"
+dir_tmp = "tmp"
+dir_out = "output"
+for dir in (dir_tmp, dir_out):
+ os.makedirs(dir, exist_ok=True)
+
+
+html_preamble = f"""
+
Preamble of this e-book
+The original version of this great book 'Harry Potter and the Methods of Rationality' by Eliezer Yudkowsky is:
+https://www.hpmor.com
+This e-book is based on the typesetting and revised text from:
+https://github.com/rjl20/hpmor
+This e-book was created at: {today}
+The latest version can be found at:
+https://github.com/rjl20/hpmor/releases/latest/
+Source code of the converter script can be found at:
+https://github.com/rjl20/hpmor/ebook/
+"""
+
+# This book is not my work, I just converted the text into e-book formats.
+# Have fun on your journey, Torben Menke
+
+
+css = """
+div.letter {
+ font-style: italic;
+ margin-left: 1em;
+}
+div.verse {
+ margin-left: 1em;
+}
+div.playdialog {
+ text-indent: -1em;
+ margin-left: 2em;
+}
+div.headlines {
+}
+div.center {
+ text-align: center;
+}
+div.center_sc {
+ text-align: center;
+ font-variant: small-caps;
+}
+div.later {
+ text-align: center;
+}
+div.emph {
+ font-style: italic;
+}
+
+span.abbrev{
+ text-transform: lowercase;
+ font-variant: small-caps;
+}
+span.prophesy{
+ font-variant: small-caps;
+}
+span.scream{
+ text-transform: uppercase;
+}
+span.shout{
+ font-variant: small-caps;
+}
+span.parsel{
+ font-style: italic;
+}
+span.headline_header{
+}
+span.headline{
+ font-style: italic;
+}
+span.headline_label{
+ font-variant: small-caps;
+}
+span.smallcaps{
+ font-variant: small-caps;
+}
+span.uppercase{
+ text-transform: uppercase;
+}
+"""
+
+# inline instead, since easier for conversion to epub
+# with open(f'output/hpmor-{lang}.css', mode='w',
+# encoding='utf-8', newline='\n') as fh:
+# fh.write(css)
+
+html_start = f"""
+
+
+
+
+Harry Potter and the Methods of Rationality
+
+
+
+"""
+#
+
+
+html_end = """\n"""
+
+
+counter_chapter = 0
+# counter_footnotes = 0
+
+
+def simplify_tex(s: str) -> str:
+ # commands to remove
+ s = re.sub(
+ r"\\lettrine\{(.)\}\{(.*?)\}", r"\1\2", s, flags=re.DOTALL | re.IGNORECASE
+ )
+ s = re.sub(
+ r"\\lettrine\[ante=(.+?)\]\{(.)\}\{(.*?)\}",
+ r"\1\2\3",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ s = re.sub(
+ r"\\lettrinepara\{(.)\}\{(.*?)\}", r"\1\2", s, flags=re.DOTALL | re.IGNORECASE
+ )
+ s = re.sub(
+ r"\\lettrinepara\[ante=(.+?)\]\{(.)\}\{(.*?)\}",
+ r"\1\2\3",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+
+ # add linebreaks to \item and \begin
+ s = re.sub(r"(? section
+ s = re.sub(r"\\OmakeIVsection\[.*?\]\{", r"\\section{", s)
+ s = s.replace("\\OmakeIVsection{", "\\section{")
+ # \latersection -> section
+ s = s.replace("\\latersection{", "\\section{")
+ # OmakeIVspecialsection
+ s = re.sub(
+ r"\\makeatletter\n\\newcommand{\\OmakeIVspecialsection}.*?\\chapter{Omake Files IV, Alternate Parallels}",
+ r"\\chapter{Omake Files IV, Alternate Parallels}\n",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ # Lord of the Rationality
+ s = re.sub(
+ r"\\OmakeIVspecialsection.*?\\raisebox\{-.32ex\}\{Y\}\}",
+ r"\\section{Lord of the Rationality}",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ # The Witch and the Wardrobe
+ s = s.replace(
+ "\\OmakeIVspecialsection[5]{\\fontspec[ExternalLocation]{NarniaBLL}456}",
+ "\\section{The Witch and the Wardrobe}",
+ )
+ # ThunderSmarts
+ s = s.replace(
+ "\\OmakeIVspecialsection[2]{\\fontspec[ExternalLocation]{Thundercats}ThunderSmarts}",
+ "\\section{ThunderSmarts}",
+ )
+ # Utilitarian Twilight
+ s = s.replace(
+ "\\OmakeIVspecialsection{\\fontspec[ExternalLocation]{Twilight}Utilitarian Twilight\protect\\footnotemark}",
+ "\\section{Utilitarian Twilight}",
+ )
+
+ # remove Latex comments
+ s = re.sub(r"(? str:
+
+ #
+ # Bulk text replacements
+ #
+ # Transfiguration is not permanent!
+ s = re.sub(
+ r"\\begin\{center\}(.+?Transfiguration is not permanent!.+?)\\end\{center\}",
+ r'Transfiguration is not permanent!
\n',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+
+ was = "{\n\\begin{center}\n\\includegraphics[scale=0.125]{Deathly_Hallows_Sign.png}\n\\end{center}\n}"
+ s = s.replace(was, "")
+
+ # paper notes in Chapter 13
+ if "Asking the Wrong Questions" in s:
+ # \begin{align*} -> writtenNote
+ myMatches = re.finditer(
+ r"\\begin\{align\*\}.+?\\end\{align\*\}", s, flags=re.DOTALL | re.IGNORECASE
+ )
+ for myMatch in myMatches:
+ was = myMatch.group(0)
+ womit = was
+ womit = womit.replace("align*", "writtenNote")
+ womit = re.sub(r"\\hbox\{(.*?)\}", r"\1", womit)
+ womit = re.sub(r"\\intertext\{(.*?)\}", r"\1", womit, flags=re.DOTALL)
+ womit = re.sub(r"\\multicolumn\{2\}\{c\}\{(.*?)\}", r"\1", womit)
+ womit = womit.replace("\\scshape", "")
+ womit = womit.replace("\\centering", "")
+ womit = womit.replace("&", "")
+ womit = womit.replace("[1.5ex]", "")
+ s = s.replace(was, womit)
+ s = re.sub(
+ r"\\begin\{center\}\s*\\scshape (\\MakeUppercase\{Warning\}.*?)\\end\{center\}",
+ r"\\begin{writtenNote}\1\\end{writtenNote}",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ s = re.sub(
+ r"\\begin\{center\}\s*\\scshape(\nAttempt failed.*?)\\end\{center\}",
+ r"\\begin{writtenNote}\1\\end{writtenNote}",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+
+ # notes in chapter 22
+ if "The Scientific Method" in s:
+ s = re.sub(
+ r"\\begin\{center\}\s*\\itshape\n\{\\scshape (Observation:)\}(.*?)\\end\{center\}",
+ r"\\begin{writtenNote}\\textsc{\1}\2\\end{writtenNote}",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ s = s.replace("{\\scshape Hypotheses:}", "\\textsc{Hypotheses:}")
+ s = s.replace("{\\scshape Tests:}", "\\textsc{Tests:}")
+
+ # notes in chapter 23
+ if "Belief in Belief" in s:
+ s = re.sub(
+ r"\\begin\{centering\}\n\\begin\{samepage\}\n\\scshape (Observation:)(.*?)\\end\{centering\}",
+ r"\\begin{writtenNote}\\textsc{\1} \2\\end{writtenNote}",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ s = re.sub(
+ r"\n\\itshape (Wizardry isn’t as powerful now as it was when Hogwarts was founded.)\s*\\end\{samepage\}",
+ r"\1",
+ s,
+ )
+ s = s.replace("\\scshape Hypotheses:\n\n", "\\textsc{Hypotheses:}")
+ s = s.replace("\\scshape Tests:", "\\textsc{Tests:}")
+ s = s.replace("\itshape\n", "")
+ s = s.replace("{\scshape Result:}", " Result:")
+
+ #
+ # cleanup
+ # commands to remove completely
+ #
+ # commands with empty parameters
+ s = re.sub(r"\\(footnotemark|hyp|noindent)\{\}", "", s)
+ # commands without parameters
+ s = re.sub(
+ r"\\(protect|footnotemark|clearpage|penalty-10|penalty\d*|noindent)\b", "", s
+ )
+ # commands without parameters but followed by linebreaks
+ s = re.sub(
+ r"\\(hplettrineextrapara|savetrivseps|firmlist|footnotemark|restoretrivseps)\n",
+ r"",
+ s,
+ )
+ # commands with 2 parameters
+ s = re.sub(r"\\(setlength|settowidth)\{.*?\}\{.*?\}\n?", r"", s)
+ # commands to remove the optional parameters from
+ s = re.sub(
+ r"(\\section|chapter|partchapter)\[.*?\]",
+ r"\1",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+
+ # some stuff to drop
+ s = s.replace("\\linebreak\\", "")
+ s = s.replace("\\vspace*{\\fill}\n", "")
+ s = s.replace("\\vskip 0pt plus 4\\baselineskip", "")
+ s = s.replace(
+ "\\vskip 1\\baselineskip plus .5\\textheight minus 1\\baselineskip", ""
+ )
+
+ # some simple replacings
+ s = s.replace("~", " ")
+ s = s.replace("\\\\", " ")
+ s = s.replace("\\$", "$")
+ s = s.replace("\\%", "%")
+ s = s.replace("\\&", "&")
+ s = s.replace("\\#", "#")
+ s = s.replace("\\-", "-")
+ s = s.replace("\\@", " ")
+
+ s = s.replace("\\emdashhyp", "—")
+ s = s.replace("\\censor{Hermione}", "xxx")
+ s = s.replace("\\times", "×")
+ s = s.replace("$\mbox{P}=\mbox{NP}$", "P =NP ")
+ s = s.replace("\mbox{“Salazar’s—”}", "“Salazar’s—”")
+ s = s.replace("170–{140}", "170–140")
+
+ # env to delete the optional parameters from
+ s = re.sub(r"\\begin\{(verse)\}\[[^\]]+\]", r"\\begin{\1}", s)
+
+ # spaces at start of line
+ s = re.sub("\n +", "\n", s)
+
+ #
+ # START OF REPLACEMENTS
+ #
+
+ # \chapters
+ myMatches = re.finditer(r"(\\chapter\{([^\}]+)\})", s)
+ for myMatch in myMatches:
+ was = myMatch.group(1)
+ womit = convert_chapter(myMatch.group(2))
+ s = s.replace(was, womit)
+ myMatches = re.finditer(r"(\\partchapter\{(.+?)\}\{(.+?)\})", s)
+ for myMatch in myMatches:
+ was = myMatch.group(1)
+ womit = convert_chapter(myMatch.group(2) + ", Part " + myMatch.group(3))
+ s = s.replace(was, womit)
+ # \namedpartchapter{The Stanford Prison Experiment}{TSPE}{VI}{Constrained Optimization}
+ myMatches = re.finditer(
+ r"(\\namedpartchapter\{([^\}]+)\}\{([^\}]+)\}\{([^\}]+)\}\{([^\}]+)\})", s
+ )
+ for myMatch in myMatches:
+ was = myMatch.group(1)
+ womit = convert_chapter(
+ myMatch.group(2) + ", Part " + myMatch.group(4) + ": " + myMatch.group(5)
+ )
+ s = s.replace(was, womit)
+
+ # simple commands without parameters
+ # \am and pm
+ s = re.sub(r"\\([ap])m\b", r" \1.m.", s, flags=re.DOTALL | re.IGNORECASE)
+ # \SPHEW
+ s = s.replace("\\SPHEW", "\\abbrev{SPHEW}")
+
+ # simple commands with 1 parameter not containing other commands
+ # custum spans
+ s = re.sub(
+ r"\\(abbrev|prophesy|scream|shout)\{([^\}\\]+)\}",
+ r'\2 ',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ # emph
+ s = re.sub(
+ r"\\emph\{([^\}\\]+)\}", r"\1 ", s, flags=re.DOTALL | re.IGNORECASE
+ )
+ # \sout = strike through
+ s = re.sub(
+ r"\\(sout)\{([^\}\\]+?)\}", r"\2 ", s, flags=re.DOTALL | re.IGNORECASE
+ )
+ # \url
+ s = re.sub(
+ r"\\(url)\{([^\}\\]+?)\}",
+ r'\2 ',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ # \textbf
+ s = re.sub(
+ r"\\(textbf)\{([^\}\\]+)\}", r"\2 ", s, flags=re.DOTALL | re.IGNORECASE
+ )
+ # \textsc
+ s = re.sub(
+ r"\\(textsc)\{([^\}\\]+)\}",
+ r'\2 ',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ # custum spans 2nd run
+ s = re.sub(
+ r"\\(abbrev|prophesy|scream|shout)\{([^\}\\]+)\}",
+ r'\2 ',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ # uppercase
+ s = re.sub(
+ r"\\(MakeUppercase|inlineheadline)\{([^\}\\]+)\}",
+ r'\2 ',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ # emph 2nd run
+ s = re.sub(
+ r"\\emph\{([^\}\\]+)\}", r"\1 ", s, flags=re.DOTALL | re.IGNORECASE
+ )
+ # \section{...} -> h3
+ s = re.sub(
+ r"\\(section)\{([^\}\\]+)\}", r"\2 ", s, flags=re.DOTALL | re.IGNORECASE
+ )
+
+ # emph
+ # emph in emph
+ s = re.sub(
+ r"\\emph\{([^\\\}]+)\\emph\{([^\\\}]+)\}([^\\\}])\}",
+ r"\1 \2\3 ",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+
+ # environments
+ s = s.replace("\\begin{writtenNote}\\centering", "\\begin{writtenNote}")
+
+ # letter writtenNote
+ s = re.sub(
+ r"\\begin\{writtenNote\}(.+?)\\end\{writtenNote\}",
+ r'\n',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ # letterAddress
+ s = re.sub(
+ r"\\letterAddress\{([^\}\\]+)\}", r"\1", s, flags=re.DOTALL | re.IGNORECASE
+ )
+ # letterClosing
+ s = re.sub(
+ r"\\letterClosing\[([^\]]+)\]\{([^\}\\]+)\}",
+ r"\1 \2",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ s = re.sub(
+ r"\\letterClosing\{([^\}\\]+)\}", r"\1", s, flags=re.DOTALL | re.IGNORECASE
+ )
+
+ # \begin{em} and emph
+ s = re.sub(
+ r"\\begin\{(em|emph)\}(.+?)\\end\{\1\}",
+ r'\2
\n',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+
+ # \begin{center} and centering
+ s = re.sub(
+ r"\\begin{(center)}\s*\\scshape(.+?)\\end\{\1\}",
+ r'\2
\n',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ s = re.sub(
+ r"\\begin\{(center|centering)\}(.+?)\\end\{\1\}",
+ r'\2
\n',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+
+ # \begin{samepage} -> ""
+ s = re.sub(
+ r"\\begin\{samepage\}(.+?)\\end\{samepage\}",
+ r"\1\n",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+
+ # \begin{verse}
+ s = re.sub(
+ r"\\begin\{verse\}(.+?)\\end\{verse\}",
+ r'\1
\n',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+
+ # \begin{playdialog}
+ s = re.sub(
+ r"\\begin\{playdialog\}(.+?)\\end\{playdialog\}",
+ r'\1
\n',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+
+ # \begin{headlines}
+ s = re.sub(
+ r"\\begin\{headlines\}(.+?)\\end\{headlines\}",
+ r'\1
\n',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ s = re.sub(
+ r"\\header\{(.+?)\}",
+ r'',
+ s,
+ flags=re.DOTALL,
+ )
+ s = re.sub(
+ r"\\label\{(.+?)\}",
+ r'\1 ',
+ s,
+ flags=re.DOTALL,
+ )
+ s = re.sub(
+ r"\\headline\{(.+?)\}",
+ r'\1 ',
+ s,
+ flags=re.DOTALL,
+ )
+
+ # \begin{enumerate} -> ol
+ s = re.sub(
+ r"\\begin\{enumerate\}\[(.)\.\](.+?)\\end\{enumerate\}",
+ r'\2 \n',
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+ s = re.sub(
+ r"\\begin\{enumerate\}(.+?)\\end\{enumerate\}",
+ r"\1 \n",
+ s,
+ flags=re.DOTALL | re.IGNORECASE,
+ )
+
+ s = re.sub(r"\s*\\item(.+?)\n", r"\1 \n", s)
+
+ # \parsel
+ myMatches = re.finditer(r"(\\parsel\{([^\}\\]+)\})", s)
+ for myMatch in myMatches:
+ was = myMatch.group(1)
+ womit = convert_parsel(myMatch.group(2))
+ s = s.replace(was, womit)
+
+ # \later
+ s = re.sub(
+ r"\\later\b", r'*
', s, flags=re.DOTALL | re.IGNORECASE
+ )
+
+ # footnotes_authorsnotetext
+ myMatches = re.finditer(
+ r"(\\authorsnotetext\{([^\}\\]+?)\})", s, flags=re.DOTALL | re.IGNORECASE
+ )
+ for myMatch in myMatches:
+ was = myMatch.group(1)
+ womit = f" [Author's Note: {myMatch.group(2).strip()} ] "
+ # womit = convert_footnotes(myMatch.group(2), authorsnote=True)
+ s = s.replace(was, womit)
+
+ # footnotetext
+ myMatches = re.finditer(
+ r"(\\footnotetext\{([^\}\\]+?)\})", s, flags=re.DOTALL | re.IGNORECASE
+ )
+ for myMatch in myMatches:
+ was = myMatch.group(1)
+ womit = f" [Author's Note: {myMatch.group(2).strip()} ] "
+ # womit = convert_footnotes(myMatch.group(2), authorsnote=False)
+ s = s.replace(was, womit)
+
+ # leftovers
+ s = re.sub(r"\{\s*\}", r"", s, flags=re.DOTALL)
+ s = re.sub(r"\[\s*\]", r"", s, flags=re.DOTALL)
+
+ # Latex spaces, etc
+ s = s.strip()
+
+ s = s.replace("\n\n", "\n")
+ # fixing p's
+ s = s.replace("
\n", "
")
+ s = s.replace("
", "")
+ s = s + "\n"
+ s = s.replace("
", "")
+ s = s.replace(" ", " ")
+ s = s.replace("
", "
")
+
+ s = re.sub(r"\]\s*", "] ", s, flags=re.DOTALL)
+
+ s = s.replace("
\n", "")
+ # multiple spaces
+ s = re.sub(r" +", r" ", s)
+ return s
+
+
+def convert_chapter(s: str) -> str:
+ global counter_chapter
+ counter_chapter += 1
+ # chapter class is used in calibre to detect chapters
+ out = f'{counter_chapter}. {s} '
+ return out
+
+
+def convert_parsel(s: str) -> str:
+ s = s.replace("ss", "ß").replace("s", "ss").replace("ß", "sss")
+ out = f'{s} '
+ return out
+
+
+# def convert_footnotes(s: str, authorsnote: bool = False) -> str:
+#
+# epub:type="noteref" only works for EPUB version 3.
+# at https://manual.calibre-ebook.com/generated/en/ebook-convert.html#epub-output-options
+# it is suggested
+# "EPUB 2 is the most widely compatible, only use EPUB 3 if you know you actually need it."
+# so I decided to uses inline author comments instead
+#
+# # \authorsnotetext{I do this in my own home.}
+# # ->
+# # 1
+# #
+# # if authorsnote = False -> I do this in my own home.
+# global counter_footnotes
+# counter_footnotes += 1
+# s_authorsnote = ""
+# if authorsnote:
+# s_authorsnote = "Author’s note: "
+# out = f""" {counter_footnotes}
+# """
+# return out
+
+
+def find_tex_commands(s: str) -> list:
+ l = []
+
+ myMatches = re.findall(r"\\[a-zA-Z0-9]+", s)
+ for myMatch in myMatches:
+ l.append(str(myMatch))
+
+ return l
+
+
+fhAll = open(f"{dir_out}/hpmor.html", mode="w", encoding="utf-8", newline="\n")
+fhAll.write(html_start)
+fhAll.write(html_preamble)
+
+
+l_tex_commands_unhandled = []
+
+for fileIn in sorted(glob.glob(f"{dir_tex_source}/hpmor-chapter-*.tex")):
+ # for fileIn in sorted(glob.glob(f"../chapters/hpmor-chapter-100.tex")):
+ (filePath, fileName) = os.path.split(fileIn)
+ (fileBaseName, fileExtension) = os.path.splitext(fileName)
+ fileOut = f"{dir_tmp}/{fileBaseName}.html"
+ with open(fileIn, mode="r", encoding="utf-8", newline="\n") as fh:
+ cont = fh.read()
+ cont = simplify_tex(cont)
+ cont = tex2html(cont)
+
+ l = find_tex_commands(cont)
+ l_tex_commands_unhandled.extend(l)
+ if len(l) > 0:
+ print(
+ f"WARN: there are leftover LaTeX commands in file {fileOut}:\n"
+ + ", ".join(l)
+ )
+
+ with open(fileOut, mode="w", encoding="utf-8", newline="\n") as fh:
+ fh.write(html_start + cont + html_end)
+
+ if fileBaseName == "hpmor-chapter-001":
+ cont = (
+ "Book 1: Harry James Potter-Evans-Verres and the Methods of Rationality \n"
+ + cont
+ )
+ elif fileBaseName == "hpmor-chapter-022":
+ cont = (
+ "Book 2: Harry James Potter-Evans-Verres and the Professor's Games \n"
+ + cont
+ )
+ elif fileBaseName == "hpmor-chapter-038":
+ cont = (
+ "Book 3: Harry James Potter-Evans-Verres and the Shadows of Death \n"
+ + cont
+ )
+ elif fileBaseName == "hpmor-chapter-065":
+ cont = (
+ "Book 4: Hermione Jean Granger and the Phoenix's Call \n"
+ + cont
+ )
+ elif fileBaseName == "hpmor-chapter-086":
+ cont = (
+ "Book 5: Harry James Potter-Evans-Verres and the Last Enemy \n"
+ + cont
+ )
+ elif fileBaseName == "hpmor-chapter-100":
+ cont = (
+ "Book 6: Harry James Potter-Evans-Verres and the Philosopher's Stone \n"
+ + cont
+ )
+
+ fhAll.write(cont)
+
+fhAll.write(html_end)
+fhAll.close()
+
+d_tex_commands_unhandled = {}
+for item in l_tex_commands_unhandled:
+ if item in d_tex_commands_unhandled:
+ d_tex_commands_unhandled[item] += 1
+ else:
+ d_tex_commands_unhandled[item] = 1
+# sort values reversed
+for key, value in sorted(
+ d_tex_commands_unhandled.items(), key=lambda item: item[1], reverse=True
+):
+ print(f"{value}\t{key}")
+
+assert (
+ len(d_tex_commands_unhandled) == 0
+), "Error: unhandled LaTeX commands found, see above"
diff --git a/ebook/2_html2epub.sh b/ebook/2_html2epub.sh
new file mode 100755
index 000000000..cdb710280
--- /dev/null
+++ b/ebook/2_html2epub.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# by Torben Menke https://entorb.net
+
+# run from within ebook dir via
+# ./2_html2epub.sh
+
+mkdir -p tmp
+
+echo 1. extract titlepage from PDF
+cp ../hpmor.pdf tmp/
+
+# 1.2 extract title page from PDF and convert to jpeg
+# 1.2a via imagemagick
+# sudo apt install imagemagick
+# convert -density 150 tmp/hpmor.pdf[0] -quality 75 tmp/title-en.jpg
+# imagemagick complains:
+# attempt to perform an operation not allowed by the security policy
+
+# 1.2b via ghostscript
+gs -dSAFER -r600 -sDEVICE=pngalpha -dFirstPage=1 -dLastPage=1 -o tmp/title-en.png tmp/hpmor.pdf
+# now imagemagick can be used for converting to the proper size
+convert -density 150 tmp/title-en.png -resize 1186x1186\> -quality 75 tmp/title-en.jpg
+
+echo 2. convert html to epub
+# use calibre instead of pandoc, as pandoc loses the css style
+# see https://manual.calibre-ebook.com/generated/en/ebook-convert.html
+# linux: sudo apt install calibre
+# windows: obtain from https://calibre-ebook.com/download_windows
+echo 2.1 calibre: html to epub
+ebook-convert output/hpmor.html output/hpmor.epub --no-default-epub-cover --cover tmp/title-en.jpg --authors "Eliezer Yudkowsky" --title "Harry Potter and the Methods of Rationality" --book-producer "Torben Menke" --pubdate 2015-03-14 --language en-US
+
+echo 2.2 calibre: epub to mobi
+ebook-convert output/hpmor.epub output/hpmor.mobi