Skip to content

Add Fonts namespace (TrueType/SFNT font parsing) - #12

Merged
Malcolmnixon merged 17 commits into
mainfrom
feature/fonts-truetype
Sep 22, 2026
Merged

Malcolmnixon merged 17 commits into
mainfrom
feature/fonts-truetype

Conversation

@Malcolmnixon

Copy link
Copy Markdown
Member

Implements Phase 5 of the CanvasNet roadmap: a new DemaConsulting.CanvasNet.Fonts namespace with TrueType/SFNT font support.

  • Public \TrueTypeFont: Load, glyph index lookup (cmap formats 4/12), glyph outline extraction to \Geometry.Path, advance-width metrics (hmtx/hhea), basic kerning (kern format 0)
  • Internal \SfntContainer/\CmapTable/\GlyfLocaReader/\HmtxHheaReader/\KernTable\ parsers, hardened against untrusted/malformed input: overflow-safe bounds checks, bounded composite-glyph recursion (depth + total-component caps), explicit CFF/OTTO rejection
  • Full companion artifacts: reqstream requirements, design docs, verification docs, SysML2 model, user guide

Passed local code-review and formal-review before opening this PR (established process for this session).

Copilot AI lite review requested due to automatic review settings September 22, 2026 16:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Eight moderate findings remain in malformed-input validation, glyph bounds, table validation, and synthetic cmap fixtures.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 3 Medium severity

Open (3)
What changed in this PR

Adds dependency-free TrueType/SFNT font parsing, glyph outlines, metrics, kerning, tests, and supporting documentation.

Changes:

  • Added public TrueTypeFont API and internal font parsers.
  • Added synthetic unit and integration tests.
  • Added requirements, design, verification, user-guide, SysML2, and review artifacts.
File Summary
test/​DemaConsulting.CanvasNet.Tests/​TestSupport/​SyntheticFontBuilder.cs Moderate findings: format-4 and format-12 fixture lengths are zero (1 vote each).
test/​DemaConsulting.CanvasNet.Tests/​Fonts/​TrueTypeFontTests.cs Reviewed; no final findings.
test/​DemaConsulting.CanvasNet.Tests/​Fonts/​SfntContainerTests.cs Reviewed; no final findings.
test/​DemaConsulting.CanvasNet.Tests/​Fonts/​KernTableTests.cs Reviewed; no final findings.
test/​DemaConsulting.CanvasNet.Tests/​Fonts/​HmtxHheaReaderTests.cs Reviewed; no final findings.
test/​DemaConsulting.CanvasNet.Tests/​Fonts/​GlyfLocaReaderTests.cs Reviewed; no final findings.
test/​DemaConsulting.CanvasNet.Tests/​Fonts/​CmapTableTests.cs Reviewed; no final findings.
test/​DemaConsulting.CanvasNet.Tests/​CanvasNetTests.cs Reviewed; no final findings.
src/​DemaConsulting.CanvasNet/​Fonts/​TrueTypeFont.cs Moderate findings: validate cmap glyph IDs (2 votes), require complete version-1 maxp data (1 vote), and bound kerning indices (1 vote).
src/​DemaConsulting.CanvasNet/​Fonts/​SfntContainer.cs Reviewed; no final findings.
src/​DemaConsulting.CanvasNet/​Fonts/​NamespaceDoc.cs Reviewed; no final findings.
src/​DemaConsulting.CanvasNet/​Fonts/​KernTable.cs Reviewed; no final findings.
src/​DemaConsulting.CanvasNet/​Fonts/​HmtxHheaReader.cs Reviewed; no final findings.
src/​DemaConsulting.CanvasNet/​Fonts/​GlyfLocaReader.cs Moderate findings: validate repeat counts (2 votes) and mutually exclusive composite transform flags (1 vote).
src/​DemaConsulting.CanvasNet/​Fonts/​CmapTable.cs Moderate finding: use overflow-safe subtable offset arithmetic at lines 73 and 92 (3 votes).
requirements.yaml Reviewed; no final findings.
README.md Reviewed; no final findings.
docs/​verification/​introduction.md Reviewed; no final findings.
docs/​verification/​canvas-net/​fonts/​true-type-font.md Reviewed; no final findings.
docs/​verification/​canvas-net/​fonts.md Reviewed; no final findings.
docs/​verification/​canvas-net.md Reviewed; no final findings.
docs/​user_guide/​introduction.md Reviewed; no final findings.
docs/​sysml2/​views/​design-views.sysml Reviewed; no final findings.
docs/​sysml2/​model/​canvas-net/​fonts/​true-type-font.sysml Reviewed; no final findings.
docs/​sysml2/​model/​canvas-net/​fonts.sysml Reviewed; no final findings.
docs/​sysml2/​model/​canvas-net/​drawing.sysml Reviewed; no final findings.
docs/​sysml2/​model/​canvas-net.sysml Reviewed; no final findings.
docs/​reqstream/​canvas-net/​fonts/​true-type-font.yaml Reviewed; no final findings.
docs/​reqstream/​canvas-net/​fonts.yaml Reviewed; no final findings.
docs/​reqstream/​canvas-net.yaml Reviewed; no final findings.
docs/​design/​introduction.md Reviewed; no final findings.
docs/​design/​canvas-net/​fonts/​true-type-font.md Reviewed; no final findings.
docs/​design/​canvas-net/​fonts.md Reviewed; no final findings.
docs/​design/​canvas-net/​drawing/​path-filler.md Reviewed; no final findings.
docs/​design/​canvas-net/​drawing.md Reviewed; no final findings.
docs/​design/​canvas-net.md Reviewed; no final findings.
.reviewmark.yaml Reviewed; no final findings.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/DemaConsulting.CanvasNet/Fonts/CmapTable.cs Outdated
Comment thread src/DemaConsulting.CanvasNet/Fonts/GlyfLocaReader.cs
Comment thread src/DemaConsulting.CanvasNet/Fonts/TrueTypeFont.cs Outdated
…cmap glyph clamp

