How the PDF Compilation Loop Verifies Page Counts and Prevents Orphaned Entries
The repository implements a repeat-until-valid workflow where tools/verify_pdf.py enforces exact page counts using pdfinfo while LaTeX macros like \needspace prevent section titles from separating from their content.
The MadsLorentzen/ai-job-search repository automates professional document generation through a robust PDF compilation loop that guarantees strict layout constraints. This system combines Python-based verification with LaTeX spacing controls to ensure every CV and cover letter meets exact page requirements while eliminating orphaned headings.
The Repeat-Until-Valid Compilation Workflow
The architecture follows a compile-verify-adjust cycle that repeats until all validation criteria pass. The process begins with LaTeX compilation engines and concludes with automated verification scripts.
LaTeX Compilation Engines
The CV compiles with lualatex while the cover letter uses xelatex, both running in non-stop mode to enable automated iteration. As noted in the repository documentation at line 221 of README.md, these engines handle the modern font requirements and Unicode characters common in international CVs.
The Verification Layer
After each compilation, the script tools/verify_pdf.py performs three critical checks to validate the generated PDF. This Python module serves as the gatekeeper for document quality, ensuring that layout adjustments continue until the output meets specifications.
How Page Count Verification Works
The verification script extracts metadata and content from the compiled PDF to enforce strict pagination requirements.
Extracting Page Counts with pdfinfo
The script calls pdfinfo to retrieve the actual page count and compares it against the expected_pages parameter. In tools/verify_pdf.py, lines 49-55 implement this check:
if expected_pages is not None:
actual_pages = parse_page_count(run_tool(["pdfinfo", str(pdf_path)]))
if actual_pages != expected_pages:
raise VerificationError(
f"expected {expected_pages} page(s), found {actual_pages}"
)
This guarantees that CVs are exactly 2 pages and cover letters are exactly 1 page, matching the requirements supplied by the CI job or Claude assistant.
Text Layer Validation
Beyond page counts, the script validates content presence using pdftotext to ensure the PDF contains extractable text. Lines 59-62 check that the document meets a minimum character threshold, preventing compilation errors that might produce blank or corrupted pages. Optional required string validation (lines 64-66) further ensures critical content sections exist.
Preventing Orphaned Entries with LaTeX Macros
While Python handles pagination verification, the LaTeX templates prevent visual orphans through spacing macros documented in .claude/skills/job-application-assistant/05-cv-templates.md (lines 70-80).
The needspace Macro
The \needspace{5\baselineskip} command appears before every \cventry in the LaTeX source. This macro forces LaTeX to check for sufficient remaining vertical space before starting a new entry. If insufficient space exists, LaTeX automatically inserts a page break, preventing section headings from appearing at the bottom of a page while their bullet lists flow to the next.
Dynamic Page Adjustment with enlargethispage
When content nearly fits but spills slightly onto an additional page, the \enlargethispage{2-3\baselineskip} command pulls extra lines onto the current page. This fine-tuning capability allows the compilation loop to compress sections slightly rather than consuming an entire new page for one or two lines of content.
Implementation in CI and Automation
The verification loop integrates directly into the continuous integration pipeline defined in .github/workflows/ci.yml.
CI Pipeline Integration
The workflow compiles the example CV using lualatex, then immediately invokes the verification script to enforce the two-page requirement. If verify_pdf raises a VerificationError, the Claude assistant or CI system adjusts the LaTeX source—adding \needspace or tuning \enlargethispage values—before recompiling. This cycle continues until the PDF satisfies both page count and content requirements.
You can run the verifier manually to check existing PDFs:
# Verify that a CV is exactly 2 pages and contains at least 100 characters
python -m tools.verify_pdf cv/main_example.pdf --pages 2 --min-chars 100
Python Implementation Example
A typical automation script implements the loop as follows:
import subprocess
from pathlib import Path
from tools.verify_pdf import verify_pdf, VerificationError
pdf_path = Path("cv/main_example.pdf")
while True:
# Compile with lualatex (non-stop mode)
subprocess.run(
["lualatex", "-interaction=nonstopmode", "-halt-on-error", "cv/main_example.tex"],
check=True,
)
try:
verify_pdf(pdf_path, expected_pages=2, min_chars=100)
print("✅ PDF is valid")
break
except VerificationError as e:
print(f"⚠️ Verification failed: {e}")
# Assistant would edit the .tex (e.g., insert \needspace) before next iteration
Summary
tools/verify_pdf.pyenforces exact page counts usingpdfinfoand validates content presence viapdftotext- The repeat-until-valid workflow automatically recompiles documents when verification fails, adjusting LaTeX spacing macros between iterations
\needspace{5\baselineskip}prevents orphaned headings by ensuring sufficient space exists before starting new entries\enlargethispageallows minor page compression to avoid single-line overflows onto new pages- The system guarantees CVs are exactly 2 pages and cover letters are exactly 1 page through automated CI integration
Frequently Asked Questions
What happens if the PDF page count doesn't match the expected value?
The verify_pdf function raises a VerificationError immediately when actual_pages differs from expected_pages. This exception triggers the compilation loop to adjust the LaTeX source—typically by modifying spacing macros—and recompile until the page count matches the requirement exactly.
How does \needspace prevent orphaned entries in LaTeX?
The \needspace{5\baselineskip} command checks that at least five lines of vertical space remain on the current page before rendering a \cventry section. If insufficient space exists, LaTeX inserts a page break first, ensuring that section titles never appear isolated at the bottom of a page while their content flows to the next.
Can I run the verification script manually outside of the CI pipeline?
Yes, the tools/verify_pdf.py module supports command-line execution. You can verify any PDF manually by specifying the expected page count and minimum character requirements: python -m tools.verify_pdf path/to/file.pdf --pages 2 --min-chars 100.
Why does the repository use different LaTeX engines for CVs versus cover letters?
The CV uses lualatex to handle complex Unicode characters and modern font features common in international applications, while the cover letter employs xelatex for its robust font handling capabilities. Both engines support the spacing macros required for orphan prevention and integrate seamlessly with the Python verification layer.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →