Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ limitations under the License.
Third-party fonts
-----------------

This product includes the Poppins typeface (src/assets/fonts/), embedded in
downloaded resume PDFs. Copyright 2020 The Poppins Project Authors
(https://github.com/itfoundry/Poppins), licensed under the SIL Open Font
License, Version 1.1. Full license text: src/assets/fonts/Poppins-OFL.txt.
This product includes the Liberation Sans typeface (src/assets/fonts/),
embedded in downloaded resume PDFs. Digitized data copyright 2010 Google
Corporation with Reserved Font Arimo, Tinos and Cousine; copyright 2012 Red
Hat, Inc. with Reserved Font Name Liberation, licensed under the SIL Open
Font License, Version 1.1. Full license text:
src/assets/fonts/LiberationSans-OFL.txt.
2 changes: 1 addition & 1 deletion scripts/check-no-literal-nul.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const ROOT = fileURLToPath(new URL("..", import.meta.url));
*
* Every entry is a format that carries NULs as a matter of course, so scanning
* it would report thousands of findings about files nobody edits by hand. The
* live ones in this repo are the two Poppins faces under `src/assets/fonts/`
* live ones in this repo are the two Liberation Sans faces under `src/assets/fonts/`
* (~15k NULs each) and the 58 PDF fixtures under `tests/fixtures/pdfs/`; the
* rest are here so the first `.png` or `.woff2` someone commits does not fail
* the build for a reason that has nothing to do with them.
Expand Down
2 changes: 1 addition & 1 deletion scripts/check-no-literal-nul.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe("nulPositions", () => {

describe("isBinaryPath", () => {
it("skips the font and PDF assets that legitimately carry NULs", () => {
expect(isBinaryPath("src/assets/fonts/Poppins-Bold.ttf")).toBe(true);
expect(isBinaryPath("src/assets/fonts/LiberationSans-Bold.ttf")).toBe(true);
expect(isBinaryPath("tests/fixtures/pdfs/latex/awesome-cv-cv.pdf")).toBe(true);
});

Expand Down
2 changes: 1 addition & 1 deletion scripts/fixture-derive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
* prints or persists a résumé field VALUE.
*
* Runs under vite-node (`runCascade` needs no pdfjs `?url` setup here). The
* round-trip hop's Poppins font fetch fails under vite-node and falls back to
* round-trip hop's body-font fetch fails under vite-node and falls back to
* Helvetica with a stderr warning — expected and harmless; the render succeeds.
*
* Usage:
Expand Down
14 changes: 7 additions & 7 deletions scripts/fixtures/gen-extended-latin-name.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* The mechanism is the #295 `toWinAnsi()` render sanitizer, whose lossy
* degradation #326 recorded as an accepted by-design tradeoff (degrade a glyph,
* never crash the export). `toWinAnsi()` runs on the STANDARD-FONT path — i.e.
* whenever the Poppins embed does not happen: always under Node (the font is a
* whenever the Liberation Sans embed does not happen: always under Node (the font is a
* bundler `?url` asset there is no server to fetch), and in the browser whenever
* that fetch fails. The two `experience` baselines #326 records
* (`google-docs-skia-proxy-classic`, `weasyprint-cairo-classic`) are the same
Expand All @@ -35,8 +35,8 @@
* left to round-trip — a separate, real defect, filed nowhere yet.
*
* The SOURCE pdf must embed a font that actually has the glyph (pdf-lib's
* `StandardFonts` throw on it), so this generator embeds the repo's own Poppins
* via fontkit — the same TTF `render-ats-pdf.ts` tries to embed.
* `StandardFonts` throw on it), so this generator embeds the repo's own
* Liberation Sans via fontkit — the same TTF `render-ats-pdf.ts` embeds.
Comment thread
s-annam marked this conversation as resolved.
*
* Everything is drawn as a SINGLE column so `detectColumnBoundaries` finds no
* gutter (`triggers` == `[]`).
Expand Down Expand Up @@ -73,14 +73,14 @@ const BLACK = rgb(0, 0, 0);
const doc = await PDFDocument.create();
doc.registerFontkit(fontkit);
const page = doc.addPage([612, 792]);
// `subset: true` keeps the committed fixture small — the full Poppins pair is
// ~300 kB, and this résumé touches under a hundred glyphs.
// `subset: true` keeps the committed fixture small — the full Liberation Sans
// pair is well over 100 kB, and this résumé touches under a hundred glyphs.
const font = await doc.embedFont(
readFileSync(join(FONT_DIR, "Poppins-Regular.ttf")),
readFileSync(join(FONT_DIR, "LiberationSans-Regular.ttf")),
{ subset: true },
);
const bold = await doc.embedFont(
readFileSync(join(FONT_DIR, "Poppins-Bold.ttf")),
readFileSync(join(FONT_DIR, "LiberationSans-Bold.ttf")),
{ subset: true },
);

Expand Down
Binary file added src/assets/fonts/LiberationSans-Bold.ttf
Binary file not shown.
102 changes: 102 additions & 0 deletions src/assets/fonts/LiberationSans-OFL.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
Digitized data copyright (c) 2010 Google Corporation
with Reserved Font Arimo, Tinos and Cousine.
Copyright (c) 2012 Red Hat, Inc.
with Reserved Font Name Liberation.

This Font Software is licensed under the SIL Open Font License,
Version 1.1.

This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL

SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007

PREAMBLE The goals of the Open Font License (OFL) are to stimulate
worldwide development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to provide
a free and open framework in which fonts may be shared and improved in
partnership with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves.
The fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.



DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such.
This may include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components
as distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting ? in part or in whole ?
any of the components of the Original Version, by changing formats or
by porting the Font Software to a new environment.

"Author" refers to any designer, engineer, programmer, technical writer
or other person who contributed to the Font Software.


PERMISSION & CONDITIONS

Permission is hereby granted, free of charge, to any person obtaining a
copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,in
Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole, must
be distributed entirely under this license, and must not be distributed
under any other license. The requirement for fonts to remain under
this license does not apply to any document created using the Font
Software.



TERMINATION
This license becomes null and void if any of the above conditions are not met.



DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
DEALINGS IN THE FONT SOFTWARE.

Binary file added src/assets/fonts/LiberationSans-Regular.ttf
Binary file not shown.
Binary file removed src/assets/fonts/Poppins-Bold.ttf
Binary file not shown.
93 changes: 0 additions & 93 deletions src/assets/fonts/Poppins-OFL.txt

This file was deleted.

Binary file removed src/assets/fonts/Poppins-Regular.ttf
Binary file not shown.
59 changes: 51 additions & 8 deletions src/hooks/useDownloadPdf.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,37 @@ describe("useDownloadPdf — download-source tagging (#313)", () => {
/**
* #664 — refuse rather than silently substituting "?" in the user's own fields.
*
* These cases need no font stub: under Node the Poppins `?url` asset is a bare
* path that `fetch` cannot resolve, so the export ALWAYS takes the Helvetica
* fallback here. That is the same condition a browser hits when the font fetch
* fails, which makes this environment the natural place to test the refusal —
* the ASCII cases above prove the same path still downloads normally.
* The real, correctly-served embedded font (Liberation Sans) covers ś/ł, so
* there is nothing to refuse over on the happy path — `render-ats-pdf.fonts.
* test.ts`'s "reports nothing when the embedded font loads" pins that. To
* exercise the refusal, this block stubs `fetch` to fail and forces a fresh
* module instance via `vi.resetModules()` + a dynamic re-import (the same
* pattern `render-ats-pdf.fonts.test.ts` uses) rather than relying on the
* statically-imported `useDownloadPdf` above: `render-ats-pdf.ts` memoizes a
* SUCCESSFUL font fetch at module scope for the life of that module instance
* (#664 — so a repeat download doesn't re-fetch), and the tagging describe
* above already forced one real success against the shared instance. Without
* a fresh module, `loadBodyFontBytes()` would short-circuit on that cached
* success and never call the failing stub at all.
*/
describe("useDownloadPdf — glyph-loss refusal (#664)", () => {
let freshUseDownloadPdf: typeof useDownloadPdf;

beforeEach(async () => {
vi.stubGlobal(
"fetch",
vi.fn(async () => {
throw new Error("network unavailable (simulated)");
}),
);
vi.resetModules();
({ useDownloadPdf: freshUseDownloadPdf } = await import("./useDownloadPdf.ts"));
});

afterEach(() => {
vi.unstubAllGlobals();
});

function namedResult(fullName: string): CascadeResult {
const base = uploadedResult();
return {
Expand All @@ -177,8 +201,27 @@ describe("useDownloadPdf — glyph-loss refusal (#664)", () => {
};
}

function FreshProbe({ result }: { result: CascadeResult }) {
const score = computeAnonymousAtsScore({
parsed: result.canonical.fields,
fieldConfidence: result.canonical.fieldConfidence,
triggers: result.triggers,
rawText: result.rawText,
sections: result.canonical.sections,
});
api = freshUseDownloadPdf(result, score, onDownloaded);
return null;
}

function mountFresh(result: CascadeResult): void {
container = document.createElement("div");
document.body.appendChild(container);
root = createRoot(container);
act(() => root.render(<FreshProbe result={result} />));
}

it("refuses the download and names the field when the fallback would mangle the name", async () => {
mount(namedResult("ANNA WIŚNIEWSKA"));
mountFresh(namedResult("ANNA WIŚNIEWSKA"));

await act(async () => {
await api.download();
Expand All @@ -192,7 +235,7 @@ describe("useDownloadPdf — glyph-loss refusal (#664)", () => {
});

it("fires no download, no analytics event and no journey mark when it refuses", async () => {
mount(namedResult("ANNA WIŚNIEWSKA"));
mountFresh(namedResult("ANNA WIŚNIEWSKA"));

await act(async () => {
await api.download();
Expand All @@ -209,7 +252,7 @@ describe("useDownloadPdf — glyph-loss refusal (#664)", () => {
it("still downloads a pure-ASCII résumé on the same failing-font path", async () => {
// The gate that keeps a font problem from blocking everyone: this is the
// identical code path as the refusal above, differing only in the data.
mount(namedResult("Jane Candidate"));
mountFresh(namedResult("Jane Candidate"));

await act(async () => {
await api.download();
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useDownloadPdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* temporary object URL.
*
* Zero-egress holds, but not because nothing is fetched: the renderer DOES issue
* a `fetch` for the vendored Poppins TTFs, and this docblock previously claimed
* a `fetch` for the vendored Liberation Sans TTFs, and this docblock previously claimed
* "no network request is made (no font fetch, no upload)", which was false. The
* fetch targets the app's own bundled-asset origin — never a font CDN — so no
* résumé bytes leave the browser, which is the actual guarantee. Say custody,
Expand All @@ -22,7 +22,7 @@
* after the bytes have reached the user: the refusal below is the one thing that
* stops a download, and reporting must never grow into a second one.
*
* This hook owns the refusal for #664. When the Poppins fetch fails, the
* This hook owns the refusal for #664. When the font fetch fails, the
* renderer falls back to Helvetica, whose WinAnsi codec replaces anything
* outside it with `?` — including a candidate's own name. Rather than hand back
* a PDF reading `ANNA WI?NIEWSKA`, the hook probes first and refuses, because
Expand Down
Loading
Loading