mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 00:26:26 +00:00
fix(verify_pdf): fold LaTeX's typographic substitutions before --contains; guard T1 fontenc for pdflatex (#385, #384) (#458)
`normalize_text()` folded whitespace only, so `--contains` compared what a
user types against what LaTeX renders. The stock CV compiled with the
documented lualatex command turns `'` into U+2019 and `--` into U+2013, so
`--contains "Master's degree"` and `--contains "2016-2024"` both reported
the keyword missing from a document that plainly contains it, through both
extractors. The documented remedy for a missing keyword is to add it, which
is the one thing the ATS section forbids.
Fold both sides at comparison time: NFC, then curly apostrophes and quotes
to ASCII, en/em dashes to `-`, no-break space to space. `--dump-text` still
writes the raw layer - that is what an ATS parses, and the date-range rule
in 05-cv-templates.md needs the raw en-dash visible there.
Separately, pdflatex without T1 font encoding stores accents decomposed
(`e` + U+0300). NFC repairs the pdftotext side of that, but pypdf reads the
same layer as `Z¨ urich` with a spacing accent, which no fold recovers.
moderncv 2.5 loads T1 itself under pdflatex; the apt-packaged 2.3.1 does
not - reproduced by compiling the template against moderncv v2.3.1 with
pdflatex (before: U+0308/U+0300 in pdftotext, `Z¨ urich` in pypdf; after:
U+00FC/U+00E8 in both). The template and the guide's preamble gain
`\ifpdftex\usepackage[T1]{fontenc}\fi`; the lualatex text layer is
byte-identical before and after.
Tests: ten new cases in test_verify_pdf.py (the fold-through and
normalize_text ones fail on the whitespace-only code) and a
test_latex_guidance.py guard that the fontenc line exists and stays inside
the pdflatex branch. framework_version 1.4.3 -> 1.4.4 on 05-cv-templates.md.
Reported and diagnosed by 9scorp4 in Discussions #385 and #384.
This commit is contained in:
@@ -136,5 +136,45 @@ class TestAtsExtractionEncoding(unittest.TestCase):
|
||||
self.assert_pdftotext_commands_pin_utf8(CV_TEMPLATES)
|
||||
|
||||
|
||||
class TestPdflatexFontEncodingGuard(unittest.TestCase):
|
||||
"""#384: the pdflatex fallback must load T1 fontenc, and only under pdflatex.
|
||||
|
||||
Without T1, pdflatex stores accented letters decomposed in the text layer
|
||||
(`e` + U+0300), so an ATS keyword match on `Genève` fails while the PDF
|
||||
looks right. moderncv 2.5 loads T1 itself; the apt-packaged 2.3.1 does not.
|
||||
The line must be guarded so the documented lualatex path is untouched.
|
||||
"""
|
||||
|
||||
GUARDED_FONTENC = re.compile(r"\\ifpdftex\s*\\usepackage\[T1\]\{fontenc\}\s*\\fi")
|
||||
|
||||
def assert_has_guarded_fontenc(self, path):
|
||||
text = path.read_text(encoding="utf-8")
|
||||
self.assertRegex(
|
||||
text,
|
||||
self.GUARDED_FONTENC,
|
||||
f"{path.name} must carry `\\ifpdftex\\usepackage[T1]{{fontenc}}\\fi` so a "
|
||||
"pdflatex fallback keeps accents precomposed in the text layer",
|
||||
)
|
||||
unguarded = [
|
||||
f"{path.name}:{lineno}: {line.strip()}"
|
||||
for lineno, line in enumerate(text.splitlines(), 1)
|
||||
if "fontenc" in line
|
||||
and not line.lstrip().startswith("%")
|
||||
and not self.GUARDED_FONTENC.search(line)
|
||||
]
|
||||
self.assertEqual(
|
||||
unguarded,
|
||||
[],
|
||||
"fontenc must stay inside the \\ifpdftex guard - lualatex output "
|
||||
"must not change:\n" + "\n".join(unguarded),
|
||||
)
|
||||
|
||||
def test_example_cv_guards_fontenc_for_pdflatex(self):
|
||||
self.assert_has_guarded_fontenc(EXAMPLE_CV)
|
||||
|
||||
def test_cv_guide_preamble_guards_fontenc_for_pdflatex(self):
|
||||
self.assert_has_guarded_fontenc(CV_TEMPLATES)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user