diff --git a/slides_template/fonts.tex b/slides_template/fonts.tex new file mode 100644 index 0000000..747563b --- /dev/null +++ b/slides_template/fonts.tex @@ -0,0 +1,19 @@ +% Font setup shared by the slides and by the notes pages that mknotes +% typesets, so that the two match. Loaded by example.tex directly and passed +% to mknotes with --preamble. +% +% Requires fontspec, which unicode-math pulls in on the slides side. Paths must +% stay relative to the document directory: LuaTeX fails to resolve a +% parent-relative Path= and dies with an unhelpful empty-filename error. + +\setsansfont{OpenSans}[ + Path = fonts/OpenSans/, Extension = .ttf, + UprightFont = *-Regular, BoldFont = *-Bold, + ItalicFont = *-Italic, BoldItalicFont = *-BoldItalic] + +\setmonofont{RobotoMono}[ + Path = fonts/RobotoMono/, Extension = .ttf, + UprightFont = *-Regular, BoldFont = *-Bold, + ItalicFont = *-Italic, BoldItalicFont = *-BoldItalic] + +\renewcommand{\familydefault}{\sfdefault} diff --git a/slides_template/mknotes b/slides_template/mknotes index f6a9118..7ecb1f4 100755 --- a/slides_template/mknotes +++ b/slides_template/mknotes @@ -151,6 +151,10 @@ def attach_notes(pages, notes): #: Side margin on the generated notes pages, as a fraction of the page. HMARGIN = 0.06 +#: Font setup shared with the slides. Used when it exists, so that the notes +#: match the deck without anyone having to remember a flag. +DEFAULT_PREAMBLE = "fonts.tex" + # The page is laid out by hand rather than by geometry: the header band runs # full bleed to the top and outer edges to meet the thumbnail, so the margins # have to be applied per element instead of to the page. @@ -159,11 +163,13 @@ NOTES_PREAMBLE = r"""\documentclass{article} margin=0bp]{geometry} \usepackage{parskip} \usepackage{xcolor} +\usepackage{fontspec} \definecolor{notegrey}{RGB}{230,230,230} \pagestyle{empty} \setlength{\parindent}{0pt} \setlength{\fboxsep}{0pt} \setlength{\topskip}{0pt} +%(preamble)s \begin{document} """ @@ -179,7 +185,7 @@ def escape_for_context(text): def build_notes_pdf(pages, width, height, thumb_w, thumb_h, workdir, - jobname="notes"): + preamble=None, jobname="notes"): """Typeset one notes page per slide page and return the resulting PDF path. The page is made at the size it will actually occupy in the finished sheet, @@ -198,7 +204,14 @@ def build_notes_pdf(pages, width, height, thumb_w, thumb_h, workdir, textwidth = width - hmargin * 2 bandwidth = width - thumb_w - body = [NOTES_PREAMBLE % {"width": width, "height": height}] + # \input rather than the file's contents, so that anything relative inside + # it (font paths, say) resolves against the document directory. LaTeX runs + # from there even though its output goes to the working directory. + extra = r"\input{%s}" % preamble if preamble else "" + + body = [NOTES_PREAMBLE % { + "width": width, "height": height, "preamble": extra, + }] total = len(pages) for index, page in enumerate(pages): @@ -249,9 +262,12 @@ def build_notes_pdf(pages, width, height, thumb_w, thumb_h, workdir, with open(source, "w", encoding="utf-8") as handle: handle.write("\n".join(body) + "\n") + # Run from the document directory so relative paths in the preamble resolve, + # but send the output elsewhere so the build leaves nothing behind. result = subprocess.run( - ["lualatex", "-interaction=nonstopmode", "-halt-on-error", jobname + ".tex"], - cwd=workdir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + ["lualatex", "-interaction=nonstopmode", "-halt-on-error", + "-output-directory=" + workdir, source], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) output = os.path.join(workdir, jobname + ".pdf") if result.returncode != 0 or not os.path.exists(output): @@ -294,6 +310,10 @@ def main(): help="base name of the build, e.g. 'example'") parser.add_argument("--output", help="output file (default: -notes.pdf)") + parser.add_argument("--preamble", metavar="FILE", default=DEFAULT_PREAMBLE, + help="LaTeX fragment to \\input into the notes pages, " + "so they can share the slides' font setup " + f"(default: {DEFAULT_PREAMBLE} when it exists)") parser.add_argument("--thumb-scale", type=float, default=0.29, metavar="F", help="slide thumbnail size on the notes half, as a " @@ -321,10 +341,19 @@ def main(): width, height = float(box.width), float(box.height) thumb = max(0.0, min(args.thumb_scale, 0.9)) + # A preamble named on the command line has to be there; the default one is + # a convenience and is skipped when the document does not use it. + preamble = args.preamble + if preamble and not os.path.exists(preamble): + if preamble != DEFAULT_PREAMBLE: + raise SystemExit(f"mknotes: {preamble} not found") + preamble = None + workdir = tempfile.mkdtemp(prefix="mknotes-") try: notes_pdf = PdfReader(build_notes_pdf( - pages, width, height, width * thumb, height * thumb, workdir)) + pages, width, height, width * thumb, height * thumb, workdir, + preamble=preamble)) writer = assemble_notes(slides, notes_pdf, width, height, thumb) output = args.output or f"{args.jobname}-notes.pdf"