Skip to content

feat(font): NewSyntheticBoldFont — faux bold for families with no bold instance - #169

Merged
tannevaled merged 1 commit into
mainfrom
feat/synthetic-bold
Aug 8, 2026
Merged

feat(font): NewSyntheticBoldFont — faux bold for families with no bold instance#169
tannevaled merged 1 commit into
mainfrom
feat/synthetic-bold

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Why

Some faces ship no bold weight the rasteriser can reach. The motivating case is a host UI typeface: macOS's SFNS is a variable font and the pure-Go engine renders its default (Regular) master only, so an application matching the native look ends up with a Regular face and no bold at all — headings then draw at body weight, silently.

What

NewSyntheticBoldFont over-strikes the run one pixel right, the same trick X11 and early PostScript printers used. It composes over any Font, including a fallback chain, so a script the primary lacks is emboldened too instead of staying light next to bold Latin.

  • Measure includes the over-strike column, so the reported width covers every pixel Draw paints. Without it two adjacent runs would overlap by a column, and a right-aligned or clipped string would lose its final stem.
  • Advance and Height are the base face's — over-striking thickens glyphs, it does not re-space them.
  • Ascent, FontData and SizePx forward when the base exposes them, so positioning and vector export keep working through the wrapper, and fall back to zero/nil when it does not.

The doc comment is explicit that a designed bold is always better; this is for when the family offers none.

Verification

  • go test -race100.0% of statements, race-clean.
  • Proved on pixels: the same string paints more coverage than the base face and reaches exactly one column further right; a Thai run behind a Latin primary is emboldened through the chain.

🤖 Generated with Claude Code

…d instance

Some faces ship no bold weight the rasteriser can reach. The motivating case is
a host UI typeface: macOS's SFNS is a variable font, and the pure-Go engine
renders its default (Regular) master only, so an application matching the native
look ends up with a Regular face and no bold at all — headings then draw at body
weight, silently.

NewSyntheticBoldFont over-strikes the run one pixel right, the same trick X11
and early PostScript printers used. It composes over ANY Font, including a
fallback chain, so a script the primary lacks is emboldened too instead of
staying light next to bold Latin.

Measure includes the over-strike column, so the reported width covers every
pixel Draw paints: without it two adjacent runs would overlap by a column, and a
right-aligned or clipped string would lose its final stem. Advance and Height
are the base face's — over-striking thickens glyphs, it does not re-space them.
Ascent, FontData and SizePx forward when the base exposes them, so positioning
and vector export keep working through the wrapper, and fall back to zero/nil
when it does not.

The doc is explicit that a designed bold is always better; this is for when the
family offers none.

100% coverage; race-clean. Proved on pixels: the same string paints more
coverage than the base face and reaches exactly one column further right.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 92086ae into main Aug 8, 2026
1 check passed
@tannevaled
tannevaled deleted the feat/synthetic-bold branch August 8, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant