#!/usr/bin/env python3 """The way from Markdown to the PDF of a company, measured at the PDF. The converter is the second road into the same design, and the two have to end in the same page: the same font, the same logo, the same table that fits the line. What is measured here is the result and not the call. """ import os import re import shutil import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import support from support import Check, Page EXAMPLES = os.path.join(support.ROOT, "examples") CONVERTER = os.path.join(support.ROOT, "bin", "md2pdf.py") OUT = os.path.join(support.BUILD, "md2pdf") def runnable(check): """The converter is called by its name, so it has to be executable. The README describes exactly that way — a link in a directory of the PATH — and a file without the bit ends in "Permission denied". Reported on 2026-09-21 from a company that had linked it and called it. """ check.that(os.access(CONVERTER, os.X_OK), "bin/md2pdf.py carries no execute bit, so a link to it on the " "PATH cannot be called by its name") def convert(check): shutil.rmtree(OUT, ignore_errors=True) os.makedirs(OUT, exist_ok=True) result = support.run(["python3", CONVERTER, "--identity", "nordwind", "--out-dir", OUT, "--keep-tex", os.path.join(EXAMPLES, "report.md")]) pdf = os.path.join(OUT, "report.pdf") check.that(os.path.exists(pdf), f"the converter produced no PDF:\n{result.stdout[-1500:]}") return pdf def measured(check): """The line and the character were measured, not taken from the fallback. The numbers the filters compute a column width from belong to the paper, the margins and the face of the company. They live outside TeX, in a Lua filter, so the converter builds a probe with the identity of the company and reads them out of it. Without the probe every company would compute with the numbers of the company this suite came from. """ metrics = os.path.join(OUT, "build", "metrics.txt") check.that(os.path.exists(metrics), "the converter wrote no measurement of the line") if not os.path.exists(metrics): return with open(metrics, encoding="utf-8") as handle: found = dict(re.findall(r"^(\w+) ([0-9.]+)pt$", handle.read(), re.MULTILINE)) check.that("linewidth" in found, "the probe measured no line width") if "linewidth" not in found: return # A4 is 597.5 points wide and the template keeps 1cm on each side, so the # line is 540 points give or take the rounding of the paper. width = float(found["linewidth"]) check.that(530 < width < 545, f"the measured line is {width} points wide, which is not the " "type area of an A4 page with the margins of this template") def inside(check, pdf): """Nothing of the document stands outside the type area. A Markdown table says nothing about its column widths, and without the filter LaTeX writes the last words of a wide table past the edge of the paper. """ page = Page(pdf, 1) factor = 72.0 / page.dpi columns = page.ink_columns() right = columns[-1] * factor left = columns[0] * factor # The paper is 595 points wide and the template keeps 1cm, 28.35 points, on # each side. Half a point of tolerance for the rounding of the raster. check.that(left > 27, f"ink stands {left:.1f} points from the left edge, " "inside the margin") check.that(right < 568, f"ink reaches {right:.1f} points, past the right " "margin of the page") def content(check, pdf): text = support.text(pdf) check.that("Lorem Ipsum" in text, "the title block did not arrive") check.that("Consetetur sadipscing elitr" in text and "41'820" in text, "the table did not arrive") # The path in the running text is broken at the characters that structure # it, so it stays inside the line. What arrives in the text layer is the # path with its pieces, never a line that runs past the margin. check.that("business-identity.sty" in text.replace("\n", ""), "the path in the running text did not arrive") faces = support.fonts(pdf) check.that(any("DejaVu" in face for face in faces), f"the marks did not reach the page: {faces}") # A formula in the three ways a writer writes one, and a fourth that is none: # inside a fenced code block the dollars are an example of themselves and stay # on the page as text. FORMULA = """--- title: Formula author: Erika Muster --- # Consetetur Inline $E = mc^2$ in a sentence. $$a = \\frac{b}{c}$$ $$ \\text{Ratio} = \\frac{\\text{Nutzen}} {\\text{Kosten}} $$ ```text $$ x = y $$ ``` """ def formula(check): """A formula arrives as a formula, also written over several lines. The grammar of the reader sees a line carrying nothing but `=` as the underline of a heading, so a formula over several lines used to come out as a heading of the first level with the dollars in it, and the build stayed green. Reported on 2026-09-21 from another company of this family, which built a twelve-page analysis with the formula standing as a green heading. """ path = os.path.join(support.BUILD, "formula.md") os.makedirs(support.BUILD, exist_ok=True) with open(path, "w", encoding="utf-8") as handle: handle.write(FORMULA) result = support.run(["python3", CONVERTER, "--identity", "nordwind", "--out-dir", OUT, "--keep-tex", path]) pdf = os.path.join(OUT, "formula.pdf") check.that(os.path.exists(pdf), f"the document with the formula did not build:\n" f"{result.stdout[-1500:]}") if not os.path.exists(pdf): return text = support.text(pdf) # The dollars of a formula never reach the page; the ones in the code block # do, and they are the proof that nothing was repaired in there. body = text.split("$$")[0] if "$$" in text else text check.that(text.count("$$") == 2, f"the page carries {text.count('$$')} rows of dollars, and only " "the two of the code block belong there") check.that("Ratio" in body and "Nutzen" in body, "the formula over several lines did not arrive as a formula") check.that("Kosten" in body, "the second half of the formula is missing") faces = support.fonts(pdf) check.that(any("Math" in face or "math" in face for face in faces), f"no mathematics face on a page with three formulas: {faces}") # The same document twice: once written in Markdown, once in LaTeX. A document # looks the same whichever road it takes, so the two pages carry their ink in # the same places. TWIN_BODY = ( ("Consetetur", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna " "aliquyam erat."), ("Dolor Sit", "Stet clita kasd gubergren, no sea takimata sanctus est " "Lorem ipsum dolor sit amet."), ("Sadipscing Elitr", "At vero eos et accusam et justo duo dolores et ea " "rebum."), ) TWIN_MD = "---\ntitle: Lorem Ipsum\nauthor: Erika Muster\ndate: 21 September 2026\n---\n\n" \ + "\n".join(f"{'#' * (level + 1)} {head}\n\n{text}\n" for level, (head, text) in enumerate(TWIN_BODY)) TWIN_TEX = r"""\documentclass[a4paper,10pt]{article} \usepackage{nordwind} \title{Lorem Ipsum} \author{Erika Muster} \date{21 September 2026} \begin{document} \maketitle """ + "\n".join( "\\%s{%s}\n\n%s\n" % (("section", "subsection", "subsubsection")[level], head, text) for level, (head, text) in enumerate(TWIN_BODY)) + r""" \end{document} """ def bands(pdf): """The rows of ink of the first page, grouped into blocks, in points.""" page = Page(pdf, 1) factor = 72.0 / page.dpi grouped, current = [], [] for row in page.ink_rows(): if current and row - current[-1] > 1: grouped.append((current[0] * factor, current[-1] * factor)) current = [] current.append(row) if current: grouped.append((current[0] * factor, current[-1] * factor)) return grouped # A picture out of Markdown: the same rules as one written in LaTeX, and the # converter is what says which of them is a raster of pixels. PICTURE_MD = """--- title: Lorem Ipsum author: Erika Muster --- ## Consetetur ![Lorem ipsum dolor sit amet](example-image-16x9.pdf) ## Dolor Sit ![Consetetur sadipscing elitr](example-image-a.png) """ def pictures(check): """A picture out of Markdown takes the size of the family. The converter hands every picture to pandoc's own bound, and that is where the rules of the template hang: the lying picture on the first page takes the whole line, the raster one on the second is the one the converter marks as pixels, and both stand centred. """ path = support.write("picture.md", PICTURE_MD) result = support.run(["python3", CONVERTER, "--identity", "nordwind", "--out-dir", OUT, path]) pdf = os.path.join(OUT, "picture.pdf") check.that(os.path.exists(pdf), f"the document with the pictures did not build:\n" f"{result.stdout[-1000:]}") if not os.path.exists(pdf): return for number, name in ((1, "lying"), (2, "raster")): page = Page(pdf, number) factor = 72.0 / page.dpi rows = page.ink_rows() check.that(rows, f"the {name} picture is not on page {number}") if not rows: continue grouped, current = [], [] for row in rows: if current and row - current[-1] > 1: grouped.append(current) current = [] current.append(row) grouped.append(current) tallest = max(grouped, key=lambda band: band[-1] - band[0]) columns = page.ink_columns(tallest[0], tallest[-1] + 1) width = (columns[-1] - columns[0] + 1) * factor left = columns[0] * factor right = page.width * factor - columns[-1] * factor check.that(width <= 545, f"the {name} picture is {width:.1f} points wide, more than " "the 540.6 of the line") check.that(abs(left - right) < 4, f"the {name} picture stands {left:.1f} points from the left " f"edge and {right:.1f} from the right, so it is not centred") def twins(check): """A document out of Markdown is the document out of LaTeX. Both roads end in the same template, so the same content has to stand in the same places: same distances under the title block, same three lines over every heading, same line for every paragraph. """ md = support.write("twin.md", TWIN_MD) result = support.run(["python3", CONVERTER, "--identity", "nordwind", "--out-dir", OUT, md]) converted = os.path.join(OUT, "twin.pdf") check.that(os.path.exists(converted), f"the twin out of Markdown did not build:\n" f"{result.stdout[-1000:]}") written, _ = support.build(support.write("twin.tex", TWIN_TEX), EXAMPLES) if not os.path.exists(converted): return out_of_md, out_of_tex = bands(converted), bands(written) check.that(len(out_of_md) == len(out_of_tex), f"the page out of Markdown carries {len(out_of_md)} blocks of " f"ink and the one out of LaTeX {len(out_of_tex)}") if len(out_of_md) != len(out_of_tex): return worst = max(abs(a[0] - b[0]) for a, b in zip(out_of_md, out_of_tex)) check.that(worst < 1, f"a block of the page stands {worst:.1f} points lower out of " "Markdown than out of LaTeX") def missing(check): """A document that does not exist ends in a message and never in a PDF.""" result = support.run(["python3", CONVERTER, "--identity", "nordwind", "--out-dir", OUT, os.path.join(OUT, "gibtesnicht.md")]) check.that(result.returncode != 0, "the converter reported success for a file that does not exist") check.that("gibtesnicht.md" in result.stdout, "the message does not name the file that is missing") check.that(not os.path.exists(os.path.join(OUT, "gibtesnicht.pdf")), "the converter produced a PDF for a file that does not exist") def main(): check = Check("md2pdf") runnable(check) pdf = convert(check) if os.path.exists(pdf): measured(check) inside(check, pdf) content(check, pdf) formula(check) pictures(check) twins(check) missing(check) return check.done() if __name__ == "__main__": sys.exit(main())