feat(cv): let a printed document set its typeface, course widths and sourcemaps - #18
Merged
Merged
Conversation
The printed layout in cv/preamble.tex hard-codes three things that an adopter
with a real bibliography and real course names has to fork the file to change.
eduardstan.github.io carries exactly such a fork, and it is the largest
recurring merge conflict between the two repositories.
Three \providecommand hooks, each read where it is used and each defaulting to
what the template prints today:
- \cvtypeface carries the whole font setup, not a font name. fontspec's
\setmainfont and an NFSS font package are different mechanisms and no single
argument spans them, so the macro holds either.
- \cvcoursecols takes the generated equal-width column specification and returns
the one to use. The tables are numbered by a new cvcoursetable counter stepped
outside the table, because tabularx reads its body more than once. The result
is expanded before array sees it, which array requires of a column
specification.
- \cvsourcemaps adds \map blocks to the one \DeclareSourcemap biblatex allows,
so a document can tidy its own entries without losing the template's two.
A document defines them before its \input{preamble.tex}. The template sets none,
no check asserts any, and the three baselines are unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
eduardstan
added a commit
that referenced
this pull request
Jul 31, 2026
…sourcemaps (#18) * feat(cv): let a document set its typeface, course widths and sourcemaps The printed layout in cv/preamble.tex hard-codes three things that an adopter with a real bibliography and real course names has to fork the file to change. eduardstan.github.io carries exactly such a fork, and it is the largest recurring merge conflict between the two repositories. Three \providecommand hooks, each read where it is used and each defaulting to what the template prints today: - \cvtypeface carries the whole font setup, not a font name. fontspec's \setmainfont and an NFSS font package are different mechanisms and no single argument spans them, so the macro holds either. - \cvcoursecols takes the generated equal-width column specification and returns the one to use. The tables are numbered by a new cvcoursetable counter stepped outside the table, because tabularx reads its body more than once. The result is expanded before array sees it, which array requires of a column specification. - \cvsourcemaps adds \map blocks to the one \DeclareSourcemap biblatex allows, so a document can tidy its own entries without losing the template's two. A document defines them before its \input{preamble.tex}. The template sets none, no check asserts any, and the three baselines are unchanged. * no-mistakes(review): docs: fix README claim that the three settings are unset * no-mistakes(document): note the three document settings in README variant list ---------
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Teach ledgerpress's shared printed-CV layout (cv/preamble.tex) to take instructions from a document instead of hard-coding a look, so that eduardstan.github.io can drop its forked cv/cv.tex and adopt the shared layout while its printed CV does not visibly change. That fork is the largest recurring merge conflict between the two repositories; this task is only the template half, a separate task switches the site over.
Exactly three differences were to be parameterised, and no more:
Hard constraints that were required and met:
Verification of the site's look was done by building a throwaway cv/proof.tex carrying the site's concrete values (cfr-lm + T1 fontenc, its two hand-tuned column width sets keyed on the counter, its four sourcemaps) and confirming it compiles and takes effect - cfr-lm and pplr8t/pplb8t/pplri8t in the log, visibly re-tuned course columns in the extracted text, the four extra maps in the .bcf. All three reproduce. That proof file was deleted and is not part of the change.
Two findings for the follow-on site task, deliberately left unimplemented here because they are outside the three-and-no-more scope: (a) \headerfontiii is a fourth thing the site changes (template \rmfamily vs site \fontfamily{ppl}\selectfont) but it needs no new mechanism because it is already \renewcommand-able after \input{preamble.tex}; (b) the site's fork also hard-codes the course header row wording, which the template now derives from the record's row keys - that is record curation, not layout.
The PR description must state explicitly the concrete settings values that reproduce the site's current look - typeface cfr-lm, its column widths, its four sourcemaps - and state plainly whether each one is achievable through what was built.
What Changed
cv/preamble.texnow\providecommands\cvtypeface, holding the whole font setup block rather than a font name (\setmainfonttakes an argument,\usepackage{cfr-lm}takes none, so no single value spans both). A document defines it before\input{preamble.tex}; the default still loadsfontspec+ TeX Gyre Pagella. eduardstan.github.io's\newcommand{\cvtypeface}{\usepackage{cfr-lm}\usepackage[T1]{fontenc}}reproduces its look through this — achievable. Its fourth difference,\headerfontiii(\fontfamily{ppl}\selectfont), needs no new mechanism: it is already\renewcommand-able after\input{preamble.tex}.\cvcoursessteps a newcvcoursetablecounter outside the table (tabularx reads its body more than once) and passes the generated equal-width column specification through\providecommanded\cvcoursecols,\edef-expanded via a new\cvcoursesprinthelper becausearrayrejects an unexpanded macro in a column specification withIllegal pream-token. The default returns#1unchanged. The site's two hand-tuned sets —P{3.1cm} P{3.8cm} Y P{1.2cm}forcvcoursetable=1,P{3.1cm} P{3.5cm} Y P{1.2cm}otherwise — are expressible as a single\ifnum\value{cvcoursetable}=1definition and print at the intended widths — achievable.\cvsourcemaps(empty by default) expands inside the template's existing\mapsblock, so a document's\mapblocks run after the template's two instead of silently replacing them — biblatex accepts exactly one\DeclareSourcemap. The site's four sourcemaps all apply, in order after the template's, without promoting them into the template as anyone's default — achievable. README.md gained "Three printed-CV settings a document carries itself" under "Make it yours" and AGENTS.md a pointer recording that the set must not grow into a theming system; no check asserts any of the three, per the repository's standing rule, and all three baselines rebuild byte-for-byte unchanged.Risk Assessment
✅ Low: Branch is three \providecommand-guarded layout hooks whose defaults reproduce today's output token-for-token plus documentation, and the only round-1 finding was a one-word README wording fix that is now applied correctly with no source change.
Testing
Rebuilt all three printed documents and proved the bundled example is untouched (baseline text hashes match and cv.pdf is pixel-identical to the recorded baseline PDF), then reconstructed the author's deleted proof document with the site's concrete cfr-lm typeface, counter-keyed course-column widths and four sourcemaps and confirmed each setting reaches the built PDF and the .bcf, including a probe map that visibly removed URLs from the printed bibliography while the template's own maps still applied; targeted node tests and the generated-data freshness gate also pass, and no new check asserts any of the three settings as the intent requires. The example record exposes two harmless notes rather than defects: the site's four maps are no-ops on it (no entry carries both doi and url, and it has no issn/eprint/series fields), and 8-bit cfr-lm cannot print that record's macrons — neither affects the shipped defaults. Node dependencies were missing and were installed to run the checks, then removed along with every build artifact, leaving the worktree clean.
/tmp/no-mistakes-evidence/01KYT4TC0HJS13GEN2YK92JZ0G/typeface-and-columns-side-by-side.png)/tmp/no-mistakes-evidence/01KYT4TC0HJS13GEN2YK92JZ0G/course-columns-per-table.png)Evidence: Bundled example unchanged: baseline hashes and pixel diff
pdftotext -layout SHA-256, built PDF vs data/cv-baseline/<name>-baseline.txt: cv/cv.pdf f52c70917b1b5d85c2f830f9a22b7e109c5b5b031280f239d736305aedbe8d0e (matches) cv/short.pdf 92b41fffe9f82491717fdedaf0932971e4954a8052e88938e0c1b4069402a190 (matches) cv/teaching.pdf 6b9b2bd2ad5db11eb05a6c53518a7bc009e3d1a9cd5820b7adafdfb123175c0e (matches) npm run check:maintainer -> exit 0 (all three baselines) Pixel comparison, built cv/cv.pdf vs data/cv-baseline/cv-baseline.pdf at 110 dpi: page 1: 0 differing pixels page 2: 0 differing pixels page 3: 0 differing pixelsEvidence: Typeface and sourcemap settings taking effect (log + .bcf + printed bibliography)
== 1. TYPEFACE (\cvtypeface) == cv.tex (template default): Package: fontspec 2022/01/15 / TeXGyrePagella proof.tex (site values): Package: cfr-lm 2015/02/01 / faces clm2j clmbx2j8t12 clmr2ij8t9 clmr2j8t10 clmr2j8t9 == 3. SOURCEMAPS (\cvsourcemaps), user-level \maps block in the .bcf biber reads == cv.bcf user maps: 2 (template's two) proof.bcf user maps: 6 (template's two, then the document's four, in that order) -- does a document \map actually reach the printed bibliography? -- proof.tex with the site's four maps: 3 URLs printed (none of the four bite on the bundled record: no entry has both doi and url, and it has no issn/eprint/series) proof.tex plus a probe \map{\step[fieldset=url, null]}: 0 URLs printed the template's own two maps still applied in the same buildEvidence: Course-table column text, default vs both hand-tuned tables
Evidence: Throwaway proof document used for verification (not part of the change; deleted from the worktree)
Evidence: Built PDFs: template defaults and the proof carrying the site's values
/tmp/no-mistakes-evidence/01KYT4TC0HJS13GEN2YK92JZ0G/px)Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed ✅
README.md:305- README says the three settings are "unset by default", but cv/preamble.tex \providecommand's all three (lines 76, 328, 416) and AGENTS.md states "Each is \providecommand'ed to today's look". An adopter probing with \ifdefined\cvtypeface after \input{preamble.tex} would find it defined. The rest of the sentence ("the template's own look is the default") is correct; only "unset" is wrong. Suggest "each defaults to what the template prints today".🔧 Fix: docs: fix README claim that the three settings are unset
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
latexmk -xelatex -cd cv/cv.tex,cv/short.tex,cv/teaching.tex(all three rebuilt from this branch)npm run check:maintainer— exit 0 for all three baselinespdftotext -layout <pdf> | sha256sumvsdata/cv-baseline/<name>-baseline.txtfor cv, short, teaching — all matchpdftoppm -r 110 -png+compare -metric AEof built cv/cv.pdf vs data/cv-baseline/cv-baseline.pdf — 0 differing pixels on pages 1-3Manual: built a throwawaycv/proof.texdefining \cvtypeface (cfr-lm + T1 fontenc), \cvcoursecols (P{3.1cm} P{3.8cm} Y P{1.2cm} for cvcoursetable=1, P{3.5cm} second column otherwise) and \cvsourcemaps (four \map blocks) before \input{preamble.tex}Manual:grep 'Package: cfr-lm' cv/proof.logand clm* face names vs fontspec/TeXGyrePagella in cv.logManual: user-level<bcf:maps>block of cv/proof.bcf (6 maps: template's 2 then the document's 4, in order) vs cv/cv.bcf (2)Manual:pdftotext -layoutof proof.pdf showing the two course tables printing at different column widthsManual probe: added\map{\step[fieldset=url, null]}and rebuilt — printed URLs went 3 to 0 while the template's own maps still appliedManual: rebuilt with README.md's documented package order\usepackage{cfr-lm}\usepackage[T1]{fontenc}— clean build, cfr-lm still in effectnode --test scripts/build-cv-data.test.mjs scripts/check-cv-baseline.test.mjs— 50 passnode scripts/build-cv-data.mjs --check— generated data fresh✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.