- CmapTable.cs: use overflow-safe (long) arithmetic for the subtable-offset
  bounds check in Parse (an offset near int.MaxValue previously wrapped
  around and passed the check, leading to an IndexOutOfRangeException
  downstream), and apply the same overflow-safe pattern to the equivalent
  entry-point bounds checks in TryParseFormat4/TryParseFormat12.
- GlyfLocaReader.cs: reject a simple-glyph flag repeat count that exceeds
  the remaining point slots with InvalidDataException instead of silently
  clipping it to the remaining count, matching this class's documented
  malformed-input contract.
- TrueTypeFont.cs: clamp GetGlyphIndex's result to 0 when the cmap subtable
  maps a codepoint to a glyph index outside [0, GlyphCount), so callers can
  always safely pass its result straight to GetGlyphOutline; updated the
  XML doc comment to make this guarantee explicit.
- Added/extended unit tests reproducing each malformed-input scenario using
  SyntheticFontBuilder synthetic fonts (no third-party font fixtures).
Copilot AI review requested due to automatic review settings September 22, 2026 17:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved input-validation, kerning-safety, and composite-glyph handling issues remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
Resolved since last review (3)
Previously missed (3)

In code that hasn't changed since last review

Medium severity Enforce declared lengths when parsing cmap subtables

src/​DemaConsulting.CanvasNet/​Fonts/​CmapTable.cs:94

These branches bound format-4/12 parsing by the enclosing cmap table, but never enforce each subtable's declared length. A malformed candidate can therefore consume bytes belonging to a following subtable and return bogus mappings instead of falling back to 0, contrary to CmapTable's malformed-input contract. Derive and validate a per-subtable end from the format-specific length before parsing.

Medium severity Reject odd format-4 segCountX2 values

src/​DemaConsulting.CanvasNet/​Fonts/​CmapTable.cs:164

segCountX2 is a byte count and must be even, but dividing an odd value truncates it while the subsequent offsets still use the odd raw count. That misaligns the format-4 arrays and can produce mappings from malformed data instead of returning the documented empty/fallback result; reject odd values before deriving segCount.

Medium severity Handle composite component offset scaling flags

src/​DemaConsulting.CanvasNet/​Fonts/​GlyfLocaReader.cs:461

Composite flags for SCALED_COMPONENT_OFFSET and UNSCALED_COMPONENT_OFFSET are not decoded, so every component translation is passed through unchanged. A valid composite that requests a scaled offset will therefore have its outline displaced incorrectly; handle the offset-mode flags (or reject unsupported modes) before calling AppendTransformed, and cover the scaled-offset case.

