One LaTeX design for a company, in three templates
A company that writes with LaTeX ends up with a document class, a letter class and a presentation theme that share a design and not a line of code. Every fix is then made three times, two of them late and the third never. Here the page is written once. A company declares its colours, faces and logo in one file, and the document template, the letter class and the presentation theme read that one file. Nothing in the suite carries a colour value, a font name or a file name of any company, which is what lets it be published while the companies stay private. Every measure of the page follows from a measurement or from a named definition: the room the head and the foot need is taken from the boxes they really build, one line of the body text stands between the head and the text and between the text and the foot, a heading never stands alone at the foot of a page, a picture takes the size of the family, and a Markdown file reaches the same page as the same document written in LaTeX. The suite ships with a company that does not exist, Nordwind AG, so that it builds and is measured anywhere: three colours, the TeX Gyre families every installation carries, and an icon drawn in TikZ. 574 checks over five measurements, all of them on the rendered page or on the build log rather than on the source. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
adc58e19d7
39 files changed
+6627
No files matched your search
+340
@@ -0,0 +1,340 @@
|
||||
#!/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
|
||||
|
||||

|
||||
|
||||
## Dolor Sit
|
||||
|
||||

|
||||
"""
|
||||
|
||||
|
||||
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())
|
||||
Reference in new issue
Block a user