diff --git a/changes/29.0.3.md b/changes/29.0.3.md index b2a4c38bec..11cc4d36c5 100644 --- a/changes/29.0.3.md +++ b/changes/29.0.3.md @@ -8,3 +8,4 @@ * Fix width of PUNCTUATION SPACE (`U+2008`) under Quasi-Proportional. * Fix `percent`=`dots` glyphs for PER {MILLE|TEN THOUSAND} SIGN (`U+2030`..`U+2031`) under Quasi-Proportional when `NWID` is enabled. * Remove untagged variant selector for Cyrillic Capital Ef (`ะค`). +* Fix glyph visual for COMBINING DOUBLE CIRCUMFLEX ABOVE (`U+1DCD`). diff --git a/packages/font-glyphs/src/letter/shared.ptl b/packages/font-glyphs/src/letter/shared.ptl index 9d0d7b0fb0..2dffaa4427 100644 --- a/packages/font-glyphs/src/letter/shared.ptl +++ b/packages/font-glyphs/src/letter/shared.ptl @@ -12,16 +12,24 @@ glyph-block Letter-Shared : begin glyph-block-import Common-Derivatives glyph-block-import Mark-Adjustment : TurnMarks LeaningAnchorMap - glyph-block-export CreateAccentedComposition - define [CreateAccentedComposition dst u srcGid accentGid] - derive-multi-part-glyphs dst u { srcGid accentGid } : function [gns gr] : glyph-proc - local { base mark } gns - local gMark : query-glyph mark + glyph-block-export CreateMultiAccentedComposition + define [CreateMultiAccentedComposition dst u gnSrc gnAccents] + derive-multi-part-glyphs dst u { gnSrc :: gnAccents } : function [gns gr] : glyph-proc + local { gnBase :: gnAccents } gns - include [refer-glyph base] AS_BASE ALSO_METRICS - currentGlyph.includeMarkWithLeaningSupport gMark LeaningAnchorMap + include [refer-glyph gnBase] AS_BASE ALSO_METRICS + foreach gnAccent [items-of gnAccents] : begin + local gAccent : query-glyph gnAccent + currentGlyph.includeMarkWithLeaningSupport gAccent LeaningAnchorMap - if (!gr && accentGid === 'dotAbove') : Dotless.set currentGlyph base + if (!gr) : begin + if (gnAccents.length === 1 && gnAccents.0 === 'dotAbove') + : then : Dotless.set currentGlyph gnBase + : else : CvDecompose.set currentGlyph { gnSrc :: gnAccents } + + glyph-block-export CreateAccentedComposition + define [CreateAccentedComposition dst u srcGid accentGid] + CreateMultiAccentedComposition dst u srcGid { accentGid } glyph-block-export CreateOgonekComposition define [CreateOgonekComposition dst u srcGid] @@ -44,13 +52,6 @@ glyph-block Letter-Shared : begin include : ApparentTranslate commaOffset 0 include [refer-glyph base] AS_BASE ALSO_METRICS - glyph-block-export CreateMultiAccentedComposition - define [CreateMultiAccentedComposition dstGid unicode srcGid accentGids fDontDecompose] - derive-glyphs dstGid unicode srcGid : lambda [src gr] : glyph-proc - include [refer-glyph src] AS_BASE ALSO_METRICS - foreach accentGid [items-of accentGids] : include [refer-glyph accentGid] - if (!fDontDecompose && !gr) : CvDecompose.set currentGlyph { src :: accentGids } - glyph-block-export CreateDependentComposite define [CreateDependentComposite gidDst unicode gidPart1 gidPart2Map] : begin local sourceGidList { gidPart1 } diff --git a/packages/font-glyphs/src/marks/tie.ptl b/packages/font-glyphs/src/marks/tie.ptl index ad597465dc..1e316d2deb 100644 --- a/packages/font-glyphs/src/marks/tie.ptl +++ b/packages/font-glyphs/src/marks/tie.ptl @@ -135,10 +135,21 @@ glyph-block Mark-Tie : begin set-base-anchor 'aboveBraceR' (0 + 0.5 * markExtend) aboveTieMid TieGlyph.set currentGlyph - include : CaretShape - xMiddle -- 0 - width -- CaretCaronWidth - top -- aboveTieTop + markFine * 0.7 - bottom -- aboveTieBottom + markStress - markFine - swEnd -- CaretCaronTerminalSw - swMid -- CaretCaronMidSw + + local leftHalf : include : dispiro + flat tieLeft aboveTieBottom [widths.center CaretCaronTerminalSw] + curl 0 aboveTieTop [widths.center CaretCaronMidSw] + local rightHalf : include : dispiro + flat tieRight aboveTieBottom [widths.center CaretCaronTerminalSw] + curl 0 aboveTieTop [widths.center CaretCaronMidSw] + + local a leftHalf.lhsKnots.1 + local b rightHalf.rhsKnots.1 + local c leftHalf.rhsKnots.1 + local d rightHalf.lhsKnots.1 + + include : spiro-outline + corner a.x a.y + corner b.x b.y + corner [Math.max c.x d.x] d.y + corner [Math.min c.x d.x] c.y diff --git a/packages/font/src/finalize/gc.mjs b/packages/font/src/finalize/gc.mjs index 172c0db9d4..091c2af0e9 100644 --- a/packages/font/src/finalize/gc.mjs +++ b/packages/font/src/finalize/gc.mjs @@ -359,9 +359,14 @@ function rectifyGlyphAndMarkComponents(glyphStore, aliasMap, markedGlyphs, memo, if (memo.has(g)) return; memo.add(g); - let refs = g.geometry.toReferences(); - if (refs) { - let parts = []; + analyzeRefs: { + let refs = g.geometry.toReferences(); + if (!refs) break analyzeRefs; + + let partGns = []; // The names of the referenced glyphs + let parts = []; // The parts of the new geometry + let hasMarked = false; // Whether any of the referenced glyphs is marked + for (let sr of refs) { // Resolve alias const alias = aliasMap.get(sr.glyph); @@ -371,14 +376,14 @@ function rectifyGlyphAndMarkComponents(glyphStore, aliasMap, markedGlyphs, memo, sr.y += alias.y; } + // Resolve reference const gn = glyphStore.queryNameOf(sr.glyph); if (!gn) { // Reference is invalid. The root glyph will be radicalized. - g.geometry = new Geometry.RadicalGeometry(g.geometry.unlinkReferences()); - return; + break analyzeRefs; } else { // Reference is valid. Process the referenced glyph. - if (!markedGlyphs.has(gn)) markedGlyphs.set(gn, d + 0x10000); + if (markedGlyphs.has(gn)) hasMarked = true; rectifyGlyphAndMarkComponents( glyphStore, aliasMap, @@ -388,10 +393,18 @@ function rectifyGlyphAndMarkComponents(glyphStore, aliasMap, markedGlyphs, memo, d + 0x10000, ); parts.push(new Geometry.ReferenceGeometry(sr.glyph, sr.x, sr.y)); + partGns.push(gn); } } - g.geometry = new Geometry.CombineGeometry(parts); - } else { - g.geometry = new Geometry.RadicalGeometry(g.geometry.unlinkReferences()); + + // If any of the referenced glyphs is marked, mark all the remaining references. + if (hasMarked) { + for (const gn of partGns) if (!markedGlyphs.has(gn)) markedGlyphs.set(gn, d + 0x10000); + g.geometry = new Geometry.CombineGeometry(parts); + return; + } } + + // Make the glyph radical if it has no marked references. + g.geometry = g.geometry.unlinkReferences(); } diff --git a/packages/font/src/finalize/glyphs.mjs b/packages/font/src/finalize/glyphs.mjs index b3ff30bd20..8ffea041cb 100644 --- a/packages/font/src/finalize/glyphs.mjs +++ b/packages/font/src/finalize/glyphs.mjs @@ -12,44 +12,12 @@ export function finalizeGlyphs(cache, para, glyphStore) { /////////////////////////////////////////////////////////////////////////////////////////////////// function regulateGlyphStore(cache, skew, glyphStore) { - const compositeMemo = new Map(); for (const g of glyphStore.glyphs()) { if (!(g.geometry.measureComplexity() & Geom.CPLX_NON_EMPTY)) continue; - if (!regulateCompositeGlyph(glyphStore, compositeMemo, g)) { - g.geometry = g.geometry.unlinkReferences(); - } - } - for (const g of glyphStore.glyphs()) { - if (!compositeMemo.get(g)) flattenSimpleGlyph(cache, skew, g); + if (!g.geometry.toReferences()) flattenSimpleGlyph(cache, skew, g); } } -/////////////////////////////////////////////////////////////////////////////////////////////////// - -function regulateCompositeGlyph(glyphStore, memo, g) { - if (memo.has(g)) return memo.get(g); - - let refs = g.geometry.toReferences(); - if (!refs) return memoSet(memo, g, false); - - for (const sr of refs) { - const gn = glyphStore.queryNameOf(sr.glyph); - if (!gn) return memoSet(memo, g, false); - } - - let refGeometries = []; - for (const sr of refs) refGeometries.push(new Geom.ReferenceGeometry(sr.glyph, sr.x, sr.y)); - g.geometry = new Geom.CombineGeometry(refGeometries); - return memoSet(memo, g, true); -} - -function memoSet(memo, g, v) { - memo.set(g, v); - return v; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - function flattenSimpleGlyph(cache, skew, g) { const ck = Geom.hashGeometry(g.geometry); const cached = cache.getGF(ck); diff --git a/packages/font/src/otd-conv/glyphs.mjs b/packages/font/src/otd-conv/glyphs.mjs index b8722c3a36..53037c2477 100644 --- a/packages/font/src/otd-conv/glyphs.mjs +++ b/packages/font/src/otd-conv/glyphs.mjs @@ -145,7 +145,7 @@ class MappedGlyphStore { const gl = new Ot.Glyph.GeometryList(); for (const ref of rs) { const target = this.queryBySourceGlyph(ref.glyph); - if (!target) throw new Error("Unreachable"); + if (!target) throw new Error("Unreachable: glyph not found"); const tfm = Ot.Glyph.Transform2X3.Translate(ref.x, ref.y); gl.items.push(new Ot.Glyph.TtReference(target, tfm)); }