Comment thread src/DemaConsulting.CanvasNet/Fonts/KernTable.cs Outdated
Copilot AI review requested due to automatic review settings September 22, 2026 17:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Five moderate issues remain unresolved in cmap validation, composite offsets, glyph truncation handling, and maxp validation.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 3 Medium severity

Open (4)

Comment thread src/DemaConsulting.CanvasNet/Fonts/CmapTable.cs Outdated
Comment thread src/DemaConsulting.CanvasNet/Fonts/GlyfLocaReader.cs Outdated
Comment thread src/DemaConsulting.CanvasNet/Fonts/TrueTypeFont.cs
…posite glyph offset scaling

- KernTable.Parse: widen the subtable end-position arithmetic (`pos + 6` and
  `pos + subtableLength`) to `long` before comparing against `tableEnd`, matching
  the overflow-safe pattern already used elsewhere in this file and in
  SfntContainer/CmapTable. Prevents a wrapped (negative) subtable end from
  bypassing the bounds check and later driving a corrupted `pos` into an
  out-of-bounds read.

- CmapTable.Parse: bound each candidate format-4/format-12 subtable by its own
  declared `length` field (not just the enclosing `cmap` table's end), via a new
  `ComputeSubtableEnd` helper. Previously a subtable could read past its own
  declared length into a following subtable's bytes and return a bogus (but
  structurally passable) mapping instead of being rejected.

- CmapTable.TryParseFormat4: reject an odd `segCountX2` outright instead of
  silently truncating `segCount` via integer division, which previously
  misaligned the parallel endCode/startCode/idDelta/idRangeOffset arrays and
  could still produce a bogus (rather than failing) lookup.

- GlyfLocaReader.DecodeCompositeGlyph: honor the `SCALED_COMPONENT_OFFSET` /
  `UNSCALED_COMPONENT_OFFSET` composite glyph flags. When a component sets
  `SCALED_COMPONENT_OFFSET` (and not `UNSCALED_COMPONENT_OFFSET`), its dx/dy
  translation is now transformed through the component's own scale/2x2 matrix
  before being applied, instead of always being applied unscaled.

Added SyntheticFontBuilder support for the new composite glyph flags, corrected
CmapFormat4/CmapFormat12 to write their subtables' real declared length (no
longer a placeholder now that CmapTable enforces it), and added regression
tests for each of the four fixes - each verified to fail against the pre-fix
code and pass against the post-fix code. Updated the composite-glyph design
doc, reqstream, and verification docs for the newly-observable
SCALED_COMPONENT_OFFSET behavior.
Malcolm Nixon added 2 commits September 22, 2026 14:12
A version-1.0 'maxp' table only had its 6-byte version+numGlyphs
prefix validated, so a font declaring version 1.0 but truncated to
6 bytes was incorrectly accepted instead of throwing
InvalidDataException per this class's documented structural-
validation contract. A version-1.0 maxp table is exactly 32 bytes
per the OpenType/TrueType spec; require that length once the
version field confirms 1.0.

Also updates SyntheticFontBuilder's maxp fixture to emit a full,
valid 32-byte version-1.0 table (placeholder values for the fields
this library never reads) instead of the previously under-sized
6-byte form, and adds a regression test proving a truncated
version-1.0 maxp table is rejected (confirmed to fail against the
pre-fix code and pass post-fix).
…sed)

Relocates the staged OpenSans-Regular.ttf and OpenSans-OFL.txt
fixtures from the repo root into a new test/.../FontFixtures/
folder (mirroring the existing PngSuite/TiffFixtures/JpegFixtures
content-item convention), renaming the license file to
OpenSans.LICENSE to match PngSuite.LICENSE's naming, and adding a
README.md documenting provenance and the SIL OFL 1.1 terms.

