Lots of improvements (OTF fixes, more GPOS, synthetic bold/italics) - #1
Open
jeske wants to merge 10 commits into
Open
Lots of improvements (OTF fixes, more GPOS, synthetic bold/italics)#1jeske wants to merge 10 commits into
jeske wants to merge 10 commits into
Conversation
…standard2.0/net9.0 lib; killed 482 phantom CS0518s in IDE tooling) - lint now covers src + tests only
Move the CFF/OTF console repro harness (TestOtfLoading) into tests/TestOtfLoading/ to make room for the upcoming GposKerning.Tests xunit project (HarfBuzz numeric kerning oracle). ProjectReference path updated; UNLICENSED-FONTS.md scope stays with the harness fonts.
xunit + HarfBuzzSharp (TEST-ONLY), hb_font scale pinned to unitsPerEm for exact integer font-unit comparison against stbtt_GetGlyphKernAdvance. Feature set pinned explicitly (+kern; liga/clig/calt/curs/dist off) to prevent GSUB substitution contaminating the xAdvance delta. Expected-RED baseline (5776 pairs/font, 0 substitutions): consola 0/0 PASS; Roboto 28/412 mismatched (all quote-glyph pairs); times 92/92 and arial 88/88 lost entirely (extension-lookup signature). Roboto-Regular.ttf committed (Apache-2.0, see FONTS.md); Arial/Times/ Consolas loaded from C:\\Windows\\Fonts, skip-guarded.
…class-0 fix stbtt_GetGlyphKernAdvance now matches HarfBuzz exactly (oracle: 5776 pairs x 4 fonts, 0 mismatches, exact font-unit integer equality). - Script/feature resolution: latn (fallback DFLT) default LangSys -> 'kern' feature -> lookup indices, resolved once per font and cached; applied in LookupList index order, accumulating across lookups. Previously ALL lookups in the font were scanned (undefined order, non-kern features included). - Extension lookups (type 9): dispatch inner type-2 subtables via 32-bit extensionOffset. Was silently skipped - Arial/Times lost their ENTIRE kern sets (88/92 pairs). - Generalized ValueRecords: size = 2*popcount(valueFormat), xAdvance at offset 2*popcount(vf & 0x0003); device-table bits count toward size only. Was hard-rejecting anything but vf1=4/vf2=0. - ClassDef fix (Common.cs): unlisted glyphs are class 0 per spec, not -1. Restores Roboto quote-pair kerning (28 pairs) via correct fmt1->fmt2 subtable fall-through with HarfBuzz 'applied' semantics. - Legacy 'kern' table fallback now also covers fonts whose GPOS has no latn/DFLT kern feature (font-level decision, never per-pair). - LookupFlags deliberately ignored: only IgnoreMarks (0x0008) seen in target fonts, a no-op for base-glyph pairs. Tests: KernOracleTests (HarfBuzz numeric oracle, now gating) plus synthetic ValueRecordTests covering 8 valueFormat combos x fmt1/fmt2 blobs - 21 tests green.
For SilkyNvg plans/crisp_text_yonly_gridfit_hinting.md (FreeType 'slight' analog). New public delegate stbtt_YPixelGridFitRemap: a monotonic remap over y-up pixel space, applied to FLATTENED outline points right before edge building (piecewise-linear maps apply exactly; no control-point straddle distortion). x is never touched, so x-subpixel phase shifts compose freely. - Bmp.stbtt__rasterize / stbtt_Rasterize: optional y_remap parameter - FontInfo.stbtt_GetGlyphBitmapBoxSubpixelYRemap: box corners through the same remap so mask and origin agree - FontInfo.stbtt_MakeGlyphBitmapSubpixelYRemap: remapped raster entry - FontInfo.stbtt_GetGlyphHorizontalLineEdges: flat horizontal segment detector (bar edges + ink side from winding; TT CW / CFF CCW) for stem-bar snapping
…n, oblique shear, horizontal scaling
SilkyNvg plans/crisp_text_synthetic_styles.md Phase A2. New FontInfoSynth.cs:
- SynthOutline.SynthesizeOutline: applies the synth transform ONCE to the
stbtt_vertex[] outline in font units (y-up), upstream of rasterization.
1. Embolden (Skia's fake-bold technique): flatten contours (~1/256 em),
build +/-halfWidth offset rings (miter joins, limit 4, bevel fallback),
append as line contours; NON-ZERO winding computes the union at raster
time. Winding normalization: dominant fill sign w = sign(sum of contour
areas); each annulus emits larger ring wound w, smaller wound -w, so
stroke bands add flesh-signed ink and net zero inside - inner rims never
erase, holes shrink correctly, collapsed counters saturate (never cancel).
2. Shear/scale: x' = (x + y*skewX) * scaleX on all coordinates including
control points (linear map - quadratics stay quadratics).
- stbtt_GetGlyphShapeSynth / stbtt_GetGlyphBitmapBoxSubpixelSynth /
stbtt_MakeGlyphBitmapSubpixelSynth (+ y_remap variants): box computed from
the transformed outline's control polygon so mask and box always agree.
- FontInfo made partial (FontInfo.cs one-line change).
netstandard2.0-compatible (no MathF/Math.Clamp/index-from-end).
…nd GPOS kernel scope/limitations
…ding erase) David's diagnosis (2026-07-29): the A2 construction emitted a +/-d loop PAIR per contour. The -d loop of a flesh contour is region-theoretically redundant (it only cancels one layer over the eroded interior) and is exactly the loop that SELF-CROSSES wherever the local stroke width t < 2d (script tails, thin connectors) - its locally-inverted lobes broke the cancellation and left 0-winding (uncovered) patches INSIDE thin features: the 'lightened tail' artifact, identical on both SilkyNvg raster tiers because the geometry is shared. Fix: offset EVERY contour by -fillSign*d (away from ink), PRESERVING source traversal orientation, and append just that one loop. The algebra collapses flesh (grow outward) and holes (shrink inward) to one uniform formula. Winding field: deep interior 2, new stroke band 1, hole centers 0. Inward loops never exist; outward self-crossings at concave corners are double-POSITIVE lobes - winding-safe under both nonzero and |signed| evaluation. Collapse-inverted hole loops keep their inverted sense, so collapsed counters SATURATE solid (correct bold degeneracy). Also halves the appended geometry per styled glyph. The loop-PAIR construction is retained behind EmboldenLoopPairLegacyMode: without the fill layer it is the stroke BAND builder (band +/-1, interior 0 = hollow outline ring) that PDF Tr render modes 1/2 need - the B1 seed (SilkyNvg plans/crisp_text_synthetic_styles.md Part B). Measured (SilkyNvg FontShapeTortureTest synthbold vs Skia): Miama 0.71->0.54%, Across Before 0.66->0.56%, sans fonts unchanged (no t<2d features); SyntheticGlyphPerfectionTest registry cells 4-10x better, now at unstyled baseline - 100% of the thin-features-turning-midgray artifacts eliminated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is not intended to be accepted as is, but to start a conversation, because SafeStbTrueTypeSharp was last touched 6 years ago.
I've been making fixes and improvements to my fork, as a fully memory safe font implementation for my projects.
I'm soon going to diverge further, so before that happens, I thought I would have a conversation to see what (if any) changes you'd like to upstream.
For example, my first two commits would be just easy nice fixes to cherry pick:
After this I started working on some light extensions of the GPOS kerning support with a HarfBuzz test oracle, y-grid-fit support, and a first cut at a Synthetic bold+italic outline processor. (there are some thin feature deficiencies compared to my Skia Oracle, but it's not terrible, and i'll get them). I also have a Type-1 to OTF converter in another repo I've been considering adding to this.
If anyone is maintaining upstream, let me know your thoughts.. otherwise, this can sit as a pointer to others looking for added features in a memory safe font engine...
Thanks for SafeStbTrueTypeSharp!