diff --git a/changes/26.1.1.md b/changes/26.2.0.md similarity index 62% rename from changes/26.1.1.md rename to changes/26.2.0.md index e8c2509d7..233c4d358 100644 --- a/changes/26.1.1.md +++ b/changes/26.2.0.md @@ -1,6 +1,7 @@ * Add characters: - CLOCK FACE ONE OCLOCK (`U+1F550`) ... CLOCK FACE TWELVE-THIRTY (`U+1F567`) (#1850). +* Implement leaning mark mechanism for `F`, `L`, `P`, `b`, `d`, `h`, `k`, `p`, `q`, `r` to get better mark placement. Now, "narrow" marks will align to these letters' extension parts (#1851). * Fix detached cedilla in Hookless asymmetric LATIN SMALL LETTER T WITH CEDILLA (`U+0163`) (#1914). -* Fix broken hookless/tailless/assymetric t variants in ſt ligature (`U+FB05`) (#1915). +* Fix broken hookless/tailless/asymmetric t variants in ſt ligature (`U+FB05`) (#1915). * Remove unnecessary tailed variants for Cyrillic Shha with Descender (`U+0527`) (#1916). * Remove unnecessary lower-right serif variants for Latin Lower K with Descender (`U+2C6A`) (#1917). diff --git a/font-src/gen/finalize/index.mjs b/font-src/gen/finalize/index.mjs index a105bc508..79eba3061 100644 --- a/font-src/gen/finalize/index.mjs +++ b/font-src/gen/finalize/index.mjs @@ -21,7 +21,7 @@ function assignSubRank(glyphStore) { // "Fixed" subfamilies are properly built. function validateMonospace(para, glyphStore) { let awSet = new Set(); - for (const [u, g] of glyphStore.encodedEntries()) { + for (const [u, n, g] of glyphStore.encodedEntries()) { const aw = Math.round(g.advanceWidth || 0); if (aw > 0) awSet.add(aw); } diff --git a/font-src/gen/otd-conv/glyph-name.mjs b/font-src/gen/otd-conv/glyph-name.mjs index e3ce6f9ef..f125839b2 100644 --- a/font-src/gen/otd-conv/glyph-name.mjs +++ b/font-src/gen/otd-conv/glyph-name.mjs @@ -1,4 +1,4 @@ -import { Joining, AnyCv, TieMark, Nwid, Wwid } from "../../support/gr.mjs"; +import { Joining, AnyCv, TieMark, Nwid, Wwid, VS01 } from "../../support/gr.mjs"; const ApplePostNames = new Map([ /* spell-checker: disable */ @@ -286,14 +286,18 @@ export function bySpacing(gSrcBase, gOtBase, internalNameMap, conflictSet) { return n; } +const NamingGr = [TieMark, VS01]; + export function byGr(gSrcBase, gOtBase, internalNameMap, conflictSet) { if (!gOtBase.name) return 0; let n = 0; for (const cv of AnyCv.query(gSrcBase)) { n += nameByGr(cv, gSrcBase, gOtBase, internalNameMap, conflictSet); } - if (TieMark.get(gSrcBase)) { - n += nameByGr(TieMark, gSrcBase, gOtBase, internalNameMap, conflictSet); + for (const gr of NamingGr) { + if (gr.get(gSrcBase)) { + n += nameByGr(gr, gSrcBase, gOtBase, internalNameMap, conflictSet); + } } return n; } diff --git a/font-src/glyphs/auto-build/accents.ptl b/font-src/glyphs/auto-build/accents.ptl index 73c631fa6..4e88b0898 100644 --- a/font-src/glyphs/auto-build/accents.ptl +++ b/font-src/glyphs/auto-build/accents.ptl @@ -1,10 +1,13 @@ ###### Automatic builds $$include '../../meta/macros.ptl' -import [Dotless AnyDerivingCv DotlessOrNot getGrTree CvDecompose] from"../../support/gr.mjs" -import [fallback] from"../../support/utils.mjs" +import [Dotless AnyDerivingCv DotlessOrNot getGrTree CvDecompose LeaningMark LeaningMarkSpacer] from"../../support/gr.mjs" +import [fallback ArrayUtil MatchUtil constant] from"../../support/utils.mjs" import as UnicodeKnowledge from"../../meta/unicode-knowledge.mjs" +extern Map +extern Set + glyph-module glyph-block AutoBuild-Accents : begin @@ -24,42 +27,150 @@ glyph-block AutoBuild-Accents : begin set map.(key) amended return amended - # Build accented glyphs - define [isAboveMark mark] : begin - return : mark && mark.markAnchors && mark.markAnchors.above + # Here, we build a simplified substitution builder that does the mark substitutions + define [substParts parts ignore backtrack input loookAhead production] : begin + local igl 0 + while (igl < parts.length) : begin + local m : substMatch parts igl ignore backtrack input loookAhead + if [not m] + : then : inc igl + : else : begin + local inputGlyphs : ArrayUtil.mapIndexToItems parts m + local producedGlyphs : production.apply null inputGlyphs + foreach i [range (m.length - 1) downtill 0] : begin + parts.splice m.(i) 1 + ArrayUtil.insertSliceAt parts m.0 producedGlyphs + set igl : m.(m.length - 1) + 1 + producedGlyphs.length - m.length - define [MarkSubst lookup] : function [p j parts] : begin - foreach { k v } [Object.entries lookup] : begin - if (p === [query-glyph k]) : set parts.(j) [query-glyph v] + return parts - define iotaBelowToLF : MarkSubst UnicodeKnowledge.iotaBelowToLfTf - define ogonekBelowToTR : MarkSubst UnicodeKnowledge.ogonekBelowToTRTf - define grekUpperTonos : MarkSubst UnicodeKnowledge.upperGrekMarkToTonosTf + define [substMatch parts igl ignore backtrack input lookAhead] : begin + if (igl >= parts.length) : return null + if [ignore parts.(igl)] : return null + if [not : input.0 parts.(igl)] : return null + + local m { igl } + + # Check input + local iglInput : igl + 1 + foreach iInput [range 1 input.length] : begin + while (iglInput < parts.length && [ignore parts.(iglInput)]) : inc iglInput + if (iglInput >= parts.length) : return null + if [not : input.(iInput) parts.(iglInput)] : return null + m.push iglInput + inc iglInput + + # Check lookahead + local iglLookAhead iglInput + foreach iLookAhead [range 0 lookAhead.length] : begin + while (iglLookAhead < parts.length && [ignore parts.(iglLookAhead)]) : inc iglLookAhead + if (iglLookAhead >= parts.length) : return null + if [not : lookAhead.(iLookAhead) parts.(iglLookAhead)] : return null + inc iglLookAhead + + # Check backtrack + local iglBacktrack : igl - 1 + foreach iBacktrack [range (backtrack.length - 1) downtill 0] : begin + while (iglBacktrack >= 0 && [ignore parts.(iglBacktrack)]) : dec iglBacktrack + if (iglBacktrack < 0) : return null + if [not : backtrack.(iBacktrack) parts.(iglBacktrack)] : return null + dec iglBacktrack + + return m + + define [dotless g] : begin + local gDotless : query-glyph : Dotless.get g + if [not gDotless] : return null + return {gDotless} + + define [isMark k] : function [g] : begin + return : g && g.markAnchors && g.markAnchors.(k) + define [isMarkExcluding k] : function [g] : begin + return : g && g.markAnchors && [Object.keys g.markAnchors].length && !g.markAnchors.(k) + define [hasBaseAnchor k] : function [g] : begin + return : g && g.baseAnchors && g.baseAnchors.(k) + define [isLeaningMark k] : function [g] : begin + return : g && g.markAnchors && g.markAnchors.(k) && [LeaningMark.get g] + define [leaningMarkSplit g] : begin + local spacer : query-glyph : LeaningMarkSpacer.get g + local alternative : query-glyph : LeaningMark.get g + if (spacer && alternative) : return { spacer alternative } + return { g } + + define [markSubst uk] : begin + local fromGlyphs : new Set + local mapping : new Map + foreach { k v } [Object.entries uk] : begin + local gFrom : query-glyph k + local gTo : query-glyph v + if (gFrom && gTo) : begin + fromGlyphs.add gFrom + mapping.set gFrom gTo + + define [matcher g] : fromGlyphs.has g + define [ignore g] : g && g.markAnchors && [Object.keys g.markAnchors].length && ![matcher g] + define [production g] : list ([mapping.get g] || g) + + return : object matcher ignore production + + define [markCombine uk] : begin + local first : new Set + local second : new Set + local mapping : new Map + + foreach { k1 m } [Object.entries uk] : begin + foreach { k2 v } [Object.entries m] : begin + local g1 : query-glyph k1 + local g2 : query-glyph k2 + local g3 : query-glyph v + if (g1 && g2 && g3) : begin + first.add g1 + second.add g2 + + local mm : mapping.get g1 + if [not mm] : begin + set mm : new Map + mapping.set g1 mm + mm.set g2 g3 + + define [matchFirst g] : first.has g + define [matchSecond g] : second.has g + define [production g1 g2] : begin + local mm : mapping.get g1 + if [not mm] : return { g1 g2 } + local g3 : mm.get g2 + if [not g3] : return { g1 g2 } + return { g3 } + + return : object matchFirst matchSecond production + + define iotaLF : markSubst UnicodeKnowledge.iotaBelowToLfTf + define ogonek : markSubst UnicodeKnowledge.ogonekBelowToTRTf + define upperTonos : markSubst UnicodeKnowledge.upperGrekMarkToTonosTf + + define markComposition : markCombine UnicodeKnowledge.markCompositionTf define [subParts parts] : begin - # Keep the semantics here synchronized with `ccmp` feature - # Handle dotless - local hasMarkAbove false - foreach p [items-of parts] : if [isAboveMark p] : set hasMarkAbove true - if (hasMarkAbove && [Dotless.get parts.0]) : begin - local dotless [query-glyph : Dotless.get parts.0] - if dotless : set parts.0 dotless + ### Keep the semantics here synchronized with `ccmp` feature - # replace below marks with trailing marks - if parts.0.baseAnchors.lf : parts.forEach iotaBelowToLF - if parts.0.baseAnchors.trailing : parts.forEach ogonekBelowToTR + # Handle dotless form + substParts parts [isMarkExcluding 'above'] {} {dotless} {[isMark 'above']} dotless - # composite greek Marks - for [local j 0] (j < parts.length) [inc j] : begin - foreach { gidFirst seconds } [Object.entries UnicodeKnowledge.markCompositionTf] - if (parts.(j) === [query-glyph gidFirst]) - foreach { gidSecond gidTo } [Object.entries seconds] - if (parts.(j + 1) === [query-glyph gidSecond]) : begin - set parts.(j) [query-glyph gidTo]; set parts.(j + 1) null + # Handle iota subscript + substParts parts iotaLF.ignore {[hasBaseAnchor 'lf']} {iotaLF.matcher} {} iotaLF.production - if parts.0.baseAnchors.grekUpperTonos : grekUpperTonos parts.1 1 parts + # Handle ogonek + substParts parts ogonek.ignore {[hasBaseAnchor 'trailing']} {ogonek.matcher} {} ogonek.production - return : parts.filter : function [x] [not : not x] + # Handle leaning marks + substParts parts [isMarkExcluding 'above'] {[MatchUtil.either [hasBaseAnchor 'leaningAbove'] [isMark 'leaningAbove']]} {[isLeaningMark 'above']} {} leaningMarkSplit + substParts parts [isMarkExcluding 'below'] {[MatchUtil.either [hasBaseAnchor 'leaningBelow'] [isMark 'leaningBelow']]} {[isLeaningMark 'below']} {} leaningMarkSplit + + # Handle mark combinations (Greek) + substParts parts MatchUtil.never {} { markComposition.matchFirst markComposition.matchSecond } {} markComposition.production + + # Handle upper Greek Tonos marks + substParts parts MatchUtil.never {[hasBaseAnchor 'grekUpperTonos']} {upperTonos.matcher} {} upperTonos.production define [pad _s n] : begin local s _s @@ -96,7 +207,7 @@ glyph-block AutoBuild-Accents : begin if (allFound && parts.length) : begin local glyphName : 'u' + [code.toString 16 :.padStart 4 '0'] - set parts : subParts parts + subParts parts set foundDecompositions.(glyphName) { glyphName code parts } local s_goalName nothing diff --git a/font-src/glyphs/auto-build/mark-doppelganger.ptl b/font-src/glyphs/auto-build/mark-doppelganger.ptl index 2c8d3bcb7..4288b92e0 100644 --- a/font-src/glyphs/auto-build/mark-doppelganger.ptl +++ b/font-src/glyphs/auto-build/mark-doppelganger.ptl @@ -3,29 +3,76 @@ $$include '../../meta/macros.ptl' import [Arcs Quadify ShapeConv] from "typo-geom" import [mix linreg clamp fallback] from"../../support/utils.mjs" import [DesignParameters] from"../../meta/aesthetics.mjs" -import [TieMark TieGlyph] from"../../support/gr.mjs" +import [TieMark AnyDerivingCv ScheduleLeaningMark LeaningMark LeaningMarkSpacer] from"../../support/gr.mjs" import [Box] from"../../support/geometry/box.mjs" +extern Set + glyph-module glyph-block Mark-Doppelganger : if [not recursive] : begin glyph-block-import CommonShapes glyph-block-import Common-Derivatives - define AnchorMap : list - list 'above' 'tieAbove' - list 'below' 'tieBelow' - - foreach { gn g } [glyphStore.namedEntries] : begin - local handled false - foreach { akFrom akTo } [items-of AnchorMap] : begin - if (!handled && g.markAnchors && g.markAnchors.(akFrom)) : begin - set handled true - local toGN : TieMark.amendName gn + define [DeriveMarkChange gr gn akFrom akTo] : begin + DeriveMeshT {gn} AnyDerivingCv : function [gns] : begin + local srcGn gns.0 + local src : query-glyph srcGn + local toGN : gr.amendName srcGn + if [not : query-glyph toGN] : begin create-glyph toGN : glyph-proc - include g AS_BASE ALSO_METRICS + include [refer-glyph srcGn] AS_BASE ALSO_METRICS set currentGlyph.markAnchors.(akTo) currentGlyph.markAnchors.(akFrom) currentGlyph.deleteMarkAnchor akFrom set currentGlyph.baseAnchors.(akTo) currentGlyph.baseAnchors.(akFrom) currentGlyph.deleteBaseAnchor akFrom - TieMark.set g toGN + gr.set src toGN + return toGN + + + local spacerGlyphSet : new Set + define TieAnchorMap : list + list 'above' 'tieAbove' + list 'below' 'tieBelow' + + do : foreach { u gn g } [glyphStore.encodedEntries] : DeriveTieMarks gn g + : where : [DeriveTieMarks gn g] : begin + local selection null + foreach { akFrom akTo } [items-of TieAnchorMap] : begin + if (!selection && g.markAnchors.(akFrom)) : begin + set selection { akFrom akTo } + + if selection : begin + local { akFrom akTo } selection + DeriveMarkChange TieMark gn akFrom akTo + + + define LeaningAnchorMap : list + list 'above' 'leaningAbove' + list 'below' 'leaningBelow' + + do : foreach { u gn g } [glyphStore.encodedEntries] : DeriveLeaningMark gn g + : where : [DeriveLeaningMark gn g] : begin + local selection null + foreach { akFrom akTo } [items-of LeaningAnchorMap] : begin + if (!selection && g.markAnchors.(akFrom) && [ScheduleLeaningMark.get g]) : begin + set selection { akFrom akTo } + + if selection : begin + local { akFrom akTo } selection + + # Build the doppelganger + DeriveMarkChange LeaningMark gn akFrom akTo + + # build spacer glyph + local deltaX : Math.round : g.baseAnchors.(akFrom).x - g.markAnchors.(akFrom).x + local deltaY : Math.round : g.baseAnchors.(akFrom).y - g.markAnchors.(akFrom).y + local spacerGn "spacerGlyph{\(akTo)}{\(deltaX)}{\(deltaY)}" + + LeaningMarkSpacer.set g spacerGn + + if [not : spacerGlyphSet.has spacerGn] : begin + spacerGlyphSet.add spacerGn + create-glyph spacerGn : glyph-proc + include g AS_BASE ALSO_METRICS + currentGlyph.clearGeometry diff --git a/font-src/glyphs/common/derivatives.ptl b/font-src/glyphs/common/derivatives.ptl index 86a86dfc1..0085b34fe 100644 --- a/font-src/glyphs/common/derivatives.ptl +++ b/font-src/glyphs/common/derivatives.ptl @@ -48,7 +48,9 @@ glyph-block Common-Derivatives : begin alias name unicode (name + '.upright') glyph-block-export query-glyph - define [query-glyph id] : return : glyphStore.queryByName id + define [query-glyph id] : begin + if [not id] : return nothing + return : glyphStore.queryByName id glyph-block-export refer-glyph define [refer-glyph id] : lambda [copyAnchors copyWidth] : begin diff --git a/font-src/glyphs/letter/cyrillic/che.ptl b/font-src/glyphs/letter/cyrillic/che.ptl index a3a89b336..cbee41306 100644 --- a/font-src/glyphs/letter/cyrillic/che.ptl +++ b/font-src/glyphs/letter/cyrillic/che.ptl @@ -22,6 +22,7 @@ glyph-block Letter-Cyrillic-Che : begin define SLAB-TAILED-BGR 6 define [CyrCheShape top pyBar bodyType slabType] : glyph-proc + set-base-anchor 'leaningBelow' (RightSB - [HSwToV HalfStroke]) 0 local bar : top * [fallback pyBar 0.5] include : match bodyType [Just BODY-TAILED] : RightwardTailedBar RightSB 0 top diff --git a/font-src/glyphs/letter/greek/upper-gamma.ptl b/font-src/glyphs/letter/greek/upper-gamma.ptl index 6a10f54b6..f8a83bc7b 100644 --- a/font-src/glyphs/letter/greek/upper-gamma.ptl +++ b/font-src/glyphs/letter/greek/upper-gamma.ptl @@ -23,6 +23,7 @@ glyph-block Letter-Greek-Upper-Gamma: begin define GammaBarLeft (SB * 1.5) define [GammaShape top slabType] : glyph-proc + set-base-anchor 'leaningBelow' (GammaBarLeft + [HSwToV HalfStroke]) 0 include : VBar.l GammaBarLeft 0 top include : HBar.t (GammaBarLeft - O) (RightSB - OX) top match slabType diff --git a/font-src/glyphs/letter/latin/k.ptl b/font-src/glyphs/letter/latin/k.ptl index 1dc0d3cbf..1cbd5d6c8 100644 --- a/font-src/glyphs/letter/latin/k.ptl +++ b/font-src/glyphs/letter/latin/k.ptl @@ -503,15 +503,12 @@ glyph-block Letter-Latin-K : begin if slabLT : include : HSerif.lt (SB + [KBalance slabLT straightBar]) Ascender SideJut if slabLB : include : tagged 'serifLB' HSerif.mb (SB + [KBalance slabLT straightBar] + [HSwToV HalfStroke]) 0 Jut + set-base-anchor 'leaningAbove' (SB + [KBalance slabLT straightBar] + [HSwToV HalfStroke]) Ascender create-glyph "k.\(suffix)" : glyph-proc include : MarkSet.b include : kBaseShape - create-glyph "k/circumflexBase.\(suffix)" : glyph-proc - include [refer-glyph "k.\(suffix)"] AS_BASE ALSO_METRICS - set-base-anchor 'above' (SB + [KBalance slabLT straightBar] + [HSwToV HalfStroke]) Ascender - create-glyph "kDescender.\(suffix)" : glyph-proc include : MarkSet.b include : kBaseShape CyrDescender @@ -550,7 +547,6 @@ glyph-block Letter-Latin-K : begin select-variant 'k' 'k' select-variant 'k/nonCursive' (shapeFrom -- 'k') - select-variant 'k/circumflexBase' (follow -- 'k') link-reduced-variant 'k/sansSerif' 'k' MathSansSerif select-variant 'kDescender' 0x2C6A select-variant 'kPalatalHook' 0x1D84 (follow -- 'kDescender') @@ -578,11 +574,6 @@ glyph-block Letter-Latin-K : begin derive-composites 'KBarLegStroke' 0xA744 'KBar' 'legSlashOver' derive-composites 'kBarLegStroke' 0xA745 'kBar' 'legSlashOver' - derive-glyphs 'kCaron' 0x1E9 'k/circumflexBase' : lambda [src gr] : glyph-proc - local shift : Width + SB - Middle + [HSwToV HalfStroke] - include [refer-glyph src] AS_BASE ALSO_METRICS - include [refer-glyph "caronAbove"] - turned 'turnK' 0xA7B0 'K' Middle (CAP / 2) turned 'turnk' 0x29E 'k' Middle (XH / 2) [MarkSet.p] turned 'turnSmcpK' 0x1DF10 'smcpK' Middle (XH / 2) diff --git a/font-src/glyphs/letter/latin/lower-b.ptl b/font-src/glyphs/letter/latin/lower-b.ptl index 2eed3e9a0..88c5da969 100644 --- a/font-src/glyphs/letter/latin/lower-b.ptl +++ b/font-src/glyphs/letter/latin/lower-b.ptl @@ -45,6 +45,7 @@ glyph-block Letter-Latin-Lower-B : begin include : Serifs set-base-anchor 'overlayOnExtension' (SB + [HSwToV : 0.5 * Stroke]) yOverlay set-base-anchor 'overlay' Middle (XH / 2) + set-base-anchor 'leaningAbove' (SB + [HSwToV HalfStroke]) Ascender create-glyph "bBar.\(suffix)" : glyph-proc include [refer-glyph "b.\(suffix)"] AS_BASE ALSO_METRICS @@ -86,8 +87,9 @@ glyph-block Letter-Latin-Lower-B : begin derive-multi-part-glyphs 'bDot' 0x1E03 {'b' 'dotAbove'} : lambda [srcs gr] : glyph-proc local { base mark } srcs include : refer-glyph mark - include : Translate (Width + HalfStroke) 0 + include : Translate (Width + [HSwToV HalfStroke]) 0 include [refer-glyph base] AS_BASE + set-base-anchor 'leaningAbove' (Middle + [HSwToV HalfStroke]) Ascender glyph-block-import Letter-Blackboard : BBS BBD BBBarLeft create-glyph 'mathbb/b' 0x1D553 : glyph-proc diff --git a/font-src/glyphs/letter/latin/lower-d.ptl b/font-src/glyphs/letter/latin/lower-d.ptl index ddeb02d17..f1923cb4f 100644 --- a/font-src/glyphs/letter/latin/lower-d.ptl +++ b/font-src/glyphs/letter/latin/lower-d.ptl @@ -96,6 +96,7 @@ glyph-block Letter-Latin-Lower-D : begin if topSerif : include : topSerif df Ascender if bottomSerif : include : bottomSerif df Ascender set-base-anchor 'overlayOnExtension' (df.rightSB - [HSwToV : 0.5 * Stroke]) yOverlay + set-base-anchor 'leaningAbove' (df.rightSB - [HSwToV HalfStroke]) Ascender create-glyph "dcroat.\(suffix)" : glyph-proc local df : DivFrame 1 @@ -165,8 +166,9 @@ glyph-block Letter-Latin-Lower-D : begin derive-multi-part-glyphs 'dDot' 0x1E0B {'d' 'dotAbove'} : lambda [srcs gr] : glyph-proc local { base mark } srcs include : refer-glyph mark - include : Translate (Width - HalfStroke) 0 + include : Translate (Width - [HSwToV HalfStroke]) 0 include [refer-glyph base] AS_BASE + set-base-anchor 'leaningAbove' (Middle - [HSwToV HalfStroke]) Ascender derive-composites 'dBar' 0xA7C8 'd' HBar.m (SB - OX) (RightSB + OX) (XH * 0.5) OverlayStroke diff --git a/font-src/glyphs/letter/latin/lower-h.ptl b/font-src/glyphs/letter/latin/lower-h.ptl index 711ca22e3..abcdee55b 100644 --- a/font-src/glyphs/letter/latin/lower-h.ptl +++ b/font-src/glyphs/letter/latin/lower-h.ptl @@ -1,7 +1,7 @@ $$include '../../../meta/macros.ptl' import [mix fallback SuffixCfg] from"../../../support/utils.mjs" -import [Dotless CvDecompose MathSansSerif] from"../../../support/gr.mjs" +import [ScheduleLeaningMark MathSansSerif] from"../../../support/gr.mjs" glyph-module @@ -57,6 +57,7 @@ glyph-block Letter-Latin-Lower-H : begin foreach { suffix { fTailed {fHasTopSerif Serifs} } } [Object.entries HConfig] : do create-glyph "h.\(suffix)" : glyph-proc include : MarkSet.b + set-base-anchor 'leaningAbove' (SB + [HSwToV HalfStroke]) Ascender include : VBar.l SB 0 Ascender include : nShoulder left -- (SB + [HSwToV Stroke]) @@ -65,10 +66,6 @@ glyph-block Letter-Latin-Lower-H : begin if fTailed : include : RightwardTailedBar RightSB 0 (XH - SmallArchDepthB) include : Serifs fTailed false - create-glyph "h/circumflexBase.\(suffix)" : glyph-proc - include [refer-glyph "h.\(suffix)"] AS_BASE ALSO_METRICS - set-base-anchor 'above' (SB + [HSwToV HalfStroke]) Ascender - create-glyph "hBar.\(suffix)" : glyph-proc include [refer-glyph "h.\(suffix)"] AS_BASE ALSO_METRICS include : HBar.mOverlay fHasTopSerif @@ -125,19 +122,9 @@ glyph-block Letter-Latin-Lower-H : begin select-variant 'h' 'h' select-variant 'h/tailless' (shapeFrom -- 'h') - select-variant 'h/circumflexBase' (follow -- 'h') link-reduced-variant 'h/descBase' 'h' link-reduced-variant 'h/sansSerif' 'h' MathSansSerif - derive-glyphs 'hCircumflex' 0x125 'h/circumflexBase' : lambda [src gr] : glyph-proc - include [refer-glyph src] AS_BASE ALSO_METRICS - include [refer-glyph "circumflexAbove"] - - - derive-glyphs 'hCaron' 0x21F 'h/circumflexBase' : lambda [src gr] : glyph-proc - include [refer-glyph src] AS_BASE ALSO_METRICS - include [refer-glyph "caronAbove"] - select-variant 'hBar' 0x127 (follow -- 'h') turned 'turnh' 0x265 'h/tailless' Middle (XH / 2) [MarkSet.p] @@ -159,6 +146,7 @@ glyph-block Letter-Latin-Lower-H : begin local { base mark } srcs include [refer-glyph base] AS_BASE ALSO_METRICS include : with-transform [ApparentTranslate [HSwToV HalfStroke] (XH - Ascender)] [refer-glyph mark] + set-base-anchor 'leaningAbove' (Middle + [HSwToV HalfStroke]) Ascender derive-glyphs 'hCedilla' 0x1E29 'h' : lambda [src gr] : glyph-proc local shift : Width + SB - Middle + [HSwToV HalfStroke] diff --git a/font-src/glyphs/letter/latin/lower-p.ptl b/font-src/glyphs/letter/latin/lower-p.ptl index 36b17f364..01557bc0c 100644 --- a/font-src/glyphs/letter/latin/lower-p.ptl +++ b/font-src/glyphs/letter/latin/lower-p.ptl @@ -51,6 +51,7 @@ glyph-block Letter-Latin-Lower-P : begin include : Serifs XH set-base-anchor 'overlayOnExtension' (SB + [HSwToV : 0.5 * Stroke]) yOverlay set-base-anchor 'strike' Middle (XH / 2) + set-base-anchor 'leaningBelow' (SB + [HSwToV HalfStroke]) Descender create-glyph "thorn.\(suffix)" : glyph-proc include : MarkSet.bp diff --git a/font-src/glyphs/letter/latin/lower-q.ptl b/font-src/glyphs/letter/latin/lower-q.ptl index 64508d3b5..e7dc42578 100644 --- a/font-src/glyphs/letter/latin/lower-q.ptl +++ b/font-src/glyphs/letter/latin/lower-q.ptl @@ -77,6 +77,7 @@ glyph-block Letter-Latin-Lower-Q : begin include : Body terminal XH Descender if sRT : include : sRT XH if sRB : include : sRB Descender + set-base-anchor 'leaningBelow' (RightSB - [HSwToV HalfStroke]) Descender create-glyph "QRTail.\(suffix)" : glyph-proc include : MarkSet.capDesc diff --git a/font-src/glyphs/letter/latin/lower-r.ptl b/font-src/glyphs/letter/latin/lower-r.ptl index 354e3ac22..250f0be69 100644 --- a/font-src/glyphs/letter/latin/lower-r.ptl +++ b/font-src/glyphs/letter/latin/lower-r.ptl @@ -72,17 +72,17 @@ glyph-block Letter-Latin-Lower-R : begin [Just rNarrowSerifed] : mix df.width rHookX df.div [Just rNarrow] : xArchMiddle + 0.1 __ rHookX - local [setMarks doTopSerif] : glyph-proc - set-base-anchor 'above' [mix [mix df.leftSB (xBar - [HSwToV Stroke]) : if doTopSerif 0.5 1] df.rightSB 0.5] XH - set-base-anchor 'overlay' (xBar - Stroke * 0.25) (XH * 0.5) - set-base-anchor 'palatalHookAttach' xBar 0 - set-base-anchor 'palatalHookPos' (xBar + [PalatalHook.adviceGap Stroke]) 0 + local [setMarks doTopSerif top bottom] : glyph-proc + set-base-anchor 'above' [mix [mix df.leftSB (xBar - [HSwToV Stroke]) : if doTopSerif 0.5 1] df.rightSB 0.5] top + set-base-anchor 'leaningBelow' (xBar - [HSwToV HalfStroke]) bottom + set-base-anchor 'overlay' (xBar - Stroke * 0.25) [mix bottom top 0.5] + set-base-anchor 'palatalHookAttach' xBar bottom + set-base-anchor 'palatalHookPos' (xBar + [PalatalHook.adviceGap Stroke]) top return : object xBar rBottomSerif rTopSerif fine xArchMiddle skew rHookX rHookXN rHookY hookSuperness setMarks define [StandardShape df md doTopSerif doBottomSerif] : glyph-proc - define [object xBar rBottomSerif rTopSerif fine xArchMiddle skew rHookX rHookY hookSuperness setMarks] : RDim df md - include : setMarks doTopSerif + define [object xBar rBottomSerif rTopSerif fine xArchMiddle skew rHookX rHookY hookSuperness] : RDim df md include : dispiro widths.lhs g4.up.start rHookX (XH - rHookY - Stroke * 0.5) [heading Upward] @@ -97,8 +97,7 @@ glyph-block Letter-Latin-Lower-R : begin define [CompactShape df md ts bs] : CompactShapeImpl df md false ts bs define [CornerHookShape df md ts bs] : CompactShapeImpl df md true ts bs define [CompactShapeImpl df md doHookSerif doTopSerif doBottomSerif] : glyph-proc - define [object xBar rBottomSerif rTopSerif fine xArchMiddle rHookXN setMarks] : RDim df md - include : setMarks doTopSerif + define [object xBar rBottomSerif rTopSerif fine xArchMiddle rHookXN] : RDim df md define xCor : if doHookSerif 0 : CorrectionOMidS * [linreg 72 0.75 108 1 Stroke] define arcTopShift : match md @@ -126,8 +125,7 @@ glyph-block Letter-Latin-Lower-R : begin if doTopSerif : include : rTopSerif XH define [EarlessCornerShape df md doTopSerif doBottomSerif] : glyph-proc - define [object xBar xArchMiddle rHookX rHookY hookSuperness rBottomSerif setMarks] : RDim df md - include : setMarks doTopSerif + define [object xBar xArchMiddle rHookX rHookY hookSuperness rBottomSerif] : RDim df md include : dispiro widths.lhs g4.up.start rHookX (XH - rHookY - Stroke * 0.5) [heading Upward] @@ -138,8 +136,7 @@ glyph-block Letter-Latin-Lower-R : begin if doBottomSerif : include : rBottomSerif 0 define [EarlessRoundedShape df md doTopSerif doBottomSerif] : glyph-proc - define [object xBar xArchMiddle rHookX rHookY hookSuperness rBottomSerif setMarks] : RDim df md - include : setMarks doTopSerif + define [object xBar xArchMiddle rHookX rHookY hookSuperness rBottomSerif] : RDim df md local hx : Math.max rHookX (xBar + 1.25 * Stroke) include : dispiro widths.lhs @@ -150,9 +147,8 @@ glyph-block Letter-Latin-Lower-R : begin if doBottomSerif : include : rBottomSerif 0 define [FlapHooklessShape df md doTopSerif doBottomSerif] : glyph-proc - define [object xBar rBottomSerif xArchMiddle setMarks] : RDim df md + define [object xBar rBottomSerif xArchMiddle] : RDim df md set-base-anchor 'overlay' (xBar - [HSwToV : 0.5 * Stroke]) (XH / 2) - include : setMarks doTopSerif include : dispiro widths.lhs g4.left.start (xArchMiddle - CorrectionOMidS * [linreg 72 0.75 108 1 Stroke]) (XH - O) @@ -184,12 +180,20 @@ glyph-block Letter-Latin-Lower-R : begin create-glyph "r.\(suffix)" : glyph-proc set-width df.width include : df.markSet.e + + define [object setMarks] : RDim df mode + include : setMarks doTS XH 0 + include : F df mode doTS doBS + currentGlyph.copyBaseAnchorIfAbsent 'leaningBelow' 'below' create-glyph "rLongLeg.\(suffix)" : glyph-proc set-width df.width include : df.markSet.p - define [object xBar rBottomSerif] : RDim df mode + + define [object xBar rBottomSerif setMarks] : RDim df mode + include : setMarks doTS XH Descender + include : F df mode doTS 0 eject-contour 'serifLB' include : VBar.r xBar Descender 0 @@ -230,11 +234,10 @@ glyph-block Letter-Latin-Lower-R : begin include : RetroflexHook.rExt xBar 0 create-glyph "rTurnRTail.\(suffix)" : glyph-proc - set-width df.width - include : df.markSet.e - include : F df mode 0 doBS + include [refer-glyph "r.\(suffix)"] AS_BASE ALSO_METRICS eject-contour 'serifLT' include : FlipAround df.middle (XH / 2) + include : df.markSet.e define [object xBar] : RDim df mode include : RetroflexHook.lExt (df.rightSB - xBar + df.leftSB) 0 diff --git a/font-src/glyphs/letter/latin/lower-t.ptl b/font-src/glyphs/letter/latin/lower-t.ptl index d671d6f19..3aaf91c43 100644 --- a/font-src/glyphs/letter/latin/lower-t.ptl +++ b/font-src/glyphs/letter/latin/lower-t.ptl @@ -67,12 +67,12 @@ glyph-block Letter-Latin-Lower-T : begin local g : include : HookShapeT dispiro df sym 0 top bot Stroke local gEnd g.knots.(g.knots.length - 1) - set-base-anchor 'bottomRight' gEnd.x gEnd.y local xLeft : BarLeftPos df sym - set-base-anchor 'above' (xLeft + [HSwToV HalfStroke]) top + set-base-anchor 'leaningAbove' (xLeft + [HSwToV HalfStroke]) top set-base-anchor 'hooktopAttach' (xLeft + [HSwToV HalfStroke]) top set-base-anchor 'below' [mix xLeft gEnd.x : StrokeWidthBlend 0.375 0.5] bot + set-base-anchor 'bottomRight' gEnd.x gEnd.y set-base-anchor 'overlay' (g.knots.0.x + [HSwToV : 0.625 * Stroke]) (XH * 0.58) export : define [Retroflex df sym top bot] : Flat.Retroflex df sym top bot @@ -96,9 +96,9 @@ glyph-block Letter-Latin-Lower-T : begin local gEnd g.rhsKnots.(g.rhsKnots.length - 1) - set-base-anchor 'bottomRight' gEnd.x gEnd.y - set-base-anchor 'above' ([BarLeftPos df sym] + [HSwToV HalfStroke]) top + set-base-anchor 'leaningAbove' ([BarLeftPos df sym] + [HSwToV HalfStroke]) top set-base-anchor 'hooktopAttach' ([BarLeftPos df sym] + [HSwToV HalfStroke]) top + set-base-anchor 'bottomRight' gEnd.x gEnd.y set-base-anchor 'overlay' (g.knots.0.x + [HSwToV : 0.125 * Stroke]) (XH * 0.58) export : define [Retroflex df sym top bot] : Flat.Retroflex df sym top bot @@ -147,7 +147,7 @@ glyph-block Letter-Latin-Lower-T : begin flat xBarLeft [if (shape === RETROFLEX) (bot + Hook + HalfStroke) hd.y] curl xBarLeft top [heading Upward] - set-base-anchor 'above' (xBarLeft + [HSwToV HalfStroke]) top + set-base-anchor 'leaningAbove' (xBarLeft + [HSwToV HalfStroke]) top set-base-anchor 'hooktopAttach' (xBarLeft + [HSwToV HalfStroke]) top set-base-anchor 'topRight' xCrossRight Ascender set-base-anchor 'overlay' (xBarLeft + [HSwToV : 0.625 * Stroke]) (XH * 0.58) @@ -172,7 +172,8 @@ glyph-block Letter-Latin-Lower-T : begin include : HCrossBar.top xcl xcr yCrossBar set-base-anchor 'overlay' (xLeft + [HSwToV HalfStroke]) (XH * 0.58) set-base-anchor 'hooktopAttach' (xLeft + [HSwToV HalfStroke]) top - set-base-anchor 'below' (xLeft + [HSwToV HalfStroke]) bot + set-base-anchor 'leaningAbove' (xLeft + [HSwToV HalfStroke]) top + set-base-anchor 'leaningBelow' (xLeft + [HSwToV HalfStroke]) bot set-base-anchor 'bottomRight' xEnd (bot + Stroke) set-base-anchor 'lTailHookAttach' xEnd (bot + Stroke) @@ -202,6 +203,8 @@ glyph-block Letter-Latin-Lower-T : begin set-width df.width include : df.markSet.b include : Style.Body df sym top 0 + currentGlyph.copyBaseAnchorIfAbsent 'leaningAbove' 'above' + currentGlyph.copyBaseAnchorIfAbsent 'leaningBelow' 'below' create-glyph "t/phoneticLeft2.\(suffix)" : glyph-proc set-width df.width diff --git a/font-src/glyphs/letter/latin/upper-f.ptl b/font-src/glyphs/letter/latin/upper-f.ptl index 857b01e94..2bba5a806 100644 --- a/font-src/glyphs/letter/latin/upper-f.ptl +++ b/font-src/glyphs/letter/latin/upper-f.ptl @@ -27,6 +27,7 @@ glyph-block Letter-Latin-Upper-F : begin define xFBarRight : Width - SB * 1.5 define [FShape] : with-params [top pyBar serifLT serifLB serifV [stroke : AdviceStroke2 2 3 top]] : glyph-proc + set-base-anchor 'leaningBelow' (xFBarLeft + [HSwToV : 0.5 * stroke]) 0 include : VBar.l (xFBarLeft) 0 top stroke include : HBar.t (xFBarLeft - O) RightSB top stroke include : tagged 'crossBar' diff --git a/font-src/glyphs/letter/latin/upper-l.ptl b/font-src/glyphs/letter/latin/upper-l.ptl index 0994bc901..dd0824756 100644 --- a/font-src/glyphs/letter/latin/upper-l.ptl +++ b/font-src/glyphs/letter/latin/upper-l.ptl @@ -16,6 +16,7 @@ glyph-block Letter-Latin-Upper-L : begin define [LBarLeftX df] : df.leftSB * 1.5 define [LRightX df] : df.rightSB - 0.75 * OX define [LShape df top sgr swv] : glyph-proc + set-base-anchor 'leaningAbove' ([LBarLeftX df] + [HSwToV : 0.5 * swv]) top include : VBar.l [LBarLeftX df] 0 top swv include : HBar.b ([LBarLeftX df] - O) [LRightX df] 0 if (sgr > 1) : begin diff --git a/font-src/glyphs/letter/latin/upper-p.ptl b/font-src/glyphs/letter/latin/upper-p.ptl index b0b562aed..dea236428 100644 --- a/font-src/glyphs/letter/latin/upper-p.ptl +++ b/font-src/glyphs/letter/latin/upper-p.ptl @@ -71,7 +71,7 @@ glyph-block Letter-Latin-Upper-P : begin include : spiro-outline PShapeOutlineKnots top mul bp overshoot sw offset - define [PShape] : with-params [top [mul PShape.defaultMul] [bp PShape.BarPos] [overshoot PShape.defaultOvershoot] [sw Stroke] [slab null] [withBar true]] : glyph-proc + define [PShape] : with-params [top [mul PShape.defaultMul] [bp PShape.BarPos] [overshoot PShape.defaultOvershoot] [sw Stroke] [slab null] [withBar true] [setMark false]] : glyph-proc include : dispiro [widths.rhs sw] [PShapeOutlineKnots top mul bp overshoot sw 0] if withBar : include : tagged 'strokeL' : VBar.l (SB * mul) 0 top sw if slab : include : slab top sw mul @@ -141,6 +141,7 @@ glyph-block Letter-Latin-Upper-P : begin create-glyph "P.\(suffix)" : glyph-proc include : MarkSet.capital + set-base-anchor 'leaningBelow' (SB * PShape.defaultMul + [HSwToV HalfStroke]) 0 include : difference PShape CAP (slab -- slabs) if fGap [PShape.OpenGap (top -- CAP) (bot -- [if fSlabBot Stroke 0])] [glyph-proc] @@ -151,6 +152,7 @@ glyph-block Letter-Latin-Upper-P : begin create-glyph "smcpP.\(suffix)" : glyph-proc include : MarkSet.e + set-base-anchor 'leaningBelow' (SB * PShape.defaultMul + [HSwToV HalfStroke]) 0 include : difference PShape XH (slab -- slabs) if fGap [PShape.OpenGap (top -- XH) (bot -- [if fSlabBot Stroke 0])] [glyph-proc] diff --git a/font-src/glyphs/marks/above.ptl b/font-src/glyphs/marks/above.ptl index 5b9469833..424f19224 100644 --- a/font-src/glyphs/marks/above.ptl +++ b/font-src/glyphs/marks/above.ptl @@ -4,7 +4,7 @@ import [Arcs Quadify ShapeConv] from "typo-geom" import [OffsetCurve BezToContoursSink GEOMETRY_PRECISION] from"../../support/geometry/curve-util.mjs" import [mix linreg clamp fallback] from"../../support/utils.mjs" import [DesignParameters] from"../../meta/aesthetics.mjs" -import [TieMark TieGlyph] from"../../support/gr.mjs" +import [ScheduleLeaningMark] from"../../support/gr.mjs" import [Box] from"../../support/geometry/box.mjs" import [Point] from"../../support/geometry/point.mjs" @@ -30,16 +30,17 @@ glyph-block Mark-Above : begin define commaAboveRadius : 0.85 * DotRadius * markHalfStroke / HalfStroke define StdAnchors : namespace - export : define [impl mk padding k] : glyph-proc + export : define [impl mk padding k fLeaning] : glyph-proc if (mk === 'above') : then : set-mark-anchor mk markMiddle (XH - padding * AccentHeight) markMiddle (aboveMarkStack + padding * AccentHeight) : else : set-mark-anchor mk markMiddle (XH - padding * AccentHeight) set-base-anchor 'aboveBraceL' (markMiddle - k * markExtend) aboveMarkMid set-base-anchor 'aboveBraceR' (markMiddle + k * markExtend) aboveMarkMid + if fLeaning : ScheduleLeaningMark.set currentGlyph - export : define [narrow] : impl 'above' 0 0 - export : define [mediumNarrow] : impl 'above' 0 0.25 - export : define [medium] : impl 'above' 0 0.5 + export : define [narrow] : impl 'above' 0 0 true + export : define [mediumNarrow] : impl 'above' 0 0.25 true + export : define [medium] : impl 'above' 0 0.5 true export : define [mediumWide] : impl 'above' 0 0.75 export : define [wide] : impl 'above' 0 1 export : define [extraWide] : impl 'above' 0 1.5 @@ -50,6 +51,7 @@ glyph-block Mark-Above : begin set-width 0 include : StdAnchors.narrow include : DrawAt markMiddle aboveMarkMid (DotRadius * kdr) + ScheduleLeaningMark.set currentGlyph create-glyph "dieresisAbove.\(suffix)" : glyph-proc set-width 0 @@ -195,7 +197,7 @@ glyph-block Mark-Above : begin create-glyph 'circumflexAbove' 0x302 : glyph-proc set-width 0 - include : StdAnchors.wide + include : StdAnchors.medium include : CaretShape xMiddle -- markMiddle width -- CaretCaronWidth @@ -206,7 +208,7 @@ glyph-block Mark-Above : begin create-glyph 'bardownAbove' 0x1DC6 : glyph-proc set-width 0 - include : StdAnchors.wide + include : StdAnchors.medium include : CaretRightShape xMiddle -- markMiddle width -- CaretCaronWidth @@ -225,7 +227,7 @@ glyph-block Mark-Above : begin create-glyph 'upbarAbove' 0x1DC7 : glyph-proc set-width 0 - include : StdAnchors.wide + include : StdAnchors.medium include : CaretLeftShape xMiddle -- markMiddle width -- CaretCaronWidth @@ -258,7 +260,7 @@ glyph-block Mark-Above : begin create-glyph 'caronAbove' 0x30c : glyph-proc set-width 0 - include : StdAnchors.wide + include : StdAnchors.medium include : CaronShape xMiddle -- markMiddle width -- CaretCaronWidth @@ -269,7 +271,7 @@ glyph-block Mark-Above : begin create-glyph 'barupAbove' 0x1DC4 : glyph-proc set-width 0 - include : StdAnchors.wide + include : StdAnchors.medium include : CaronRightShape xMiddle -- markMiddle width -- CaretCaronWidth @@ -288,7 +290,7 @@ glyph-block Mark-Above : begin create-glyph 'downbarAbove' 0x1DC5 : glyph-proc set-width 0 - include : StdAnchors.wide + include : StdAnchors.medium include : CaronLeftShape xMiddle -- markMiddle width -- CaretCaronWidth diff --git a/font-src/glyphs/marks/adjust.ptl b/font-src/glyphs/marks/adjust.ptl index 94e056773..01164f5f9 100644 --- a/font-src/glyphs/marks/adjust.ptl +++ b/font-src/glyphs/marks/adjust.ptl @@ -8,9 +8,15 @@ glyph-block Mark-Adjustment : begin if currentGlyph.baseAnchors.below : begin local a : currentGlyph.gizmo.unapply currentGlyph.baseAnchors.below if (a.y > y) : set-base-anchor 'below' a.x y + if currentGlyph.baseAnchors.leaningBelow : begin + local a : currentGlyph.gizmo.unapply currentGlyph.baseAnchors.leaningBelow + if (a.y > y) : set-base-anchor 'leaningBelow' a.x y glyph-block-export ExtendAboveBaseAnchors define [ExtendAboveBaseAnchors y] : glyph-proc if currentGlyph.baseAnchors.above : begin local a : currentGlyph.gizmo.unapply currentGlyph.baseAnchors.above if (a.y < y) : set-base-anchor 'above' a.x y + if currentGlyph.baseAnchors.leaningAbove : begin + local a : currentGlyph.gizmo.unapply currentGlyph.baseAnchors.leaningAbove + if (a.y < y) : set-base-anchor 'leaningAbove' a.x y diff --git a/font-src/glyphs/marks/below.ptl b/font-src/glyphs/marks/below.ptl index 2e851caf4..bb6e050a2 100644 --- a/font-src/glyphs/marks/below.ptl +++ b/font-src/glyphs/marks/below.ptl @@ -3,7 +3,7 @@ $$include '../../meta/macros.ptl' import [Arcs Quadify ShapeConv] from "typo-geom" import [mix linreg clamp fallback] from"../../support/utils.mjs" import [DesignParameters] from"../../meta/aesthetics.mjs" -import [TieMark TieGlyph] from"../../support/gr.mjs" +import [ScheduleLeaningMark] from"../../support/gr.mjs" import [Box] from"../../support/geometry/box.mjs" glyph-module @@ -22,14 +22,15 @@ glyph-block Mark-Below : begin define belowMarkStack (0 - AccentStackOffset) define StdAnchors : namespace - export : define [impl padding k] : glyph-proc + export : define [impl padding k fLeaning] : glyph-proc set-mark-anchor 'below' markMiddle (0 + padding * AccentHeight) markMiddle (belowMarkStack - padding * AccentHeight) set-base-anchor 'belowBraceL' (markMiddle - k * markExtend) belowMarkMid set-base-anchor 'belowBraceR' (markMiddle + k * markExtend) belowMarkMid + if fLeaning : ScheduleLeaningMark.set currentGlyph - export : define [narrow] : impl 0 0 - export : define [mediumNarrow] : impl 0 0.25 - export : define [medium] : impl 0 0.5 + export : define [narrow] : impl 0 0 true + export : define [mediumNarrow] : impl 0 0.25 true + export : define [medium] : impl 0 0.5 true export : define [mediumWide] : impl 0 0.75 export : define [wide] : impl 0 1 @@ -174,6 +175,7 @@ glyph-block Mark-Below : begin mirrorAnchor aboveGlyph currentGlyph 'above' 'below' mirrorAnchor aboveGlyph currentGlyph 'aboveBraceL' 'belowBraceR' mirrorAnchor aboveGlyph currentGlyph 'aboveBraceR' 'belowBraceL' + if [ScheduleLeaningMark.get aboveGlyph] : ScheduleLeaningMark.set currentGlyph TurnAboveMarkToBelow 'graveBelow' 0x316 'graveAbove' TurnAboveMarkToBelow 'acuteBelow' 0x317 'acuteAbove' diff --git a/font-src/glyphs/marks/composite.ptl b/font-src/glyphs/marks/composite.ptl index c71aa8a87..be3ae8f95 100644 --- a/font-src/glyphs/marks/composite.ptl +++ b/font-src/glyphs/marks/composite.ptl @@ -3,7 +3,6 @@ $$include '../../meta/macros.ptl' import [Arcs Quadify ShapeConv] from "typo-geom" import [mix linreg clamp fallback] from"../../support/utils.mjs" import [DesignParameters] from"../../meta/aesthetics.mjs" -import [TieMark TieGlyph] from"../../support/gr.mjs" import [Box] from"../../support/geometry/box.mjs" glyph-module diff --git a/font-src/glyphs/marks/horn-and-angle.ptl b/font-src/glyphs/marks/horn-and-angle.ptl index 9d77ae2fb..816e213b6 100644 --- a/font-src/glyphs/marks/horn-and-angle.ptl +++ b/font-src/glyphs/marks/horn-and-angle.ptl @@ -3,7 +3,6 @@ $$include '../../meta/macros.ptl' import [Arcs Quadify ShapeConv] from "typo-geom" import [mix linreg clamp fallback] from"../../support/utils.mjs" import [DesignParameters] from"../../meta/aesthetics.mjs" -import [TieMark TieGlyph] from"../../support/gr.mjs" import [Box] from"../../support/geometry/box.mjs" glyph-module diff --git a/font-src/glyphs/marks/overlay.ptl b/font-src/glyphs/marks/overlay.ptl index a0107e192..035fc8a62 100644 --- a/font-src/glyphs/marks/overlay.ptl +++ b/font-src/glyphs/marks/overlay.ptl @@ -3,7 +3,6 @@ $$include '../../meta/macros.ptl' import [Arcs Quadify ShapeConv] from "typo-geom" import [mix linreg clamp fallback] from"../../support/utils.mjs" import [DesignParameters] from"../../meta/aesthetics.mjs" -import [TieMark TieGlyph] from"../../support/gr.mjs" import [Box] from"../../support/geometry/box.mjs" glyph-module diff --git a/font-src/glyphs/marks/shared-metrics.ptl b/font-src/glyphs/marks/shared-metrics.ptl index 531c417e1..42e8b3361 100644 --- a/font-src/glyphs/marks/shared-metrics.ptl +++ b/font-src/glyphs/marks/shared-metrics.ptl @@ -3,7 +3,6 @@ $$include '../../meta/macros.ptl' import [Arcs Quadify ShapeConv] from "typo-geom" import [mix linreg clamp fallback] from"../../support/utils.mjs" import [DesignParameters] from"../../meta/aesthetics.mjs" -import [TieMark TieGlyph] from"../../support/gr.mjs" import [Box] from"../../support/geometry/box.mjs" glyph-module diff --git a/font-src/meta/aesthetics.ptl b/font-src/meta/aesthetics.ptl index 82bc5365a..c9c2dc776 100644 --- a/font-src/meta/aesthetics.ptl +++ b/font-src/meta/aesthetics.ptl @@ -300,6 +300,7 @@ class CMarkSet set this.markAnchors markAnchors set this.baseAnchors baseAnchors public [applyToGlyph g] : begin + g.clearAnchors g.copyAnchors this export : define [compositeBaseAnchors] : begin diff --git a/font-src/otl/gpos-mark-mkmk.ptl b/font-src/otl/gpos-mark-mkmk.ptl index cd35b7c53..814141267 100644 --- a/font-src/otl/gpos-mark-mkmk.ptl +++ b/font-src/otl/gpos-mark-mkmk.ptl @@ -4,8 +4,8 @@ extern Map extern Set define MarkClasses { - 'above' 'tieAbove' 'topLeft' 'topRight' 'grekUpperTonos' 'aboveBraceL' 'aboveBraceR' - 'below' 'tieBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf' 'belowBraceL' 'belowBraceR' + 'above' 'tieAbove' 'leaningAbove' 'topLeft' 'topRight' 'grekUpperTonos' 'aboveBraceL' 'aboveBraceR' + 'below' 'tieBelow' 'leaningBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf' 'belowBraceL' 'belowBraceR' 'overlay' 'slash' 'strike' 'cvDecompose' 'enclosureInner' 'enclosureInnerFirstHalf' 'enclosureInnerSecondHalf' @@ -13,10 +13,10 @@ define MarkClasses { } define MarkInteractions : new Map : list - list 'aboveBraceL' {'aboveBraceL' 'above' 'tieAbove' 'topLeft' 'topRight' 'grekUpperTonos'} - list 'aboveBraceR' {'aboveBraceR' 'above' 'tieAbove' 'topLeft' 'topRight' 'grekUpperTonos'} - list 'belowBraceL' {'belowBraceL' 'below' 'tieBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf'} - list 'belowBraceR' {'belowBraceR' 'below' 'tieBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf'} + list 'aboveBraceL' {'aboveBraceL' 'above' 'tieAbove' 'leaningAbove' 'topLeft' 'topRight' 'grekUpperTonos'} + list 'aboveBraceR' {'aboveBraceR' 'above' 'tieAbove' 'leaningAbove' 'topLeft' 'topRight' 'grekUpperTonos'} + list 'belowBraceL' {'belowBraceL' 'below' 'tieBelow' 'leaningBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf'} + list 'belowBraceR' {'belowBraceR' 'below' 'tieBelow' 'leaningBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf'} define MkmkStackingLimit : new Set { 'above' 'below' } diff --git a/font-src/otl/gsub-ccmp.ptl b/font-src/otl/gsub-ccmp.ptl index 20e1ffc9f..02ccb1080 100644 --- a/font-src/otl/gsub-ccmp.ptl +++ b/font-src/otl/gsub-ccmp.ptl @@ -1,7 +1,7 @@ $$include '../meta/macros.ptl' import [AddCommonFeature AddFeature AddLookup AddFeatureLookup ChainRuleBuilder BeginLookupBlock EndLookupBlock UkMapToLookup UkMap2ToLookup] from"./table-util.mjs" -import [AnyCv Dotless TieMark TieGlyph OgonekTrY IsSuperscript IsSubscript] from"../support/gr.mjs" +import [Dotless TieMark TieGlyph OgonekTrY IsSuperscript IsSubscript LeaningMark LeaningMarkSpacer] from"../support/gr.mjs" import as UnicodeKnowledge from"../meta/unicode-knowledge.mjs" extern Set @@ -40,24 +40,18 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin define groupLF {} define dotlessFrom {} define dotlessTo {} - define TieMarkFrom {} - define TieMarkTo {} - define TieGlyphs {} - foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== "."): begin - if g.baseAnchors.lf : groupLF.push gid - if g.baseAnchors.grekUpperTonos : groupGrekUpperTonos.push gid + foreach { gn g } [glyphStore.namedEntries] : if (gn.(0) !== "."): begin + if g.baseAnchors.lf : groupLF.push gn + if g.baseAnchors.grekUpperTonos : groupGrekUpperTonos.push gn if [Dotless.get g] : begin - dotlessFrom.push gid + dotlessFrom.push gn dotlessTo.push [Dotless.get g] - if [TieGlyph.get g] : TieGlyphs.push gid - if [TieMark.get g] : begin - TieMarkFrom.push gid - TieMarkTo.push [TieMark.get g] define [IotaLF] : UkMapToLookup UnicodeKnowledge.iotaBelowToLfTf define [GrekUpperTonosTf] : UkMapToLookup UnicodeKnowledge.upperGrekMarkToTonosTf + # Dotless transform export-lookup : AddLookup sink : object .type 'gsub_chaining' .ignoreGlyphs [filterMarkByClassNegated markGlyphs 'above'] @@ -65,7 +59,7 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin chain-rule (dotlessFrom ~> dotlessTo) (aboveMark ~> null) chain-rule groupGrekUpperTonos [GrekUpperTonosTf] - # Iota transform (max 6 middle marks are supported) + # Iota transform export-lookup : AddLookup sink : object .type 'gsub_chaining' .ignoreGlyphs [filterMarkByClassNegated markGlyphs 'below'] @@ -84,7 +78,17 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin 'brackAbove' { 'leftBrackAbove' 'rightBrackAbove' } 'parenBelow' { 'leftParenBelow' 'rightParenBelow' } - # Tie marks + Ccmp-Group "Tie Mark Transform" : begin + define TieMarkFrom {} + define TieMarkTo {} + define TieGlyphs {} + + foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== ".") : begin + if [TieGlyph.get g] : TieGlyphs.push gid + if [TieMark.get g] : begin + TieMarkFrom.push gid + TieMarkTo.push [TieMark.get g] + define lookupTieMarkLigature : AddLookup sink : object .type 'gsub_ligature' .substitutions : {}.concat @@ -99,13 +103,47 @@ export : define [buildCCMP sink glyphStore markGlyphs] : begin inputEnds 3 apply {{.at 1 .lookup lookupTieMarkLigature}} + Ccmp-Group "Leaning Mark Trasnform" : begin + define LeaningAnchorMap : list + list 'above' 'leaningAbove' + list 'below' 'leaningBelow' + + foreach { mkCenter mkLeaning } [items-of LeaningAnchorMap] : begin + local basesToConsider {} + local markFrom {} + local markTo {} + local markSpacer {} + local splitMapping {} + + foreach { gn g } [glyphStore.namedEntries] : if (gn.(0) !== ".") : begin + if (![markGlyphs.all.has gn] && g.baseAnchors.(mkLeaning)) : basesToConsider.push gn + if (g.markAnchors.(mkCenter) && [LeaningMark.get g] && [LeaningMarkSpacer.get g]) : begin + markFrom.push gn + markTo.push [LeaningMark.get g] + markSpacer.push [LeaningMarkSpacer.get g] + splitMapping.push { gn {[LeaningMarkSpacer.get g] [LeaningMark.get g]} } + + define lookupTurnMarkIntoLeaningAndSpacer : AddLookup sink : object + .type 'gsub_multiple' + .substitutions : Object.fromEntries splitMapping + + export-lookup : AddLookup sink : object + .type 'gsub_chaining' + .ignoreGlyphs [filterMarkByClassNegated markGlyphs mkCenter] + .rules : list + object + .match { [basesToConsider.concat markSpacer] markFrom } + .inputBegins 1 + .inputEnds 2 + .apply {{.at 1 .lookup lookupTurnMarkIntoLeaningAndSpacer}} + Ccmp-Group "Rhotic Hook Transform" : begin define superscripts {} define subscripts {} - foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== "."): begin - if [IsSuperscript.get g] : superscripts.push gid - if [IsSubscript.get g] : subscripts.push gid + foreach { gn g } [glyphStore.namedEntries] : if (gn.(0) !== ".") : begin + if [IsSuperscript.get g] : superscripts.push gn + if [IsSubscript.get g] : subscripts.push gn export-lookup : AddLookup sink : object .type 'gsub_chaining' diff --git a/font-src/support/glyph-store.mjs b/font-src/support/glyph-store.mjs index e8877ba30..fe360708f 100644 --- a/font-src/support/glyph-store.mjs +++ b/font-src/support/glyph-store.mjs @@ -14,8 +14,14 @@ export class GlyphStore { namedEntries() { return this.nameForward.entries(); } - encodedEntries() { - return this.encodingForward.entries(); + glyphNames() { + return this.nameForward.keys(); + } + *encodedEntries() { + for (const [u, g] of this.encodingForward.entries()) { + const name = this.nameBackward.get(g); + if (name) yield [u, name, g]; + } } *flattenCodes(g, flatteners) { { diff --git a/font-src/support/glyph/index.mjs b/font-src/support/glyph/index.mjs index 345a2d572..2ee47e0b2 100644 --- a/font-src/support/glyph/index.mjs +++ b/font-src/support/glyph/index.mjs @@ -23,6 +23,10 @@ export class Glyph { this.ctxTag = null; } + toString() { + return ``; + } + get identifier() { return this._m_identifier; } @@ -182,6 +186,11 @@ export class Glyph { this.baseAnchors[id] = new Anchor(mbx, mby).transform(this.gizmo); } } + copyBaseAnchorIfAbsent(to, from) { + if (this.baseAnchors[from] && !this.baseAnchors[to]) { + this.baseAnchors[to] = new Anchor(this.baseAnchors[from].x, this.baseAnchors[from].y); + } + } clearAnchors() { this.baseAnchors = {}; this.markAnchors = {}; diff --git a/font-src/support/gr.mjs b/font-src/support/gr.mjs index cd62b93e1..1219c220f 100644 --- a/font-src/support/gr.mjs +++ b/font-src/support/gr.mjs @@ -20,6 +20,9 @@ export const LowerYDotAtBelow = LinkedGlyphProp("LowerYDotAtBelow"); export const DependentSelector = LinkedGlyphProp("DependentSelector"); export const MathSansSerif = LinkedGlyphProp("MathSansSerif"); export const VS01 = LinkedGlyphProp("VS01"); +export const TieMark = LinkedGlyphProp("TieMark"); +export const LeaningMark = LinkedGlyphProp("LeaningMark"); +export const LeaningMarkSpacer = LinkedGlyphProp("LeaningMarkSpacer"); function LinkedGlyphProp(key) { return { get(glyph) { @@ -30,6 +33,12 @@ function LinkedGlyphProp(key) { if (typeof toGid !== "string") throw new Error("Must supply a GID instead of a glyph"); if (!glyph.related) glyph.related = {}; glyph.related[key] = toGid; + }, + amendName(name) { + return `${key}{${name}}`; + }, + amendOtName(name) { + return `${name}.${key}`; } }; } @@ -61,25 +70,6 @@ function DecompositionProp(key) { }; } -export const TieMark = { - tag: "TMRK", - get(glyph) { - if (glyph && glyph.related) return glyph.related.TieMark; - else return null; - }, - set(glyph, toGid) { - if (typeof toGid !== "string") throw new Error("Must supply a GID instead of a glyph"); - if (!glyph.related) glyph.related = {}; - glyph.related.TieMark = toGid; - }, - amendName(name) { - return `TieMark{${name}}`; - }, - amendOtName(name) { - return name + ".tieMark"; - } -}; - export const TieGlyph = { get(glyph) { if (glyph && glyph.related) return glyph.related.TieGlyph; @@ -109,6 +99,7 @@ export const NeqLigationSlashDotted = BoolProp("NeqLigationSlashDotted"); export const OgonekTrY = BoolProp("OgonekTrY"); export const IsSuperscript = BoolProp("IsSuperscript"); export const IsSubscript = BoolProp("IsSubscript"); +export const ScheduleLeaningMark = BoolProp("ScheduleLeaningMark"); export const Joining = { get(glyph) { @@ -242,6 +233,7 @@ export function getGrTree(gid, grSetList, fnGidToGlyph) { getGrTreeImpl(gid, grSetList, fnGidToGlyph, sink); return sink; } + function getGrTreeImpl(gid, grSetList, fnGidToGlyph, sink) { if (!grSetList.length) return; const g = fnGidToGlyph(gid); @@ -463,5 +455,6 @@ export const SvInheritableRelations = [ DependentSelector, Joining, NeqLigationSlashDotted, - OgonekTrY + OgonekTrY, + ScheduleLeaningMark ]; diff --git a/font-src/support/utils.mjs b/font-src/support/utils.mjs index 8aa3c49d8..1de582122 100644 --- a/font-src/support/utils.mjs +++ b/font-src/support/utils.mjs @@ -80,3 +80,39 @@ export class $NamedParameterPair$ { this.right = r; } } + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +export const MatchUtil = { + never() { + return false; + }, + equal(x) { + return y => y === x; + }, + negate(f) { + return x => !f(x); + }, + both(a, b) { + return x => a(x) && b(x); + }, + either(a, b) { + return x => a(x) || b(x); + } +}; +export function constant(x) { + return () => x; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +export const ArrayUtil = { + mapIndexToItems(a, indexes) { + let answer = []; + for (const item of indexes) answer.push(a[item]); + return answer; + }, + insertSliceAt(a, i, b) { + a.splice(i, 0, ...b); + } +};