Wires FontFixtures\**  into the test project as CopyToOutputDirectory
content, then adds a genuine integration test
(TrueTypeFontRealFontIntegrationTests) that loads the real font via
TrueTypeFont.Load, resolves a real glyph index for 'A', extracts its
real outline, transforms it from font design units (y-up) into pixel
space (y-down, scaled/margined to fit a 128x128 Surface), fills it
via PathFiller, and asserts on the actual rendered pixels: ink is
present within the glyph's transformed bounding box, and the
canvas's far corners remain fully transparent. Also checks a real
positive advance width and that a self-kerning lookup does not
throw. No reqstream/design/verification artifacts are touched - this
is a test-only addition proving existing documented behavior.
Copilot AI review requested due to automatic review settings September 22, 2026 18:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread .reviewmark.yaml
Comment thread src/DemaConsulting.CanvasNet/Fonts/CmapTable.cs
Comment thread src/DemaConsulting.CanvasNet/Fonts/CmapTable.cs Outdated
Comment thread src/DemaConsulting.CanvasNet/Fonts/CmapTable.cs
Comment thread src/DemaConsulting.CanvasNet/Fonts/KernTable.cs Outdated
Comment thread docs/sysml2/model/canvas-net/fonts/true-type-font.sysml Outdated
Comment thread docs/verification/canvas-net/fonts.md Outdated
Comment thread docs/verification/canvas-net/fonts/true-type-font.md Outdated
Malcolm Nixon added 3 commits September 22, 2026 14:49
… round 2)

- CmapTable format-4: reject subtables whose segments are not strictly
  increasing/non-overlapping by endCode, whose startCode exceeds endCode, or
  whose final segment is not the mandatory 0xFFFF terminator.
- CmapTable format-4: widen the indirect idRangeOffset glyph-index address
  computation to long arithmetic and reject addresses that point backward
  (before glyphIdArray) or past the subtable end, instead of only bounds-
  checking the upper side with unwidened int arithmetic.
- CmapTable format-12: reject subtables whose groups are not strictly
  increasing/non-overlapping by (startCharCode, endCharCode).
- KernTable format-0: widen the body-offset bounds check to long arithmetic
  to match the other overflow-safe checks already in this file.
- Add regression tests for all four fixes (CmapTableTests, KernTableTests),
  each verified to fail against the pre-fix code and pass post-fix.
- .reviewmark.yaml: add the missing test file to the Fonts unit review set.
- true-type-font.sysml: add the missing test file to the unit's testRef.
- fonts.md / fonts/true-type-font.md: correct the verification environment
  description - one integration test now loads a real, licensed (SIL OFL 1.1)
  OpenSans-Regular.ttf production font fixture to prove end-to-end
  composition with the Drawing pipeline on real-world glyph data; all other
  Fonts tests remain synthetic-fixture-based.
- Require both startCode and endCode of the final format-4 segment to be
  0xFFFF, per the OpenType spec's mandatory terminator sentinel (previously
  only endCode was checked, letting a spec-violating 'catch-all' segment
  such as startCode=0, endCode=0xFFFF through).
- Add an explicit non-overlap check for format-4 segments: each segment's
  startCode must be greater than the previous segment's endCode. The prior
  comment incorrectly claimed strictly-increasing endCode alone implied
  non-overlap; it does not (e.g. (0,10) followed by (5,20) passes a naive
  strictly-increasing-endCode check yet overlaps). Comment updated to
  describe the now-explicit non-overlap guarantee.
- Add regression tests covering both gaps (rejecting a non-0xFFFF terminator
  startCode, and rejecting overlapping-but-endCode-increasing segments), and
  fix an existing test whose synthetic fallback subtable relied on the same
  previously-unenforced terminator requirement.
Copilot AI review requested due to automatic review settings September 22, 2026 19:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/DemaConsulting.CanvasNet/Fonts/CmapTable.cs Outdated
Comment thread src/DemaConsulting.CanvasNet/Fonts/CmapTable.cs Outdated
Comment thread docs/verification/canvas-net/fonts/true-type-font.md Outdated
… docs

- CmapTable.TryParseFormat4: compute the endCode/startCode/idDelta/
  idRangeOffset/glyphIdArray offset chain in long arithmetic before the
  bounds check, so a malformed subtable offset combined with a large
  segCountX2 can no longer overflow 32-bit int addition, wrap past the
  glyphIdArrayOffset > tableEnd guard, and trigger an
  IndexOutOfRangeException or an out-of-bounds read in the array
  population loop.
