1.0.9 rejects the Path2D objects pdfjs-dist 6.3.289 passes to fill/clip,
failing every raster test with "Value is none of these types `String`, `Path`".
Adding packages/pdf to CI turned it red on ubuntu-latest for two reasons,
neither of which is a real regression.
Rasterized pixels depend on the host font rasterizer. The chikorita, ditto
and all-template baselines differ on Linux in rasterSha256 alone: every page
count, item count and text coordinate is byte-identical to the macOS-authored
baseline. Compare the portable geometry on every host and the pixels only on
the platform the PNGs were generated on, so the characterization keeps
protecting layout without asserting another machine's antialiasing.
The picture-fit override case rasterizes twice and timed out at Vitest's 5s
default on a CI runner, with the date suite landing at 3.5-4.7s. Give the
package a 30s timeout rather than leaving every rendering test a runner
slowdown away from failing.
Verified on linux/amd64 in Docker: both files pass, 18/18.
The Lapras section marginTop added in c0c658c0 shifted every date marker in
that template down the page, but the date-layout characterization baseline
was not regenerated, so packages/pdf has been failing on main since. The
delta is geometry-only: same page count (2), same text item count (87), same
markers, x and width unchanged; 17 markers move on y and the raster hashes
follow.
Nothing caught it because the workflow ran test:ci for only server and
tooling, leaving 17 packages uncovered. Replace that filter list with the
full workspace run so a package cannot silently lose coverage again, and
move it after the migration and storage steps that the api suite needs.
Serial execution is deliberate. Running the packages in parallel oversubscribes
the runner and starves the PDF rasterization and API rate-limit suites past
their timeouts; 19/19 pass consistently at --concurrency=1.
* fix(pdf): isolate character metadata for cached font glyphs
* test(pdf): verify glyph aliases do not grow cache
* test(pdf): assert glyph aliases are unique
* feat(editor): support whole-paragraph indentation
* fix(docx): retain indentation in quotes and RTL documents
* fix(exports): bound paragraph insets in narrow PDF columns
* fix(pdf): type bounded paragraph rendering consistently
* test(editor): use explicit list conversion commands
* fix(docx): preserve quote inset on list items
* feat(skills): add inline layout option for skill items
* fix: restore default skills layout (regressed by inline feature)
- Restore metrics rowGap style for default layout
- Only render LevelDisplay inside the row for inline layout, not default
* refactor(pdf): Extract inline skills style logic from JSX to reusable function
* test(pdf): add test coverage for inline skills item layout
- Add test suite SkillsSectionInlineFormat to verify isInlineSkillsItem and getSkillsItemStyle behavior
* test(pdf): add comprehensive test coverage for inline skills item style logic
- Test combinations of proficiency, level, and keywords fields (0, 1, 3 fields)
* test(schema): add test coverage for column equals 1 when layout is inline
* test(web): add component-level tests for inline and columns layouts
* test(import): add v4 parser-level test for missing skills layout
* docs: regenerate skills layout references
* test(docx): include skills layout in section fixtures
---------
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
* fix(pdf): keep list markers with their first text fragment
* fix(pdf): preserve page breaks while rewinding list companions
* fix(pdf): consume oversized list marker presence hints
* test(pdf): allow cold startup for pagination process guard
* fix(pdf): key list presence spacer
* fix(pdf): add left padding to section heading text to prevent first-character clipping
Closes#3380
* fix(pdf): apply heading padding default after style composition
Apply paddingLeft: 1 only when no composed style fragment already defines it, so an explicit paddingLeft from a template or style rule is preserved. Keep the fallback for an empty style list.
* fix(pdf): keep heading safety padding on text only
---------
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
* feat(ats): add ATS checker and replace resume analysis
Adds a public, browser-only ATS checker at /ats-checker and an ATS Check
section in the builder's right sidebar. PDFs are parsed locally: text
extraction, reading order, contact and date recovery, section detection,
and file-level readability are scored deterministically, with evidence
cited per finding and skipped checks reported rather than counted as
passes.
Removes the AI-scored resume analysis it supersedes: the resume_analysis
table (dropped via migration), the get_resume_analysis MCP tool, and
POST /ai/analyze-resume. The replacement, POST /ai/ats-review, reviews
extracted resume text and returns qualitative feedback with no score.
Also bumps the version to 5.2.9 and adds the changelog entry.
* chore(deps): bump workspace dependencies
* fix(ats-checker): keep negation inside each 'what this does not do' bullet
The three bullets were bare fragments whose negation came from the
section heading, which translators never see. A dozen locales rendered
them as affirmative assertions or imperatives, so the page claimed the
checker enforces a one-page rule and predicts rejection -- the opposite
of the source, and directly contradicted by the sentence beside it.
Each bullet now carries its own negation, so the polarity cannot be
lost in translation. Re-extracted and refilled across all 53 target
locales.
* fix(pdf): resolve bold text weight from the family's bold face
Bold text (<strong>, rich-text bold, template bold styles) previously
rendered at the last stored body weight, which is ambiguous: families
are commonly stored as ["400","600"] (the typography picker's default
pairing), so bold rendered at SemiBold — nearly indistinguishable from
Regular for faces like Open Sans (#3310).
Add resolveBoldFontWeight() to the fonts package: keep a deliberate
stored bold-class choice (>= 700), else prefer the family's true Bold
face ("700"), else the heaviest >= 600 face; return null so callers
keep their existing fallback when the family has no bold-class face.
Wire it through use-register-fonts, the shared base-template-styles
builder, base-styles and the Scizor template. Default body IBM Plex
Serif ["400","500"] now renders bold at 700 (base-reset-fidelity
expectation updated accordingly).
Fixes#3310
* fix(pdf): register bold fallback faces for CJK glyph substitution
When resolveBoldFontWeight maps stored weights like ["400","600"] to the
family's 700 face, register that weight on each PDF fallback font too so
glyph-level substitution keeps bold glyphs instead of snapping to 600.
Also reorder @reactive-resume/fonts imports per Biome convention.
---------
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
* fix(pdf): render emoji via a Noto Emoji script fallback
Emoji in resume content (flags, globe, pictographs) rendered as mojibake
in the preview and PDF export because the per-codepoint fallback chain
registered no emoji-capable font: every font in the stack lacked the
glyphs, so layout fell through to single-byte standard-font encoding —
each UTF-16 code unit truncated to its low byte (#3321).
Follows the #2986/#3190 script-fallback pattern: detect emoji content
(regional indicators unioned with Extended_Pictographic), map it to the
monochrome Noto Emoji web font (TrueType glyf outlines, PDF-embeddable),
and register it in the fallback stack for both serif and sans stacks.
Out-of-range weight requests alias to the nearest served weight (300-700)
so registration never falls back to the preview subset.
* fix(pdf): detect keycap emoji via the combining enclosing keycap
Greptile review on #3351: keycap sequences like 1\uFE0F\u20E3 carry no
regional indicator and no Extended_Pictographic codepoint, so they
bypassed the emoji detector and rendered garbled — the exact class of
bug #3321 fixes. Union U+20E3 into the detector; every valid keycap
sequence contains it.
* Update packages/utils/src/locale.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* [autofix.ci] apply automated fixes
---------
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* fix(fonts): register Vazirmatn in webfont catalog for JSON imports
Imported resumes can set typography.fontFamily to Vazirmatn, but the
popularity-sorted Google Fonts slice omits it so PDF registration fell
back to IBM Plex Serif and Persian/Arabic glyphs stacked or tofu (#3098).
Add Vazirmatn as a locale-coverage manual entry (same pattern as Carlito)
and cover catalog resolution with unit tests.
* test(pdf): keep Vazirmatn as primary family for fa-IR registration
Prove JSON-imported Vazirmatn is handed to Font.register instead of
being rewritten to IBM Plex Serif (#3098).
* fix(fonts): address CodeRabbit review on Vazirmatn catalog
Assert getWebFontSource resolves files["400"] for Vazirmatn instead of
only matching the preview fallback, and split the font-generation log
line to satisfy the 120-column Biome limit.
Change richListItemContent from flex: initial to flex: 1 with minWidth: 0.
This makes the content area fill remaining space after the marker and
columnGap, so text wraps within the user-set margin instead of overflowing.
Fixes#3336