Skip to content

Stage B, PR 1: bmf sub-object and memoised flattenGlyph#65

Merged
aurpelai merged 1 commit into
mainfrom
feat/stage-b-readers
Jun 21, 2026
Merged

Stage B, PR 1: bmf sub-object and memoised flattenGlyph#65
aurpelai merged 1 commit into
mainfrom
feat/stage-b-readers

Conversation

@aurpelai

Copy link
Copy Markdown
Owner

Summary

First of three PRs finishing the Stage A → Stage B migration. Splits today's conflated Glyph.xoffset/yoffset/xadvance (which is doing two jobs: editor union-bbox origin + BMF char-line metadata) into:

  • glyph.bmf: { xoffset, yoffset, xadvance } — the BMF char-line nudge from source-font metrics (TTF bearings or parsed .fnt). Independent of where ink sits in the layer buffer.
  • flattenGlyph(glyph).xoffset/yoffset — the union-bbox origin of inked layers. Pure derivation, now memoised via WeakMap<Glyph, FlattenedGlyph> (Glyph mutations always produce a new object, so reference identity tracks invalidation).

Every reader has been migrated to one or the other; every writer populates bmf correctly. Legacy top-level fields (pixels/width/height/xoffset/yoffset/xadvance) stay populated by syncLegacyFields for this PR — they're marked @deprecated and PR 2 deletes them along with a v6 DB migration. PR 3 then rolls PortableFont to v3 with full layer-stack round-trip.

Behavioural fix surfaced during migration

Moving a TTF-imported glyph no longer overwrites its source-font bearing. Previously, the move tool updated the single glyph.xoffset/yoffset field — which was the BMF metadata and the flatten origin — so dragging a glyph silently destroyed the TTF bearing recorded at import time. Now bmf.xoffset/yoffset are independent of layer offsets, so moves only affect the flatten origin.

Side cleanup

DEFAULT_XADVANCE_RATIO = 0.7 added to config/font-defaults.ts and used by makeBlankGlyph. The 0.7 was previously a magic number that happened to match CAP_HEIGHT_RATIO — but they're different concepts and shouldn't drift together.

Test plan

  • pnpm test — 108 tests pass, including new flattenGlyph cache suite
  • pnpm build — type-check + production build clean
  • pnpm lint — no warnings
  • Manually verify in the editor: paint a glyph, add/remove/reorder layers, switch glyphs, repack atlas, export .fnt + PNG, export .bmffont.json and re-import — confirm no visible regression
  • Verify a TTF-imported glyph still exports at its original baseline position (regression check on the bmf.xoffset + flat.xoffset + placement.trimX export formula)

🤖 Generated with Claude Code

…, PR 1)

Splits the conflated Glyph.xoffset/yoffset/xadvance into glyph.bmf (BMF
char-line metadata) and flattenGlyph(glyph) (union-bbox origin), and adds
a WeakMap-memoised flattenGlyph so callers can switch off the legacy
top-level fields without per-call recomputation.

Legacy fields stay populated by syncLegacyFields for one more PR (mirror
writes, deprecate reads); PR 2 deletes them and runs the v6 DB migration.

Behavioural fix surfaced during migration: moving a TTF-imported glyph
no longer overwrites its source-font bearing — bmf.xoffset/yoffset are
now independent of where ink sits in the layer buffer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@aurpelai aurpelai merged commit ecb7956 into main Jun 21, 2026
1 check passed
@aurpelai aurpelai deleted the feat/stage-b-readers branch June 21, 2026 21:00
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