- CmapTable format-12 lookup delegate: compute
  startGlyphIds[mid] + (code - starts[mid]) in long arithmetic and reject
  results that would not fit in a non-negative int, so a malformed group
  can no longer silently wrap to a small, plausible-looking (but bogus)
  glyph index.
- docs/verification/canvas-net/fonts/true-type-font.md: scope the
  "no third-party font files are used" claim to the synthetic-fixture
  coverage only, removing the self-contradiction with the following
  sentences describing the real Open Sans integration test fixture.
- Add CmapTableTests regression coverage for both offset-chain overflow
  and format-12 glyph-id wraparound, each verified to fail pre-fix and
  pass post-fix.
Copilot AI review requested due to automatic review settings September 22, 2026 19:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Seven unresolved findings remain, including one critical resource-allocation issue and six moderate validation issues.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
Resolved since last review (3)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Reject format-12 codepoints above U+10FFFF

src/​DemaConsulting.CanvasNet/​Fonts/​CmapTable.cs:369

The format-12 lookup only rejects negative inputs. Because a malformed subtable can also declare a range above Unicode's maximum, GetGlyphIndex(0x110000) can return a nonzero glyph instead of the documented .notdef fallback for an invalid Unicode codepoint. Bound the lookup to U+10FFFF (and reject out-of-range format-12 groups when parsing).

Comment thread src/DemaConsulting.CanvasNet/Fonts/CmapTable.cs
TryParseFormat12 allocated three parallel uint[] arrays sized directly by an
attacker-controlled numGroups after only an enclosing-table bounds check,
letting a bounds-valid but adversarial format-12 cmap subtable (declaring a
near-maximal numGroups paired with a suitably large but internally-consistent
declared table/file size) exhaust process memory.

Add MaxFormat12Groups (65536), checked before any further bounds checking or
allocation, following the same explicit-resource-cap philosophy already used
by GlyfLocaReader's MaxDepth/MaxTotalComponents. Real fonts - even CJK-heavy
ones covering the full Unicode codepoint space - need at most a few thousand
groups, so 65536 is generous headroom while keeping worst-case allocation to
a few hundred KB.

Kept the three parallel uint[] arrays (starts/ends/startGlyphIds) rather than
combining them into one struct array: with numGroups now capped, the total
byte footprint is the same order of magnitude either way, and the difference
is only per-array object overhead - not a meaningful part of the DoS surface -
so combining them was judged not worth the added code churn/risk.

Adds a regression test proving a subtable declaring numGroups one above the
cap (with real, non-overlapping group data making it otherwise bounds-valid)
is rejected; verified the test fails without the cap and passes with it.
Copilot AI review requested due to automatic review settings September 22, 2026 19:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved critical and moderate findings remain in glyph expansion, input validation, and test safety.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
Resolved since last review (1)
Previously missed (3)

In code that hasn't changed since last review

Medium severity Reject unsupported cmap header versions

src/​DemaConsulting.CanvasNet/​Fonts/​CmapTable.cs:75

The cmap header version is never checked before numTables is read. Since the supported cmap header version is 0, a malformed table with a nonzero version but otherwise valid records is currently accepted and can return mappings, contrary to this helper's documented malformed-input fallback to glyph 0. Reject nonzero versions before parsing the encoding records.

Medium severity Avoid near-2 GiB allocation in cmap test

test/​DemaConsulting.CanvasNet.Tests/​Fonts/​CmapTableTests.cs:631

This test allocates a byte[] whose length is essentially Array.MaxLength (about 2 GiB). The allocation can fail with OutOfMemoryException before CmapTable.Parse runs, making the test unsuitable for CI and unable to verify the overflow path reliably. Exercise the widened address arithmetic through a small-buffer/helper abstraction instead of materializing a near-int.MaxValue array.

This issue also appears on line 793 of the same file.

Medium severity Avoid 2 GiB allocation in kern test

test/​DemaConsulting.CanvasNet.Tests/​Fonts/​KernTableTests.cs:172

This test constructs a byte[] of about 2 GiB to make tableOffset overflow when adding the subtable length. That allocation is not realistic for the test process and can fail before KernTable.Parse is called; test the arithmetic helper directly or refactor the bounds calculation so it can be exercised with a small backing buffer.

