/** * Rendering must not depend on earlier renders in the same process (src/pdf/quirks.test.tsx, quirk 13). * This file runs in a fresh module context, so the first document below really is the first one that * touches Poppins Regular. */ import { createElement as h } from "react"; import { Font, Text } from "@react-pdf/renderer"; import { describe, expect, it } from "vitest"; import { doc } from "../testing/docs"; import { parsePdfText } from "../testing/pdfText"; import { renderCore } from "../render/core"; import { setupPdfTest } from "../testing/setup"; import { FONT_MANIFEST } from "./manifest"; import { familySources, stack } from "./register"; setupPdfTest(); type Glyph = { id: number; codePoints: number[]; components?: Array<{ glyphID: number }> }; type FkFont = { characterSet: number[]; glyphForCodePoint(cp: number): Glyph; getGlyph(id: number): Glyph; numGlyphs: number }; const fontOf = (family: string, fontWeight: number, fontStyle: string) => (Font as unknown as { getFont: (d: object) => { data: FkFont } }).getFont({ fontFamily: family, fontWeight, fontStyle }).data; const poppins = { fontFamily: stack("Poppins"), fontSize: 10, lineHeight: "12pt", fontWeight: 400 }; describe("glyph priming", () => { it("a colon set in Poppins Regular before any period does not stop a later amount being extracted as text", async () => { await renderCore(doc(h(Text, { style: poppins }, "Bank : State"))); const r = await renderCore(doc(h(Text, { style: poppins }, "₹1,200.00"))); const pages = await parsePdfText(r.bytes); const text = pages.pages.flatMap((p) => p.items.map((i) => i.str)).join(""); expect(text).toContain("₹1,200.00"); }); it("the period glyph keeps its code point", () => { expect(fontOf("Poppins", 400, "normal").glyphForCodePoint(0x2e).codePoints).toEqual([0x2e]); }); it("every glyph that a composite uses as a component is created with a code point, in every bundled face", async () => { // Reads fontkit's glyph cache and cmap directly: asking for a glyph through the public API would create it with code points and hide the bug. type Peek = { numGlyphs: number; _glyphs: Record; _cmapProcessor: { lookup(cp: number): number }; _getBaseGlyph(id: number): { _decode?(): { components?: Array<{ glyphID: number }> } | null }; }; const bad: string[] = []; let checked = 0; for (const fam of FONT_MANIFEST.families) { for (const s of familySources(fam)) { await Font.load({ fontFamily: fam.family, fontWeight: s.fontWeight, fontStyle: s.fontStyle }); const font = fontOf(fam.family, s.fontWeight, s.fontStyle); const peek = font as unknown as Peek; const mapped = new Set(font.characterSet.filter((cp) => cp < 0xe000 || cp > 0xf8ff).map((cp) => peek._cmapProcessor.lookup(cp))); const comps = new Set(); const cache = peek._glyphs; peek._glyphs = {}; for (let id = 0; id < peek.numGlyphs; id++) for (const c of peek._getBaseGlyph(id)._decode?.()?.components ?? []) comps.add(c.glyphID); peek._glyphs = cache; for (const id of comps) { if (!mapped.has(id)) continue; checked++; if ((peek._glyphs[id]?.codePoints.length ?? 0) === 0) bad.push(`${fam.family} ${s.file} component glyph ${id}`); } } } expect(checked).toBeGreaterThan(1000); expect(bad).toEqual([]); }); });