Comment thread src/DemaConsulting.CanvasNet/Fonts/GlyfLocaReader.cs
Copilot AI review requested due to automatic review settings September 22, 2026 20:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Six moderate findings and one integration-boundary nit remain unresolved.

Review effort: Lite
Findings: 1 High severity

Open (1)
Previously missed (4)

In code that hasn't changed since last review

Medium severity Reject format-12 groups with code points above U+10FFFF

src/​DemaConsulting.CanvasNet/​Fonts/​CmapTable.cs:387

Format-12 startCharCode/endCharCode values are limited to Unicode U+10FFFF, but this validation checks only ordering. A malformed group such as [0x110000, 0x110000] is accepted, and GetGlyphIndex(0x110000) can then return a glyph instead of the documented 0 fallback for malformed cmap data. Reject groups whose end code exceeds 0x10FFFF and cover that case with a regression test.

Medium severity Validate kerning glyph indices against GlyphCount

src/​DemaConsulting.CanvasNet/​Fonts/​TrueTypeFont.cs:221

This public method delegates directly to KernTable, which only bounds indices to ushort and does not know this font's GlyphCount. Consequently, a two-glyph font containing a kern pair such as (100, 101) returns that adjustment for out-of-range glyph indices, contrary to the documented/requirement contract that out-of-range kerning queries return 0. Check both indices against GlyphCount before delegating and add a regression for an in-range ushort pair outside the font.

Medium severity Avoid oversized allocation in cmap offset overflow test

test/​DemaConsulting.CanvasNet.Tests/​Fonts/​CmapTableTests.cs:631

This allocation is not sparse: with idRangeOffsetOffset = Array.MaxLength - 30, the computed length is roughly 2.146 GB. The test can therefore exhaust the test process or fail on the runtime's maximum object size before CmapTable.Parse runs, despite the comment claiming only a small buffer is needed. Exercise the narrow arithmetic helper through reflection (as the KernTable overflow test does) or redesign the fixture so it does not allocate from the artificial absolute offset.

This issue also appears on line 793 of the same file.

Medium severity Avoid oversized allocation in kern offset overflow test

test/​DemaConsulting.CanvasNet.Tests/​Fonts/​KernTableTests.cs:172

This test also allocates a dense array whose length is about 2.147 GB (tableOffset + tableLength), while tableOffset was deliberately selected near int.MaxValue. That exceeds the runtime's addressable array limit or available test memory, so the test fails before KernTable.Parse can verify the overflow guard. Use a direct helper/reflection test or another fixture that fits in a normal byte array.

Malcolm Nixon added 2 commits September 22, 2026 16:56
Prevents a large simple glyph referenced many times via composite
components from producing hundreds of millions of copied path
commands from a tiny font file (memory/CPU exhaustion DoS). Adds a
MaxTotalPoints budget alongside the existing depth/component-count
caps, charged before allocation/copy at both the simple-glyph decode
site and the composite AppendTransformed copy site.
- CmapTable: reject format-12 groups whose endCharCode exceeds
  U+10FFFF, so malformed cmap data cannot resolve a glyph for an
  invalid code point.
- TrueTypeFont.GetKerning: validate both glyph indices against
  GlyphCount before delegating to KernTable, so a kern pair that is a
  valid ushort but out of range for this font's glyph count returns 0
  instead of a stale/incorrect adjustment, while preserving the
  never-throws contract.
- Rework the CmapTable format-4 and KernTable subtable-position
  overflow regression tests to exercise the extracted arithmetic
  helpers directly via reflection instead of allocating near-2GB byte
  arrays to simulate huge file offsets.
- Correct the design doc's composite-glyph-bounds description and add
  the missing requirement/verification entry for the new total
  point/command budget.
Copilot AI review requested due to automatic review settings September 22, 2026 20:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Three moderate glyph-data validation findings remain unresolved, along with documentation and requirement-traceability nits.

Review effort: Lite
Findings: None

Resolved since last review (1)

The requirement added for the total point/command budget was not
referenced from its parent's children list, leaving it unreachable
from the quality/system-tagged root and failing reqstream's
orphaned-requirement check.
Copilot AI review requested due to automatic review settings September 22, 2026 21:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Three unresolved moderate CmapTable issues require fixes before approval; two fixture nits are also noted.

Review effort: Lite
Findings: None

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.

2 participants