@weasel-js/font 1.10.1 → 1.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/defaultIgnorable.ts","../src/glyphMetrics.ts","../src/emojiCluster.ts","../src/glyphWarnings.ts","../src/resolveGlyph.ts","../src/outline/localFonts.ts","../src/textSdf.ts"],"names":[],"mappings":";;;;AACA,IAAM,MAAA,GAAmD;AAAA,EACvD,CAAC,KAAQ,GAAM,CAAA;AAAA,EAAG,CAAC,KAAQ,GAAM,CAAA;AAAA,EAAG,CAAC,MAAQ,IAAM,CAAA;AAAA,EAAG,CAAC,MAAQ,IAAM,CAAA;AAAA,EACrE,CAAC,MAAQ,IAAM,CAAA;AAAA,EAAG,CAAC,MAAQ,IAAM,CAAA;AAAA,EAAG,CAAC,MAAQ,IAAM,CAAA;AAAA,EAAG,CAAC,MAAQ,IAAM,CAAA;AAAA,EACrE,CAAC,MAAQ,IAAM,CAAA;AAAA,EAAG,CAAC,OAAQ,KAAM,CAAA;AAAA,EAAG,CAAC,OAAQ,KAAM,CAAA;AAAA,EAAG,CAAC,OAAQ,KAAM,CAAA;AAAA,EACrE,CAAC,OAAQ,KAAM,CAAA;AAAA,EAAG,CAAC,OAAQ,KAAM,CAAA;AAAA,EAAG,CAAC,QAAS,MAAO,CAAA;AAAA,EAAG,CAAC,QAAS,MAAO,CAAA;AAAA,EACzE,CAAC,QAAS,MAAO;AACnB,CAAA;AAOO,SAAS,mBAAmB,EAAA,EAAqB;AACtD,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,CAAC,CAAC,EAAA,EAAI,EAAE,CAAA,KAAM,EAAA,IAAM,EAAA,IAAM,EAAA,IAAM,EAAE,CAAA;AACvD;;;ACoBO,SAAS,YAAY,KAAA,EAA4B;AACtD,EAAA,OAAO,KAAA,CAAM,QAAA,GAAW,CAAA,IAAK,KAAA,CAAM,KAAA,GAAQ,CAAA,IAAK,KAAA,CAAM,IAAA,GAAO,CAAA,IAAK,kBAAA,CAAmB,KAAA,CAAM,EAAE,CAAA;AAC/F;AAIA,SAAS,YAAA,CAAa,MAAc,SAAA,EAAwD;AAC1F,EAAA,MAAM,SAAS,MAAuD;AACpE,IAAA,MAAM,OAAO,IAAA,CAAK,WAAA;AAClB,IAAA,OAAO,MAAM,MAAA,KAAW,MAAA,IAAa,IAAA,CAAK,OAAA,KAAY,SAClD,IAAA,GACA,MAAA;AAAA,EACN,CAAA;AACA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,KAAK,IAAA,CAAK,IAAA;AAAA,IAChB,IAAI,IAAA,GAAO;AAAE,MAAA,OAAO,KAAK,MAAA,CAAO,IAAA;AAAA,IAAM,CAAA;AAAA,IACtC,IAAI,MAAA,GAAS;AACX,MAAA,MAAM,OAAO,MAAA,EAAO;AACpB,MAAA,OAAO,OAAO,IAAA,CAAK,MAAA,GAAS,KAAK,IAAA,CAAK,IAAA,GAAO,KAAK,MAAA,CAAO,IAAA;AAAA,IAC3D,CAAA;AAAA,IACA,IAAI,OAAA,GAAU;AACZ,MAAA,MAAM,OAAO,MAAA,EAAO;AACpB,MAAA,OAAO,IAAA,IAAQ,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,IAAA;AAAA,IAC1C,CAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA,EAAQ,CAAC,CAAA,EAAG,CAAA,KAAM,IAAA,CAAK,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,EAAG,GAAA,CAAI,CAAC,CAAA,IAAK;AAAA,GACtD;AACF;AAEA,SAAS,iBAAiB,IAAA,EAAiC;AACzD,EAAA,MAAM,OAAA,GAAU,KAAK,WAAA,EAAa,OAAA;AAClC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,MAAM,IAAA,CAAK,QAAA;AAAA,IACX,MAAA,EAAQ,IAAA,CAAK,WAAA,EAAa,MAAA,IAAU,IAAA,CAAK,QAAA;AAAA,IACzC,GAAI,OAAA,KAAY,MAAA,GAAY,EAAE,OAAA,KAAY,EAAC;AAAA,IAC3C,SAAA,EAAW,CAAC,EAAA,KAAO,IAAA,CAAK,UAAU,EAAE,CAAA;AAAA,IACpC,QAAQ,CAAC,CAAA,EAAG,MAAM,IAAA,CAAK,MAAA,CAAO,GAAG,CAAC;AAAA,GACpC;AACF;AAEA,SAAS,cAAc,IAAA,EAAiC;AACtD,EAAA,OAAO,YAAA,CAAa,IAAA,CAAK,IAAA,EAAM,CAAC,EAAA,KAAO;AACrC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,YAAA,CAAa,EAAE,CAAA;AAClC,IAAA,OAAO,WAAA,CAAY,KAAK,CAAA,GAAI,KAAA,CAAM,QAAA,GAAW,IAAA;AAAA,EAC/C,CAAC,CAAA;AACH;AAEA,IAAM,MAAA,uBAAa,OAAA,EAA8B;AAEjD,SAAS,IAAA,CAAuB,MAAS,KAAA,EAAgD;AACvF,EAAA,IAAI,CAAA,GAAI,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA;AACvB,EAAA,IAAI,CAAC,CAAA,EAAG;AACN,IAAA,CAAA,GAAI,MAAM,IAAI,CAAA;AACd,IAAA,MAAA,CAAO,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,EACpB;AACA,EAAA,OAAO,CAAA;AACT;AAOO,SAAS,UAAU,MAAA,EAAiD;AACzE,EAAA,IAAI,OAAO,WAAA,EAAa,OAAO,IAAA,CAAK,MAAA,CAAO,aAAa,gBAAgB,CAAA;AACxE,EAAA,IAAI,OAAO,KAAA,EAAO;AAChB,IAAA,OAAO,KAAK,MAAA,CAAO,KAAA,CAAM,IAAA,EAAM,CAAC,SAAS,YAAA,CAAa,IAAA,EAAM,CAAC,EAAA,KAAO,KAAK,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA,EAAG,QAAA,IAAY,IAAI,CAAC,CAAA;AAAA,EAC7G;AACA,EAAA,IAAI,OAAO,WAAA,EAAa,OAAO,IAAA,CAAK,MAAA,CAAO,aAAa,aAAa,CAAA;AACrE,EAAA,OAAO,MAAA;AACT;;;AC3GA,IAAM,KAAA,GAAQ,sEAAA;AAGd,IAAM,mBAAA,GAAsB,IAAA;AAOrB,SAAS,qBAAqB,OAAA,EAA0B;AAC7D,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,IAAK,OAAA,CAAQ,WAAW,CAAC,CAAA,GAAI,qBAAqB,OAAO,KAAA;AAChF,EAAA,OAAO,MAAM,IAAA,CAAK,OAAO,KAAK,CAAC,OAAA,CAAQ,SAAS,QAAQ,CAAA;AAC1D;;;ACHO,SAAS,gBAAA,CAAiB,QAAgB,EAAA,EAAkB;AACjE,EAAA,IAAI,CAAC,oBAAA,CAAqB,CAAA,cAAA,EAAiB,MAAM,CAAA,CAAA,EAAI,EAAE,EAAE,CAAA,EAAG;AAC5D,EAAA,MAAM,EAAA,GAAK,MAAA,CAAO,aAAA,CAAc,EAAE,CAAA;AAClC,EAAA,OAAA,CAAQ,IAAA;AAAA,IACN,0BAA0B,EAAA,CAAG,QAAA,CAAS,EAAE,CAAA,CAAE,WAAA,GAAc,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,EAAA,EACpE,KAAK,SAAA,CAAU,EAAE,CAAC,CAAA,MAAA,EAAS,MAAM,qIAEd,MAAM,CAAA,oDAAA;AAAA,GAE/B;AACF;AAGA,IAAM,eAAA,GAAkB,CAAC,kBAAA,EAAoB,uBAAA,EAAyB,sBAAsB,CAAA;AAOrF,SAAS,aAAA,CAAc,MAAA,EAAgB,MAAA,EAAgB,KAAA,EAAwB;AACpF,EAAA,IAAI,WAAA,CAAY,MAAA,EAAQ,MAAA,EAAQ,KAAK,CAAA,EAAG;AACxC,EAAA,MAAM,OAAO,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,MAAM,IAAI,KAAK,CAAA,CAAA;AACzC,EAAA,IAAI,eAAA,CAAgB,IAAA,CAAK,CAAC,IAAA,KAAS,sBAAA,CAAuB,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE,CAAC,CAAA,EAAG;AAC/E,EAAA,IAAI,CAAC,oBAAA,CAAqB,CAAA,WAAA,EAAc,IAAI,EAAE,CAAA,EAAG;AACjD,EAAA,OAAA,CAAQ,IAAA;AAAA,IACN,CAAA,wBAAA,EAA2B,MAAM,CAAA,EAAA,EAAK,MAAM,IAAI,KAAK,CAAA,8TAAA;AAAA,GAMvD;AACF;;;ACbO,SAAS,OAAA,CAAQ,UAAyB,EAAA,EAAkC;AACjF,EAAA,MAAM,OAAA,GAAU,UAAU,QAAQ,CAAA;AAClC,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AACrB,EAAA,IAAI,SAAS,WAAA,EAAa;AACxB,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,SAAA,CAAU,EAAE,CAAA;AACpC,IAAA,OAAO,OAAA,KAAY,IAAA,GAAO,IAAA,GAAO,EAAE,QAAA,EAAU,SAAS,OAAA,EAAS,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,IAAA,EAAK;AAAA,EACzF;AACA,EAAA,MAAM,OAAO,QAAA,CAAS,WAAA;AACtB,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,KAAA,EAAO,IAAA,IAAQ,IAAA,EAAM,IAAA;AAC3C,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,GAAQ,IAAA,EAAM,OAAA,CAAQ,IAAI,EAAE,CAAA,GAAI,IAAA,EAAM,YAAA,CAAa,EAAE,CAAA;AAC5E,EAAA,IAAI,CAAC,QAAQ,CAAC,KAAA,IAAU,QAAQ,CAAC,WAAA,CAAY,KAAK,CAAA,EAAI,OAAO,IAAA;AAC7D,EAAA,OAAO,EAAE,QAAA,EAAU,OAAA,EAAS,SAAS,KAAA,CAAM,QAAA,EAAU,MAAM,KAAA,EAAM;AACnE;AAQO,SAAS,aACd,QAAA,EACA,MAAA,EACA,QACA,KAAA,EACA,EAAA,EACA,OAAO,IAAA,EACe;AACtB,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA;AAChC,EAAA,IAAI,KAAK,OAAO,GAAA;AAChB,EAAA,MAAM,QAAA,GAAW,oBAAA,CAAqB,MAAA,EAAQ,MAAA,EAAQ,KAAK,CAAA;AAC3D,EAAA,MAAM,SAAA,GAAY,QAAA,IAAY,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA;AAClD,EAAA,IAAI,WAAW,OAAO,SAAA;AACtB,EAAA,IAAI,IAAA,EAAM,gBAAA,CAAiB,QAAA,CAAS,QAAA,CAAS,QAAQ,EAAE,CAAA;AACvD,EAAA,OAAO,IAAA;AACT;AAUO,SAAS,mBAAA,CACd,QAAA,EACA,MAAA,EACA,MAAA,EACA,OACA,OAAA,EACsB;AACtB,EAAA,IAAI,CAAC,oBAAA,CAAqB,OAAO,CAAA,EAAG,OAAO,IAAA;AAC3C,EAAA,MAAM,OAAO,QAAA,CAAS,WAAA,GAAc,WAAW,oBAAA,CAAqB,MAAA,EAAQ,QAAQ,KAAK,CAAA;AACzF,EAAA,MAAM,OAAO,IAAA,EAAM,WAAA;AACnB,EAAA,MAAM,OAAA,GAAU,IAAA,IAAQ,SAAA,CAAU,IAAI,CAAA;AACtC,EAAA,MAAM,KAAA,GAAQ,IAAA,EAAM,cAAA,CAAe,OAAO,CAAA;AAC1C,EAAA,IAAI,CAAC,QAAQ,CAAC,IAAA,IAAQ,CAAC,OAAA,IAAW,CAAC,OAAO,OAAO,IAAA;AACjD,EAAA,OAAO,EAAE,QAAA,EAAU,IAAA,EAAM,OAAA,EAAS,OAAA,EAAS,MAAM,QAAA,EAAU,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,KAAA,EAAM;AACpF;;;AChDO,SAAS,kBAAA,GAA8B;AAC5C,EAAA,OAAO,OAAO,UAAA,KAAe,WAAA,IACxB,OAAQ,WAA+B,eAAA,KAAoB,UAAA;AAClE;AASA,IAAM,YAAA,GAAuD;AAAA,EAC3D,CAAC,cAAc,GAAG,CAAA;AAAA,EAAG,CAAC,cAAc,GAAG,CAAA;AAAA,EACvC,CAAC,aAAa,GAAG,CAAA;AAAA,EAAG,CAAC,aAAa,GAAG,CAAA;AAAA,EACrC,CAAC,cAAc,GAAG,CAAA;AAAA,EAAG,CAAC,cAAc,GAAG,CAAA;AAAA,EACvC,CAAC,YAAY,GAAG,CAAA;AAAA,EAAG,CAAC,YAAY,GAAG,CAAA;AAAA,EACnC,CAAC,aAAa,GAAG,CAAA;AAAA,EACjB,CAAC,SAAS,GAAG,CAAA;AAAA,EAAG,CAAC,SAAS,GAAG,CAAA;AAAA,EAC7B,CAAC,QAAQ,GAAG,CAAA;AAAA,EACZ,CAAC,UAAU,GAAG,CAAA;AAAA,EACd,CAAC,SAAS,GAAG,CAAA;AAAA,EACb,CAAC,QAAQ,GAAG,CAAA;AAAA,EAAG,CAAC,YAAY,GAAG,CAAA;AAAA,EAC/B,CAAC,QAAQ,GAAG,CAAA;AAAA,EAAG,CAAC,WAAW,GAAG,CAAA;AAAA,EAAG,CAAC,UAAU,GAAG;AACjD,CAAA;AAIA,SAAS,gBAAgB,KAAA,EAAuB;AAC9C,EAAA,OAAO,KAAA,CAAM,WAAA,EAAY,CAAE,OAAA,CAAQ,qCAAqC,IAAI,CAAA;AAC9E;AAQO,SAAS,eAAe,KAAA,EAAqD;AAClF,EAAA,MAAM,CAAA,GAAI,gBAAgB,KAAK,CAAA;AAC/B,EAAA,MAAM,SAAS,CAAA,CAAE,QAAA,CAAS,QAAQ,CAAA,IAAK,CAAA,CAAE,SAAS,SAAS,CAAA;AAC3D,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,IAAA,CAAK,CAAC,CAAC,IAAI,CAAA,KAAM,CAAA,CAAE,QAAA,CAAS,IAAI,CAAC,CAAA,GAAI,CAAC,CAAA,IAAK,GAAA;AACvE,EAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,MAAA,GAAS,WAAW,QAAA,EAAS;AACvD;AAOA,SAAS,oBAAoB,KAAA,EAAuB;AAClD,EAAA,MAAM,KAAA,GAAQ,gBAAgB,KAAK,CAAA,CAAE,MAAM,SAAS,CAAA,CAAE,OAAO,OAAO,CAAA;AACpE,EAAA,OAAO,MAAM,MAAA,CAAO,CAAC,MAAM,CAAA,KAAM,QAAA,IAAY,MAAM,SAAA,IAC9C,CAAC,YAAA,CAAa,IAAA,CAAK,CAAC,CAAC,IAAI,MAAM,IAAA,KAAS,CAAC,CAAC,CAAA,CAAE,MAAA;AACnD;AA2BA,eAAsB,uBAAA,CACpB,IAAA,GAAiC,EAAC,EACA;AAClC,EAAA,MAAM,QAAS,UAAA,CAA+B,eAAA;AAC9C,EAAA,IAAI,OAAO,UAAU,UAAA,EAAY;AAC/B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AAEA,EAAA,MAAM,SAAS,IAAA,CAAK,QAAA,GAAW,IAAI,GAAA,CAAI,IAAA,CAAK,QAAQ,CAAA,GAAI,IAAA;AACxD,EAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,IAAA,CAAK,UAAU,CAAA;AAKxC,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAyD;AAC5E,EAAA,KAAA,MAAW,QAAQ,IAAA,EAAM;AACvB,IAAA,IAAI,UAAU,CAAC,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA,EAAG;AACxC,IAAA,MAAM,EAAE,MAAA,EAAQ,KAAA,EAAM,GAAI,cAAA,CAAe,KAAK,KAAK,CAAA;AACnD,IAAA,MAAM,MAAM,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,CAAA,EAAI,MAAM,IAAI,KAAK,CAAA,CAAA;AAC7C,IAAA,MAAM,UAAA,GAAa,mBAAA,CAAoB,IAAA,CAAK,KAAK,CAAA;AACjD,IAAA,MAAM,IAAA,GAAO,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AAC3B,IAAA,IAAI,IAAA,IAAQ,IAAA,CAAK,UAAA,IAAc,UAAA,EAAY;AAC3C,IAAA,MAAA,CAAO,GAAA,CAAI,GAAA,EAAK,EAAE,IAAA,EAAM,YAAY,CAAA;AAAA,EACtC;AAEA,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAY;AACjC,EAAA,KAAA,MAAW,EAAE,IAAA,EAAK,IAAK,MAAA,CAAO,QAAO,EAAG;AACtC,IAAA,oBAAA,CAAqB,IAAA,CAAK,QAAQ,cAAA,CAAe,IAAA,CAAK,KAAK,CAAA,EAAG,MAAM,IAAA,CAAK,IAAA,EAAK,EAAG;AAAA;AAAA;AAAA,MAG/E,MAAA,EAAQ,oBAAA,CAAqB,IAAA,CAAK,cAAc,CAAA;AAAA;AAAA;AAAA,MAGhD,QAAQ,CAAA,MAAA,EAAS,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,cAAc,CAAC,CAAA,CAAA;AAAA,KACrD,CAAA;AACD,IAAA,QAAA,CAAS,GAAA,CAAI,KAAK,MAAM,CAAA;AAAA,EAC1B;AACA,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,CAAC,GAAG,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,aAAA,CAAc,CAAC,CAAC,CAAA;AAAA,IACzD,OAAO,MAAA,CAAO;AAAA,GAChB;AACF;;;ACnJO,IAAM,eAAA,GAAkB;AAExB,IAAM,aAAA,GAAgB;AA4BtB,IAAM,mBAAA;AAAA;AAAA,EAAiC;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAe9C,IAAM,kBAAA,GAAqB,CAAA;AAG3B,IAAM,kBAAA,GAAqB,CAAC,SAAA,GAAY,UAAA,EAAY,YAAY,UAAU,CAAA;AASnE,SAAS,eAAA,CACd,MAAA,EAA4B,MAAA,EAAgB,MAAA,EAAgB,KAAA,EACrB;AACvC,EAAA,IAAI,MAAA,KAAW,UAAU,OAAO,kBAAA;AAChC,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,MAAA,EAAQ,MAAA,EAAQ,KAAK,CAAA,EAAG,IAAA;AAC7C,EAAA,IAAI,CAAC,MAAM,OAAO,MAAA;AAClB,EAAA,MAAM,KAAA,GAAQ,KAAK,aAAA,IAAiB,kBAAA;AACpC,EAAA,OAAO,CAAC,KAAK,MAAA,CAAO,MAAA,GAAS,OAAO,IAAA,CAAK,MAAA,CAAO,SAAS,KAAK,CAAA;AAChE","file":"index.js","sourcesContent":["/** Unicode's `Default_Ignorable_Code_Point` ranges (DerivedCoreProperties.txt). */\nconst RANGES: ReadonlyArray<readonly [number, number]> = [\n [0x00ad, 0x00ad], [0x034f, 0x034f], [0x061c, 0x061c], [0x115f, 0x1160],\n [0x17b4, 0x17b5], [0x180b, 0x180f], [0x200b, 0x200f], [0x202a, 0x202e],\n [0x2060, 0x206f], [0x3164, 0x3164], [0xfe00, 0xfe0f], [0xfeff, 0xfeff],\n [0xffa0, 0xffa0], [0xfff0, 0xfff8], [0x1bca0, 0x1bca3], [0x1d173, 0x1d17a],\n [0xe0000, 0xe0fff],\n];\n\n/**\n * Whether `cp` is one a renderer shows as nothing by design: joiners,\n * variation selectors, zero-width spaces, bidi controls, the soft hyphen. A\n * face serving one as no advance and no ink has served it.\n */\nexport function isDefaultIgnorable(cp: number): boolean {\n return RANGES.some(([lo, hi]) => cp >= lo && cp <= hi);\n}\n","/**\n * One shape for the advances, kerning and baseline of whichever tier resolved\n * a face. A baked atlas and the canvas tier measure in their bake-size pixels\n * and a parsed face in ems; `size` is the divisor that takes any of them to\n * world units (`fontSize / size`), which is why an em-space face reports 1.\n */\nimport type { BmFont, BmFontChar } from './FontAtlas';\nimport { isDefaultIgnorable } from './defaultIgnorable';\nimport type { DynamicFace } from './dynamic/dynamicAtlas';\nimport type { OutlineFace } from './outline/OutlineFace';\nimport type { ResolveResult } from './registerFont';\n\n/** Where a face's advances, kerning and baseline come from. */\nexport interface GlyphMetrics {\n /** Units per em of everything else here. */\n readonly size: number;\n /** Line top to baseline in the glyph frame: what an atlas glyph's `yoffset`\n * is measured against. */\n readonly base: number;\n /** Baseline to the top of the face's line box: the ascent a browser sets\n * the face with, or `base` for a face that states none. */\n readonly ascent: number;\n /** Baseline to the bottom of the face's line box. Absent for a face that\n * states no ascent and descent, which hangs its baseline `base` below the\n * line top instead of centering the face in its line. */\n readonly descent?: number;\n /** Advance for `cp`, or null when the face has no glyph for it. On the\n * canvas tier this measures the glyph and queues its bake. */\n advanceOf(cp: number): number | null;\n kernOf(left: number, right: number): number;\n}\n\n/** A canvas face that cannot measure a code point reports no advance and no\n * ink for it, which is its only way to say it has no glyph, and exactly how\n * it serves a default-ignorable one. A glyph with ink has no width until its\n * bake lands, and says so with a negative page. */\nexport function usableGlyph(glyph: BmFontChar): boolean {\n return glyph.xadvance > 0 || glyph.width > 0 || glyph.page < 0 || isDefaultIgnorable(glyph.id);\n}\n\n/** Reads through to `font` rather than copying: the canvas tier measures a\n * face again in place when its web font lands. */\nfunction atlasMetrics(font: BmFont, advanceOf: (cp: number) => number | null): GlyphMetrics {\n const stated = (): { ascent: number; descent: number } | undefined => {\n const face = font.faceMetrics;\n return face?.ascent !== undefined && face.descent !== undefined\n ? face as { ascent: number; descent: number }\n : undefined;\n };\n return {\n size: font.info.size,\n get base() { return font.common.base; },\n get ascent() {\n const face = stated();\n return face ? face.ascent * font.info.size : font.common.base;\n },\n get descent() {\n const face = stated();\n return face && face.descent * font.info.size;\n },\n advanceOf,\n kernOf: (l, r) => font.kerningMap.get(l)?.get(r) ?? 0,\n };\n}\n\nfunction outlineMetricsOf(face: OutlineFace): GlyphMetrics {\n const descent = face.faceMetrics?.descent;\n return {\n size: 1,\n base: face.ascender,\n ascent: face.faceMetrics?.ascent ?? face.ascender,\n ...(descent !== undefined ? { descent } : {}),\n advanceOf: (cp) => face.advanceOf(cp),\n kernOf: (l, r) => face.kernOf(l, r),\n };\n}\n\nfunction canvasMetrics(face: DynamicFace): GlyphMetrics {\n return atlasMetrics(face.font, (cp) => {\n const glyph = face.requestGlyph(cp);\n return usableGlyph(glyph) ? glyph.xadvance : null;\n });\n}\n\nconst byFace = new WeakMap<object, GlyphMetrics>();\n\nfunction memo<F extends object>(face: F, build: (face: F) => GlyphMetrics): GlyphMetrics {\n let m = byFace.get(face);\n if (!m) {\n m = build(face);\n byFace.set(face, m);\n }\n return m;\n}\n\n/**\n * The metrics of whichever tier `result` resolved to, or `undefined` for a\n * miss, which has none. One object per face, so two results that landed on the\n * same face compare equal by identity.\n */\nexport function metricsOf(result: ResolveResult): GlyphMetrics | undefined {\n if (result.outlineFace) return memo(result.outlineFace, outlineMetricsOf);\n if (result.entry) {\n return memo(result.entry.font, (font) => atlasMetrics(font, (cp) => font.charMap.get(cp)?.xadvance ?? null));\n }\n if (result.dynamicFace) return memo(result.dynamicFace, canvasMetrics);\n return undefined;\n}\n","const EMOJI = /\\p{Emoji_Presentation}|\\p{Extended_Pictographic}\\uFE0F|\\uFE0F\\u20E3/u;\n\n/** The first code point that is an emoji by default (U+231A WATCH). */\nconst FIRST_DEFAULT_EMOJI = 0x231a;\n\n/**\n * Whether a grapheme cluster is drawn as an emoji rather than as text: it\n * holds a code point that is one by default, or one made so by U+FE0F, and no\n * U+FE0E asking for text.\n */\nexport function hasEmojiPresentation(cluster: string): boolean {\n if (cluster.length === 1 && cluster.charCodeAt(0) < FIRST_DEFAULT_EMOJI) return false;\n return EMOJI.test(cluster) && !cluster.includes('\\uFE0E');\n}\n","/**\n * Warnings about what the registered fonts cannot serve. Text is laid out per\n * frame, so each is claimed once through the fallback warning gate, which the\n * registry and fallback resets both clear.\n */\nimport { claimFallbackWarning, fallbackWarningClaimed } from './fallback';\nimport type { FontStyle } from './fontStyle';\nimport { fontPending } from './registerFont';\n\n/** Say, once per family and code point, that no tier has a glyph for it. */\nexport function warnMissingGlyph(family: string, cp: number): void {\n if (!claimFallbackWarning(`missing-glyph|${family}|${cp}`)) return;\n const ch = String.fromCodePoint(cp);\n console.warn(\n `weasel: no glyph for U+${cp.toString(16).toUpperCase().padStart(4, '0')} ` +\n `(${JSON.stringify(ch)}) in \"${family}\", and the dynamic tier could not ` +\n `rasterize it — skipping the character. Bake it into the atlas, or call ` +\n `registerCanvasFont(\"${family}\") to serve missing codepoints from ` +\n `installed fonts.`,\n );\n}\n\n/** The registry's own warnings that a face will draw nothing, by key prefix. */\nconst NOTHING_RENDERS = ['unusable-default', 'self-unusable-default', 'unregistered-default'];\n\n/**\n * Say, once per face, that text set in it has no metrics and lays out as\n * nothing. Silent while the face is still loading, which is late rather than\n * missing, and when the registry has already said why it cannot render.\n */\nexport function warnNoMetrics(family: string, weight: number, style: FontStyle): void {\n if (fontPending(family, weight, style)) return;\n const face = `${family}|${weight}|${style}`;\n if (NOTHING_RENDERS.some((kind) => fallbackWarningClaimed(`${kind}|${face}`))) return;\n if (!claimFallbackWarning(`no-metrics|${face}`)) return;\n console.warn(\n `weasel: no metrics for \"${family}\" ${weight}/${style} — neither ` +\n `an atlas nor an outline face resolved, so every run set in it lays out as ` +\n `nothing. Register it with registerFont / registerCanvasFont / ` +\n `registerFontOutlines. If you did, check for a duplicated @weasel-js/font in ` +\n `node_modules — its registry is module state, so a second copy is a second, ` +\n `empty one.`,\n );\n}\n","/**\n * Finding the glyph for one code point: in the tier a run resolved to, then\n * in the dynamic tier, then nowhere. A miss never draws a stand-in `?`, which\n * is indistinguishable from one the author wrote, and the BmFont atlas format\n * has no `.notdef` to draw.\n */\nimport type { BmFont, BmFontChar } from './FontAtlas';\nimport { hasEmojiPresentation } from './emojiCluster';\nimport type { FontStyle } from './fontStyle';\nimport { metricsOf, usableGlyph, type GlyphMetrics } from './glyphMetrics';\nimport { warnMissingGlyph } from './glyphWarnings';\nimport { resolveGlyphFallback, type ResolveResult } from './registerFont';\n\n/** A code point's glyph, and the tier that ended up serving it. */\nexport interface ResolvedGlyph {\n /** The tier serving the glyph. Not the run's own when the code point\n * escalated to the dynamic tier. */\n resolved: ResolveResult;\n /** That tier's metrics, which are what `advance` is measured in. */\n metrics: GlyphMetrics;\n advance: number;\n /** The atlas to sample and the glyph's record in it. Both null on the\n * outline tier, which has geometry instead. A canvas glyph's record gains\n * its atlas rect in place when its bake lands. */\n font: BmFont | null;\n glyph: BmFontChar | null;\n}\n\n/** The glyph for `cp` in the tier `resolved` landed on, or null when that\n * tier has none. */\nexport function glyphIn(resolved: ResolveResult, cp: number): ResolvedGlyph | null {\n const metrics = metricsOf(resolved);\n if (!metrics) return null;\n if (resolved.outlineFace) {\n const advance = metrics.advanceOf(cp);\n return advance === null ? null : { resolved, metrics, advance, font: null, glyph: null };\n }\n const face = resolved.dynamicFace;\n const font = resolved.entry?.font ?? face?.font;\n const glyph = resolved.entry ? font?.charMap.get(cp) : face?.requestGlyph(cp);\n if (!font || !glyph || (face && !usableGlyph(glyph))) return null;\n return { resolved, metrics, advance: glyph.xadvance, font, glyph };\n}\n\n/**\n * The glyph for `cp` in a run that asked for `(family, weight, style)` and\n * resolved to `resolved`: that tier's own, else the dynamic tier's under the\n * face the run asked for. Null when neither has it, with a warning naming the\n * code point unless `warn` is false.\n */\nexport function resolveGlyph(\n resolved: ResolveResult,\n family: string,\n weight: number,\n style: FontStyle,\n cp: number,\n warn = true,\n): ResolvedGlyph | null {\n const own = glyphIn(resolved, cp);\n if (own) return own;\n const fallback = resolveGlyphFallback(family, weight, style);\n const escalated = fallback && glyphIn(fallback, cp);\n if (escalated) return escalated;\n if (warn) warnMissingGlyph(resolved.resolved.family, cp);\n return null;\n}\n\n/**\n * The color glyph for a whole grapheme cluster with emoji presentation, in a\n * run that asked for `(family, weight, style)` and resolved to `resolved`.\n * The canvas tier is the only one that draws a cluster as one glyph or in its\n * own colors, so it serves these whatever tier serves the run's text. Null for\n * any other cluster, and where the canvas tier is not to be had or draws no\n * color for this one: the cluster is then laid out code point by code point.\n */\nexport function resolveColorCluster(\n resolved: ResolveResult,\n family: string,\n weight: number,\n style: FontStyle,\n cluster: string,\n): ResolvedGlyph | null {\n if (!hasEmojiPresentation(cluster)) return null;\n const tier = resolved.dynamicFace ? resolved : resolveGlyphFallback(family, weight, style);\n const face = tier?.dynamicFace;\n const metrics = tier && metricsOf(tier);\n const glyph = face?.requestCluster(cluster);\n if (!tier || !face || !metrics || !glyph) return null;\n return { resolved: tier, metrics, advance: glyph.xadvance, font: face.font, glyph };\n}\n","/**\n * Local Font Access → outline registrations.\n *\n * The dynamic canvas-SDF tier can render any installed family without ever\n * seeing its bytes: it asks canvas 2D to `fillText` and takes the pixels. The\n * outline tier cannot — it needs the actual font file — and the only web API\n * that hands one over is `window.queryLocalFonts()`, which is Chromium-only\n * and gated behind a permission prompt that requires a user gesture.\n *\n * So this is the one part of the tier a consumer has to *ask* for, from a\n * click. A refusal is ordinary: no registrations, and large text keeps\n * rendering from the SDF tier. Safari and Firefox have no equivalent API at\n * all, so \"denied\" is the permanent state for most of the web.\n *\n * Registration is eager but reading is lazy: every matching face gets a thunk\n * that calls `FontData.blob()` at first glyph request. A machine can have\n * hundreds of faces and a `.ttc` runs to tens of megabytes.\n */\n\nimport type { FontStyle } from '../fontStyle';\nimport { createOpenTypeParser } from './opentypeParser';\nimport { registerFontOutlines } from './outlineRegistry';\n\n/**\n * The slice of the Local Font Access API this module uses. `queryLocalFonts`\n * is not in TypeScript's DOM types.\n */\ninterface LocalFontData {\n postscriptName: string;\n fullName: string;\n family: string;\n style: string;\n blob(): Promise<Blob>;\n}\n\ntype LocalFontWindow = typeof globalThis & {\n queryLocalFonts?: (opts?: { postscriptNames?: string[] }) => Promise<LocalFontData[]>;\n};\n\n/** Is the Local Font Access API present at all? False on every non-Chromium\n * browser and in any non-secure context. */\nexport function canQueryLocalFonts(): boolean {\n return typeof globalThis !== 'undefined'\n && typeof (globalThis as LocalFontWindow).queryLocalFonts === 'function';\n}\n\n/**\n * CSS weights for the weight words that appear in a `style` string.\n *\n * Ordered longest-first where one name contains another (\"ExtraBold\" before\n * \"Bold\", \"DemiBold\" before \"Bold\"), because the match is a substring scan\n * and \"Bold\" would otherwise claim every heavier face for 700.\n */\nconst WEIGHT_WORDS: readonly (readonly [string, number])[] = [\n ['extrablack', 950], ['ultrablack', 950],\n ['extrabold', 800], ['ultrabold', 800],\n ['extralight', 200], ['ultralight', 200],\n ['semibold', 600], ['demibold', 600],\n ['semilight', 350],\n ['black', 900], ['heavy', 900],\n ['bold', 700],\n ['medium', 500],\n ['light', 300],\n ['thin', 100], ['hairline', 100],\n ['book', 400], ['regular', 400], ['normal', 400],\n];\n\n/** Lowercased, with a split weight name closed up: \"Demi Bold\" and\n * \"Semi-Bold\" read as the single words the table above lists. */\nfunction joinWeightWords(style: string): string {\n return style.toLowerCase().replace(/\\b(extra|ultra|semi|demi)[\\s_-]+/g, '$1');\n}\n\n/**\n * Parse a `FontData.style` string — \"Regular\", \"Bold Italic\", \"Condensed\n * ExtraLight Oblique\" — into the CSS variant the kit's registry is keyed by.\n *\n * Anything unrecognized lands on 400/normal.\n */\nexport function parseFontStyle(style: string): { weight: number; style: FontStyle } {\n const s = joinWeightWords(style);\n const italic = s.includes('italic') || s.includes('oblique');\n const weight = WEIGHT_WORDS.find(([word]) => s.includes(word))?.[1] ?? 400;\n return { weight, style: italic ? 'italic' : 'normal' };\n}\n\n/**\n * How many words of a `FontData.style` string are *not* weight or slant —\n * \"Condensed\", \"Display\", \"Poster\". A face with none of them is the plain\n * member of its slot, and the one to keep when two faces collide.\n */\nfunction styleQualifierCount(style: string): number {\n const words = joinWeightWords(style).split(/[\\s_-]+/).filter(Boolean);\n return words.filter((w) => w !== 'italic' && w !== 'oblique'\n && !WEIGHT_WORDS.some(([word]) => word === w)).length;\n}\n\n/** Options for `enableLocalFontOutlines`. */\nexport interface LocalFontOutlinesOptions {\n /**\n * Restrict registration to these families. Defaults to every installed\n * family, which can be hundreds of registry entries.\n */\n families?: readonly string[];\n}\n\n/** What `enableLocalFontOutlines` registered. */\nexport interface LocalFontOutlinesResult {\n /** Families that now have at least one outline face, in menu order. */\n families: readonly string[];\n /** Total faces registered across those families. */\n faces: number;\n}\n\n/**\n * Ask for permission to read installed fonts, and register outline sources\n * for the faces it returns. **Must be called from a user gesture** — the\n * permission prompt requires one, and without it the call rejects.\n *\n * Rejects when the API is missing or the user denies; neither leaves the\n * registry in a partial state.\n */\nexport async function enableLocalFontOutlines(\n opts: LocalFontOutlinesOptions = {},\n): Promise<LocalFontOutlinesResult> {\n const query = (globalThis as LocalFontWindow).queryLocalFonts;\n if (typeof query !== 'function') {\n throw new Error(\n 'weasel enableLocalFontOutlines: this browser has no Local Font Access API ' +\n '(queryLocalFonts). Large text keeps rendering from the SDF tier.',\n );\n }\n\n const wanted = opts.families ? new Set(opts.families) : null;\n const data = await query.call(globalThis);\n\n // Several installed faces can land on one (family, weight, style) slot —\n // \"Helvetica Neue Condensed Bold\" reduces to 700/normal exactly like\n // \"Helvetica Neue Bold\". Pick the least-qualified name for each slot.\n const chosen = new Map<string, { font: LocalFontData; qualifiers: number }>();\n for (const font of data) {\n if (wanted && !wanted.has(font.family)) continue;\n const { weight, style } = parseFontStyle(font.style);\n const key = `${font.family}|${weight}|${style}`;\n const qualifiers = styleQualifierCount(font.style);\n const prev = chosen.get(key);\n if (prev && prev.qualifiers <= qualifiers) continue;\n chosen.set(key, { font, qualifiers });\n }\n\n const families = new Set<string>();\n for (const { font } of chosen.values()) {\n registerFontOutlines(font.family, parseFontStyle(font.style), () => font.blob(), {\n // The PostScript name is how a member is picked out of a `.ttc`\n // collection, which is what most macOS system families ship as.\n parser: createOpenTypeParser(font.postscriptName),\n // The DOM already has every installed face; naming it avoids reading\n // the collection file a second time.\n cssSrc: `local(${JSON.stringify(font.postscriptName)})`,\n });\n families.add(font.family);\n }\n return {\n families: [...families].sort((a, b) => a.localeCompare(b)),\n faces: chosen.size,\n };\n}\n","/**\n * The GLSL that turns an atlas sample into glyph coverage, and the constants\n * naming which kind of atlas a sample came from.\n *\n * Not a program: glyphs stage into the renderer's batch alongside solid\n * geometry and image quads, and the batch shader pastes this in.\n *\n * MSDF channel layout: msdf-bmfont-xml writes R, G, and B as independent\n * signed-distance fields covering different edge directions, and the true\n * field is their median. The runtime canvas bake writes one channel that *is*\n * the field. Both encode the edge at 0.5, which is what lets one threshold\n * serve them.\n */\n\nimport type { FontStyle } from './fontStyle';\nimport { getFont } from './registerFont';\nimport { PAGE_SIZE, SDF_RADIUS } from './dynamic/dynamicAtlas';\n\n/** `a_paintMode` value for glyphs off an MSDF atlas — the median of R,G,B. */\nexport const GLYPH_MODE_MSDF = 1;\n/** `a_paintMode` value for glyphs off a runtime canvas bake — `.r` alone. */\nexport const GLYPH_MODE_R8 = 2;\n\n/**\n * Glyph coverage from one atlas sample, as GLSL for a program to paste in.\n *\n * `mode` says which field the sample carries: `GLYPH_MODE_MSDF` takes the\n * median of R,G,B, `GLYPH_MODE_R8` reads `.r` alone.\n *\n * The antialiasing band has to be one *screen* pixel wide: `fieldPerUv` is how\n * far the field runs per unit of texture coordinate on each axis (see\n * `glyphFieldScale`), and the screen derivatives of `uv` turn that into field\n * units per screen pixel.\n *\n * Not `fwidth(field)`: that is the field's slope only where the field is a\n * straight ramp. Across a stem narrower than a pixel the two columns of a 2x2\n * quad sample either side of the ridge at equal values, the derivative reads\n * flat, and the stem is thresholded away. The derivative of `uv` is constant\n * across a glyph quad, so it cannot do that. Nor a constant band, which is\n * right at one scale only.\n *\n * **Nothing here branches, and the caller must not branch around it.**\n * A derivative in non-uniform control flow is undefined, so it has to\n * be taken before anything selects on paint mode. A merged program therefore\n * runs the glyph math on fragments that are not glyphs and discards the result.\n *\n * `synthBold` shifts the threshold to thicken strokes where the resolver fell\n * back from a missing bold variant to the regular atlas.\n */\nexport const GLYPH_COVERAGE_GLSL = /* glsl */ `\nfloat median(float r, float g, float b) {\n return max(min(r, g), min(max(r, g), b));\n}\n\nfloat glyphCoverage(vec4 texel, float mode, float synthBold, vec2 uv, vec2 fieldPerUv) {\n float field = mix(median(texel.r, texel.g, texel.b), texel.r, step(1.5, mode));\n float fieldPerPx = 0.5 * (length(dFdx(uv) * fieldPerUv) + length(dFdy(uv) * fieldPerUv));\n float aaW = max(0.5 * fieldPerPx, 0.0005);\n float threshold = 0.5 - synthBold;\n return smoothstep(threshold - aaW, threshold + aaW, field);\n}\n`;\n\n/** msdf-bmfont-xml's default `distanceRange`, for an atlas that records none. */\nconst DEFAULT_MSDF_RANGE = 4;\n\n/** A runtime canvas page: `PAGE_SIZE` texels, field 0 to 1 over `SDF_RADIUS` of them. */\nconst CANVAS_FIELD_SCALE = [PAGE_SIZE / SDF_RADIUS, PAGE_SIZE / SDF_RADIUS] as const;\n\n/**\n * Field units per unit of texture coordinate along u and v for the atlas a\n * glyph group samples — the `fieldPerUv` that `glyphCoverage` takes. A baked\n * MSDF atlas answers from its page size and `distanceRange`, a runtime canvas\n * page from the dynamic tier's page size and bake radius. `undefined` for an\n * atlas that is not registered.\n */\nexport function glyphFieldScale(\n source: 'atlas' | 'canvas', family: string, weight: number, style: FontStyle,\n): readonly [number, number] | undefined {\n if (source === 'canvas') return CANVAS_FIELD_SCALE;\n const font = getFont(family, weight, style)?.font;\n if (!font) return undefined;\n const range = font.distanceRange ?? DEFAULT_MSDF_RANGE;\n return [font.common.scaleW / range, font.common.scaleH / range];\n}\n"]}
1
+ {"version":3,"sources":["../src/outline/fontEmbedding.ts","../src/defaultIgnorable.ts","../src/glyphMetrics.ts","../src/emojiCluster.ts","../src/glyphWarnings.ts","../src/resolveGlyph.ts","../src/outline/localFonts.ts","../src/textSdf.ts"],"names":[],"mappings":";;;;;AASA,eAAsB,aAAA,CACpB,MAAA,EAAgB,MAAA,EAAgB,KAAA,EACD;AAC/B,EAAA,IAAK,MAAM,iBAAiB,MAAA,EAAQ,EAAE,QAAQ,KAAA,EAAO,CAAA,KAAO,OAAA,EAAS,OAAO,IAAA;AAC5E,EAAA,OAAO,eAAe,MAAA,EAAQ,MAAA,EAAQ,KAAK,CAAA,EAAG,aAAY,IAAK,IAAA;AACjE;;;ACbA,IAAM,MAAA,GAAmD;AAAA,EACvD,CAAC,KAAQ,GAAM,CAAA;AAAA,EAAG,CAAC,KAAQ,GAAM,CAAA;AAAA,EAAG,CAAC,MAAQ,IAAM,CAAA;AAAA,EAAG,CAAC,MAAQ,IAAM,CAAA;AAAA,EACrE,CAAC,MAAQ,IAAM,CAAA;AAAA,EAAG,CAAC,MAAQ,IAAM,CAAA;AAAA,EAAG,CAAC,MAAQ,IAAM,CAAA;AAAA,EAAG,CAAC,MAAQ,IAAM,CAAA;AAAA,EACrE,CAAC,MAAQ,IAAM,CAAA;AAAA,EAAG,CAAC,OAAQ,KAAM,CAAA;AAAA,EAAG,CAAC,OAAQ,KAAM,CAAA;AAAA,EAAG,CAAC,OAAQ,KAAM,CAAA;AAAA,EACrE,CAAC,OAAQ,KAAM,CAAA;AAAA,EAAG,CAAC,OAAQ,KAAM,CAAA;AAAA,EAAG,CAAC,QAAS,MAAO,CAAA;AAAA,EAAG,CAAC,QAAS,MAAO,CAAA;AAAA,EACzE,CAAC,QAAS,MAAO;AACnB,CAAA;AAOO,SAAS,mBAAmB,EAAA,EAAqB;AACtD,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,CAAC,CAAC,EAAA,EAAI,EAAE,CAAA,KAAM,EAAA,IAAM,EAAA,IAAM,EAAA,IAAM,EAAE,CAAA;AACvD;;;ACoBO,SAAS,YAAY,KAAA,EAA4B;AACtD,EAAA,OAAO,KAAA,CAAM,QAAA,GAAW,CAAA,IAAK,KAAA,CAAM,KAAA,GAAQ,CAAA,IAAK,KAAA,CAAM,IAAA,GAAO,CAAA,IAAK,kBAAA,CAAmB,KAAA,CAAM,EAAE,CAAA;AAC/F;AAIA,SAAS,YAAA,CAAa,MAAc,SAAA,EAAwD;AAC1F,EAAA,MAAM,SAAS,MAAuD;AACpE,IAAA,MAAM,OAAO,IAAA,CAAK,WAAA;AAClB,IAAA,OAAO,MAAM,MAAA,KAAW,MAAA,IAAa,IAAA,CAAK,OAAA,KAAY,SAClD,IAAA,GACA,MAAA;AAAA,EACN,CAAA;AACA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,KAAK,IAAA,CAAK,IAAA;AAAA,IAChB,IAAI,IAAA,GAAO;AAAE,MAAA,OAAO,KAAK,MAAA,CAAO,IAAA;AAAA,IAAM,CAAA;AAAA,IACtC,IAAI,MAAA,GAAS;AACX,MAAA,MAAM,OAAO,MAAA,EAAO;AACpB,MAAA,OAAO,OAAO,IAAA,CAAK,MAAA,GAAS,KAAK,IAAA,CAAK,IAAA,GAAO,KAAK,MAAA,CAAO,IAAA;AAAA,IAC3D,CAAA;AAAA,IACA,IAAI,OAAA,GAAU;AACZ,MAAA,MAAM,OAAO,MAAA,EAAO;AACpB,MAAA,OAAO,IAAA,IAAQ,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,IAAA;AAAA,IAC1C,CAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA,EAAQ,CAAC,CAAA,EAAG,CAAA,KAAM,IAAA,CAAK,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,EAAG,GAAA,CAAI,CAAC,CAAA,IAAK;AAAA,GACtD;AACF;AAEA,SAAS,iBAAiB,IAAA,EAAiC;AACzD,EAAA,MAAM,OAAA,GAAU,KAAK,WAAA,EAAa,OAAA;AAClC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,MAAM,IAAA,CAAK,QAAA;AAAA,IACX,MAAA,EAAQ,IAAA,CAAK,WAAA,EAAa,MAAA,IAAU,IAAA,CAAK,QAAA;AAAA,IACzC,GAAI,OAAA,KAAY,MAAA,GAAY,EAAE,OAAA,KAAY,EAAC;AAAA,IAC3C,SAAA,EAAW,CAAC,EAAA,KAAO,IAAA,CAAK,UAAU,EAAE,CAAA;AAAA,IACpC,QAAQ,CAAC,CAAA,EAAG,MAAM,IAAA,CAAK,MAAA,CAAO,GAAG,CAAC;AAAA,GACpC;AACF;AAEA,SAAS,cAAc,IAAA,EAAiC;AACtD,EAAA,OAAO,YAAA,CAAa,IAAA,CAAK,IAAA,EAAM,CAAC,EAAA,KAAO;AACrC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,YAAA,CAAa,EAAE,CAAA;AAClC,IAAA,OAAO,WAAA,CAAY,KAAK,CAAA,GAAI,KAAA,CAAM,QAAA,GAAW,IAAA;AAAA,EAC/C,CAAC,CAAA;AACH;AAEA,IAAM,MAAA,uBAAa,OAAA,EAA8B;AAEjD,SAAS,IAAA,CAAuB,MAAS,KAAA,EAAgD;AACvF,EAAA,IAAI,CAAA,GAAI,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA;AACvB,EAAA,IAAI,CAAC,CAAA,EAAG;AACN,IAAA,CAAA,GAAI,MAAM,IAAI,CAAA;AACd,IAAA,MAAA,CAAO,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,EACpB;AACA,EAAA,OAAO,CAAA;AACT;AAOO,SAAS,UAAU,MAAA,EAAiD;AACzE,EAAA,IAAI,OAAO,WAAA,EAAa,OAAO,IAAA,CAAK,MAAA,CAAO,aAAa,gBAAgB,CAAA;AACxE,EAAA,IAAI,OAAO,KAAA,EAAO;AAChB,IAAA,OAAO,KAAK,MAAA,CAAO,KAAA,CAAM,IAAA,EAAM,CAAC,SAAS,YAAA,CAAa,IAAA,EAAM,CAAC,EAAA,KAAO,KAAK,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA,EAAG,QAAA,IAAY,IAAI,CAAC,CAAA;AAAA,EAC7G;AACA,EAAA,IAAI,OAAO,WAAA,EAAa,OAAO,IAAA,CAAK,MAAA,CAAO,aAAa,aAAa,CAAA;AACrE,EAAA,OAAO,MAAA;AACT;;;AC3GA,IAAM,KAAA,GAAQ,sEAAA;AAGd,IAAM,mBAAA,GAAsB,IAAA;AAOrB,SAAS,qBAAqB,OAAA,EAA0B;AAC7D,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,IAAK,OAAA,CAAQ,WAAW,CAAC,CAAA,GAAI,qBAAqB,OAAO,KAAA;AAChF,EAAA,OAAO,MAAM,IAAA,CAAK,OAAO,KAAK,CAAC,OAAA,CAAQ,SAAS,QAAQ,CAAA;AAC1D;;;ACHO,SAAS,gBAAA,CAAiB,QAAgB,EAAA,EAAkB;AACjE,EAAA,IAAI,CAAC,oBAAA,CAAqB,CAAA,cAAA,EAAiB,MAAM,CAAA,CAAA,EAAI,EAAE,EAAE,CAAA,EAAG;AAC5D,EAAA,MAAM,EAAA,GAAK,MAAA,CAAO,aAAA,CAAc,EAAE,CAAA;AAClC,EAAA,OAAA,CAAQ,IAAA;AAAA,IACN,0BAA0B,EAAA,CAAG,QAAA,CAAS,EAAE,CAAA,CAAE,WAAA,GAAc,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,EAAA,EACpE,KAAK,SAAA,CAAU,EAAE,CAAC,CAAA,MAAA,EAAS,MAAM,qIAEd,MAAM,CAAA,oDAAA;AAAA,GAE/B;AACF;AAGA,IAAM,eAAA,GAAkB,CAAC,kBAAA,EAAoB,uBAAA,EAAyB,sBAAsB,CAAA;AAOrF,SAAS,aAAA,CAAc,MAAA,EAAgB,MAAA,EAAgB,KAAA,EAAwB;AACpF,EAAA,IAAI,WAAA,CAAY,MAAA,EAAQ,MAAA,EAAQ,KAAK,CAAA,EAAG;AACxC,EAAA,MAAM,OAAO,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,MAAM,IAAI,KAAK,CAAA,CAAA;AACzC,EAAA,IAAI,eAAA,CAAgB,IAAA,CAAK,CAAC,IAAA,KAAS,sBAAA,CAAuB,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE,CAAC,CAAA,EAAG;AAC/E,EAAA,IAAI,CAAC,oBAAA,CAAqB,CAAA,WAAA,EAAc,IAAI,EAAE,CAAA,EAAG;AACjD,EAAA,OAAA,CAAQ,IAAA;AAAA,IACN,CAAA,wBAAA,EAA2B,MAAM,CAAA,EAAA,EAAK,MAAM,IAAI,KAAK,CAAA,8TAAA;AAAA,GAMvD;AACF;;;ACbO,SAAS,OAAA,CAAQ,UAAyB,EAAA,EAAkC;AACjF,EAAA,MAAM,OAAA,GAAU,UAAU,QAAQ,CAAA;AAClC,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AACrB,EAAA,IAAI,SAAS,WAAA,EAAa;AACxB,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,SAAA,CAAU,EAAE,CAAA;AACpC,IAAA,OAAO,OAAA,KAAY,IAAA,GAAO,IAAA,GAAO,EAAE,QAAA,EAAU,SAAS,OAAA,EAAS,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,IAAA,EAAK;AAAA,EACzF;AACA,EAAA,MAAM,OAAO,QAAA,CAAS,WAAA;AACtB,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,KAAA,EAAO,IAAA,IAAQ,IAAA,EAAM,IAAA;AAC3C,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,GAAQ,IAAA,EAAM,OAAA,CAAQ,IAAI,EAAE,CAAA,GAAI,IAAA,EAAM,YAAA,CAAa,EAAE,CAAA;AAC5E,EAAA,IAAI,CAAC,QAAQ,CAAC,KAAA,IAAU,QAAQ,CAAC,WAAA,CAAY,KAAK,CAAA,EAAI,OAAO,IAAA;AAC7D,EAAA,OAAO,EAAE,QAAA,EAAU,OAAA,EAAS,SAAS,KAAA,CAAM,QAAA,EAAU,MAAM,KAAA,EAAM;AACnE;AAQO,SAAS,aACd,QAAA,EACA,MAAA,EACA,QACA,KAAA,EACA,EAAA,EACA,OAAO,IAAA,EACe;AACtB,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA;AAChC,EAAA,IAAI,KAAK,OAAO,GAAA;AAChB,EAAA,MAAM,QAAA,GAAW,oBAAA,CAAqB,MAAA,EAAQ,MAAA,EAAQ,KAAK,CAAA;AAC3D,EAAA,MAAM,SAAA,GAAY,QAAA,IAAY,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA;AAClD,EAAA,IAAI,WAAW,OAAO,SAAA;AACtB,EAAA,IAAI,IAAA,EAAM,gBAAA,CAAiB,QAAA,CAAS,QAAA,CAAS,QAAQ,EAAE,CAAA;AACvD,EAAA,OAAO,IAAA;AACT;AAUO,SAAS,mBAAA,CACd,QAAA,EACA,MAAA,EACA,MAAA,EACA,OACA,OAAA,EACsB;AACtB,EAAA,IAAI,CAAC,oBAAA,CAAqB,OAAO,CAAA,EAAG,OAAO,IAAA;AAC3C,EAAA,MAAM,OAAO,QAAA,CAAS,WAAA,GAAc,WAAW,oBAAA,CAAqB,MAAA,EAAQ,QAAQ,KAAK,CAAA;AACzF,EAAA,MAAM,OAAO,IAAA,EAAM,WAAA;AACnB,EAAA,MAAM,OAAA,GAAU,IAAA,IAAQ,SAAA,CAAU,IAAI,CAAA;AACtC,EAAA,MAAM,KAAA,GAAQ,IAAA,EAAM,cAAA,CAAe,OAAO,CAAA;AAC1C,EAAA,IAAI,CAAC,QAAQ,CAAC,IAAA,IAAQ,CAAC,OAAA,IAAW,CAAC,OAAO,OAAO,IAAA;AACjD,EAAA,OAAO,EAAE,QAAA,EAAU,IAAA,EAAM,OAAA,EAAS,OAAA,EAAS,MAAM,QAAA,EAAU,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,KAAA,EAAM;AACpF;;;AChDO,SAAS,kBAAA,GAA8B;AAC5C,EAAA,OAAO,OAAO,UAAA,KAAe,WAAA,IACxB,OAAQ,WAA+B,eAAA,KAAoB,UAAA;AAClE;AASA,IAAM,YAAA,GAAuD;AAAA,EAC3D,CAAC,cAAc,GAAG,CAAA;AAAA,EAAG,CAAC,cAAc,GAAG,CAAA;AAAA,EACvC,CAAC,aAAa,GAAG,CAAA;AAAA,EAAG,CAAC,aAAa,GAAG,CAAA;AAAA,EACrC,CAAC,cAAc,GAAG,CAAA;AAAA,EAAG,CAAC,cAAc,GAAG,CAAA;AAAA,EACvC,CAAC,YAAY,GAAG,CAAA;AAAA,EAAG,CAAC,YAAY,GAAG,CAAA;AAAA,EACnC,CAAC,aAAa,GAAG,CAAA;AAAA,EACjB,CAAC,SAAS,GAAG,CAAA;AAAA,EAAG,CAAC,SAAS,GAAG,CAAA;AAAA,EAC7B,CAAC,QAAQ,GAAG,CAAA;AAAA,EACZ,CAAC,UAAU,GAAG,CAAA;AAAA,EACd,CAAC,SAAS,GAAG,CAAA;AAAA,EACb,CAAC,QAAQ,GAAG,CAAA;AAAA,EAAG,CAAC,YAAY,GAAG,CAAA;AAAA,EAC/B,CAAC,QAAQ,GAAG,CAAA;AAAA,EAAG,CAAC,WAAW,GAAG,CAAA;AAAA,EAAG,CAAC,UAAU,GAAG;AACjD,CAAA;AAIA,SAAS,gBAAgB,KAAA,EAAuB;AAC9C,EAAA,OAAO,KAAA,CAAM,WAAA,EAAY,CAAE,OAAA,CAAQ,qCAAqC,IAAI,CAAA;AAC9E;AAQO,SAAS,eAAe,KAAA,EAAqD;AAClF,EAAA,MAAM,CAAA,GAAI,gBAAgB,KAAK,CAAA;AAC/B,EAAA,MAAM,SAAS,CAAA,CAAE,QAAA,CAAS,QAAQ,CAAA,IAAK,CAAA,CAAE,SAAS,SAAS,CAAA;AAC3D,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,IAAA,CAAK,CAAC,CAAC,IAAI,CAAA,KAAM,CAAA,CAAE,QAAA,CAAS,IAAI,CAAC,CAAA,GAAI,CAAC,CAAA,IAAK,GAAA;AACvE,EAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,MAAA,GAAS,WAAW,QAAA,EAAS;AACvD;AAOA,SAAS,oBAAoB,KAAA,EAAuB;AAClD,EAAA,MAAM,KAAA,GAAQ,gBAAgB,KAAK,CAAA,CAAE,MAAM,SAAS,CAAA,CAAE,OAAO,OAAO,CAAA;AACpE,EAAA,OAAO,MAAM,MAAA,CAAO,CAAC,MAAM,CAAA,KAAM,QAAA,IAAY,MAAM,SAAA,IAC9C,CAAC,YAAA,CAAa,IAAA,CAAK,CAAC,CAAC,IAAI,MAAM,IAAA,KAAS,CAAC,CAAC,CAAA,CAAE,MAAA;AACnD;AA2BA,eAAsB,uBAAA,CACpB,IAAA,GAAiC,EAAC,EACA;AAClC,EAAA,MAAM,QAAS,UAAA,CAA+B,eAAA;AAC9C,EAAA,IAAI,OAAO,UAAU,UAAA,EAAY;AAC/B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AAEA,EAAA,MAAM,SAAS,IAAA,CAAK,QAAA,GAAW,IAAI,GAAA,CAAI,IAAA,CAAK,QAAQ,CAAA,GAAI,IAAA;AACxD,EAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,IAAA,CAAK,UAAU,CAAA;AAKxC,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAyD;AAC5E,EAAA,KAAA,MAAW,QAAQ,IAAA,EAAM;AACvB,IAAA,IAAI,UAAU,CAAC,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA,EAAG;AACxC,IAAA,MAAM,EAAE,MAAA,EAAQ,KAAA,EAAM,GAAI,cAAA,CAAe,KAAK,KAAK,CAAA;AACnD,IAAA,MAAM,MAAM,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,CAAA,EAAI,MAAM,IAAI,KAAK,CAAA,CAAA;AAC7C,IAAA,MAAM,UAAA,GAAa,mBAAA,CAAoB,IAAA,CAAK,KAAK,CAAA;AACjD,IAAA,MAAM,IAAA,GAAO,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AAC3B,IAAA,IAAI,IAAA,IAAQ,IAAA,CAAK,UAAA,IAAc,UAAA,EAAY;AAC3C,IAAA,MAAA,CAAO,GAAA,CAAI,GAAA,EAAK,EAAE,IAAA,EAAM,YAAY,CAAA;AAAA,EACtC;AAEA,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAY;AACjC,EAAA,KAAA,MAAW,EAAE,IAAA,EAAK,IAAK,MAAA,CAAO,QAAO,EAAG;AACtC,IAAA,oBAAA,CAAqB,IAAA,CAAK,QAAQ,cAAA,CAAe,IAAA,CAAK,KAAK,CAAA,EAAG,MAAM,IAAA,CAAK,IAAA,EAAK,EAAG;AAAA;AAAA;AAAA,MAG/E,MAAA,EAAQ,oBAAA,CAAqB,IAAA,CAAK,cAAc,CAAA;AAAA;AAAA;AAAA,MAGhD,QAAQ,CAAA,MAAA,EAAS,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,cAAc,CAAC,CAAA,CAAA;AAAA,KACrD,CAAA;AACD,IAAA,QAAA,CAAS,GAAA,CAAI,KAAK,MAAM,CAAA;AAAA,EAC1B;AACA,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,CAAC,GAAG,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,aAAA,CAAc,CAAC,CAAC,CAAA;AAAA,IACzD,OAAO,MAAA,CAAO;AAAA,GAChB;AACF;;;AClJO,IAAM,eAAA,GAAkB;AAExB,IAAM,aAAA,GAAgB;AA4BtB,IAAM,mBAAA;AAAA;AAAA,EAAiC;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAe9C,IAAM,kBAAA,GAAqB,CAAA;AAG3B,IAAM,kBAAA,GAAqB,CAAC,SAAA,GAAY,UAAA,EAAY,YAAY,UAAU,CAAA;AASnE,SAAS,eAAA,CACd,MAAA,EAA4B,MAAA,EAAgB,MAAA,EAAgB,KAAA,EACrB;AACvC,EAAA,IAAI,MAAA,KAAW,UAAU,OAAO,kBAAA;AAChC,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,MAAA,EAAQ,MAAA,EAAQ,KAAK,CAAA,EAAG,IAAA;AAC7C,EAAA,IAAI,CAAC,MAAM,OAAO,MAAA;AAClB,EAAA,MAAM,KAAA,GAAQ,KAAK,aAAA,IAAiB,kBAAA;AACpC,EAAA,OAAO,CAAC,KAAK,MAAA,CAAO,MAAA,GAAS,OAAO,IAAA,CAAK,MAAA,CAAO,SAAS,KAAK,CAAA;AAChE;AAOO,IAAM,iBAAA,GAAoB;AAS1B,SAAS,mBAAA,CACd,MAAA,EAA4B,MAAA,EAAgB,MAAA,EAAgB,KAAA,EACxC;AACpB,EAAA,IAAI,MAAA,KAAW,QAAA,EAAU,OAAQ,iBAAA,GAAoB,UAAA,GAAc,SAAA;AACnE,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,MAAA,EAAQ,MAAA,EAAQ,KAAK,CAAA,EAAG,IAAA;AAC7C,EAAA,IAAI,CAAC,MAAM,OAAO,MAAA;AAClB,EAAA,OAAQ,iBAAA,IAAqB,IAAA,CAAK,aAAA,IAAiB,kBAAA,CAAA,GAAuB,KAAK,IAAA,CAAK,IAAA;AACtF","file":"index.js","sourcesContent":["import type { FontStyle } from '../fontStyle';\nimport type { FaceEmbedding } from './OutlineFace';\nimport { loadFontOutlines, outlineMetrics } from './outlineRegistry';\n\n/**\n * The embeddable sfnt and glyph map of a registered outline face, once its\n * bytes have loaded. `null` when nothing is registered for the variant, the\n * load failed, or the face's parser has no sfnt behind it.\n */\nexport async function fontEmbedding(\n family: string, weight: number, style: FontStyle,\n): Promise<FaceEmbedding | null> {\n if ((await loadFontOutlines(family, { weight, style })) !== 'ready') return null;\n return outlineMetrics(family, weight, style)?.embedding?.() ?? null;\n}\n","/** Unicode's `Default_Ignorable_Code_Point` ranges (DerivedCoreProperties.txt). */\nconst RANGES: ReadonlyArray<readonly [number, number]> = [\n [0x00ad, 0x00ad], [0x034f, 0x034f], [0x061c, 0x061c], [0x115f, 0x1160],\n [0x17b4, 0x17b5], [0x180b, 0x180f], [0x200b, 0x200f], [0x202a, 0x202e],\n [0x2060, 0x206f], [0x3164, 0x3164], [0xfe00, 0xfe0f], [0xfeff, 0xfeff],\n [0xffa0, 0xffa0], [0xfff0, 0xfff8], [0x1bca0, 0x1bca3], [0x1d173, 0x1d17a],\n [0xe0000, 0xe0fff],\n];\n\n/**\n * Whether `cp` is one a renderer shows as nothing by design: joiners,\n * variation selectors, zero-width spaces, bidi controls, the soft hyphen. A\n * face serving one as no advance and no ink has served it.\n */\nexport function isDefaultIgnorable(cp: number): boolean {\n return RANGES.some(([lo, hi]) => cp >= lo && cp <= hi);\n}\n","/**\n * One shape for the advances, kerning and baseline of whichever tier resolved\n * a face. A baked atlas and the canvas tier measure in their bake-size pixels\n * and a parsed face in ems; `size` is the divisor that takes any of them to\n * world units (`fontSize / size`), which is why an em-space face reports 1.\n */\nimport type { BmFont, BmFontChar } from './FontAtlas';\nimport { isDefaultIgnorable } from './defaultIgnorable';\nimport type { DynamicFace } from './dynamic/dynamicAtlas';\nimport type { OutlineFace } from './outline/OutlineFace';\nimport type { ResolveResult } from './registerFont';\n\n/** Where a face's advances, kerning and baseline come from. */\nexport interface GlyphMetrics {\n /** Units per em of everything else here. */\n readonly size: number;\n /** Line top to baseline in the glyph frame: what an atlas glyph's `yoffset`\n * is measured against. */\n readonly base: number;\n /** Baseline to the top of the face's line box: the ascent a browser sets\n * the face with, or `base` for a face that states none. */\n readonly ascent: number;\n /** Baseline to the bottom of the face's line box. Absent for a face that\n * states no ascent and descent, which hangs its baseline `base` below the\n * line top instead of centering the face in its line. */\n readonly descent?: number;\n /** Advance for `cp`, or null when the face has no glyph for it. On the\n * canvas tier this measures the glyph and queues its bake. */\n advanceOf(cp: number): number | null;\n kernOf(left: number, right: number): number;\n}\n\n/** A canvas face that cannot measure a code point reports no advance and no\n * ink for it, which is its only way to say it has no glyph, and exactly how\n * it serves a default-ignorable one. A glyph with ink has no width until its\n * bake lands, and says so with a negative page. */\nexport function usableGlyph(glyph: BmFontChar): boolean {\n return glyph.xadvance > 0 || glyph.width > 0 || glyph.page < 0 || isDefaultIgnorable(glyph.id);\n}\n\n/** Reads through to `font` rather than copying: the canvas tier measures a\n * face again in place when its web font lands. */\nfunction atlasMetrics(font: BmFont, advanceOf: (cp: number) => number | null): GlyphMetrics {\n const stated = (): { ascent: number; descent: number } | undefined => {\n const face = font.faceMetrics;\n return face?.ascent !== undefined && face.descent !== undefined\n ? face as { ascent: number; descent: number }\n : undefined;\n };\n return {\n size: font.info.size,\n get base() { return font.common.base; },\n get ascent() {\n const face = stated();\n return face ? face.ascent * font.info.size : font.common.base;\n },\n get descent() {\n const face = stated();\n return face && face.descent * font.info.size;\n },\n advanceOf,\n kernOf: (l, r) => font.kerningMap.get(l)?.get(r) ?? 0,\n };\n}\n\nfunction outlineMetricsOf(face: OutlineFace): GlyphMetrics {\n const descent = face.faceMetrics?.descent;\n return {\n size: 1,\n base: face.ascender,\n ascent: face.faceMetrics?.ascent ?? face.ascender,\n ...(descent !== undefined ? { descent } : {}),\n advanceOf: (cp) => face.advanceOf(cp),\n kernOf: (l, r) => face.kernOf(l, r),\n };\n}\n\nfunction canvasMetrics(face: DynamicFace): GlyphMetrics {\n return atlasMetrics(face.font, (cp) => {\n const glyph = face.requestGlyph(cp);\n return usableGlyph(glyph) ? glyph.xadvance : null;\n });\n}\n\nconst byFace = new WeakMap<object, GlyphMetrics>();\n\nfunction memo<F extends object>(face: F, build: (face: F) => GlyphMetrics): GlyphMetrics {\n let m = byFace.get(face);\n if (!m) {\n m = build(face);\n byFace.set(face, m);\n }\n return m;\n}\n\n/**\n * The metrics of whichever tier `result` resolved to, or `undefined` for a\n * miss, which has none. One object per face, so two results that landed on the\n * same face compare equal by identity.\n */\nexport function metricsOf(result: ResolveResult): GlyphMetrics | undefined {\n if (result.outlineFace) return memo(result.outlineFace, outlineMetricsOf);\n if (result.entry) {\n return memo(result.entry.font, (font) => atlasMetrics(font, (cp) => font.charMap.get(cp)?.xadvance ?? null));\n }\n if (result.dynamicFace) return memo(result.dynamicFace, canvasMetrics);\n return undefined;\n}\n","const EMOJI = /\\p{Emoji_Presentation}|\\p{Extended_Pictographic}\\uFE0F|\\uFE0F\\u20E3/u;\n\n/** The first code point that is an emoji by default (U+231A WATCH). */\nconst FIRST_DEFAULT_EMOJI = 0x231a;\n\n/**\n * Whether a grapheme cluster is drawn as an emoji rather than as text: it\n * holds a code point that is one by default, or one made so by U+FE0F, and no\n * U+FE0E asking for text.\n */\nexport function hasEmojiPresentation(cluster: string): boolean {\n if (cluster.length === 1 && cluster.charCodeAt(0) < FIRST_DEFAULT_EMOJI) return false;\n return EMOJI.test(cluster) && !cluster.includes('\\uFE0E');\n}\n","/**\n * Warnings about what the registered fonts cannot serve. Text is laid out per\n * frame, so each is claimed once through the fallback warning gate, which the\n * registry and fallback resets both clear.\n */\nimport { claimFallbackWarning, fallbackWarningClaimed } from './fallback';\nimport type { FontStyle } from './fontStyle';\nimport { fontPending } from './registerFont';\n\n/** Say, once per family and code point, that no tier has a glyph for it. */\nexport function warnMissingGlyph(family: string, cp: number): void {\n if (!claimFallbackWarning(`missing-glyph|${family}|${cp}`)) return;\n const ch = String.fromCodePoint(cp);\n console.warn(\n `weasel: no glyph for U+${cp.toString(16).toUpperCase().padStart(4, '0')} ` +\n `(${JSON.stringify(ch)}) in \"${family}\", and the dynamic tier could not ` +\n `rasterize it — skipping the character. Bake it into the atlas, or call ` +\n `registerCanvasFont(\"${family}\") to serve missing codepoints from ` +\n `installed fonts.`,\n );\n}\n\n/** The registry's own warnings that a face will draw nothing, by key prefix. */\nconst NOTHING_RENDERS = ['unusable-default', 'self-unusable-default', 'unregistered-default'];\n\n/**\n * Say, once per face, that text set in it has no metrics and lays out as\n * nothing. Silent while the face is still loading, which is late rather than\n * missing, and when the registry has already said why it cannot render.\n */\nexport function warnNoMetrics(family: string, weight: number, style: FontStyle): void {\n if (fontPending(family, weight, style)) return;\n const face = `${family}|${weight}|${style}`;\n if (NOTHING_RENDERS.some((kind) => fallbackWarningClaimed(`${kind}|${face}`))) return;\n if (!claimFallbackWarning(`no-metrics|${face}`)) return;\n console.warn(\n `weasel: no metrics for \"${family}\" ${weight}/${style} — neither ` +\n `an atlas nor an outline face resolved, so every run set in it lays out as ` +\n `nothing. Register it with registerFont / registerCanvasFont / ` +\n `registerFontOutlines. If you did, check for a duplicated @weasel-js/font in ` +\n `node_modules — its registry is module state, so a second copy is a second, ` +\n `empty one.`,\n );\n}\n","/**\n * Finding the glyph for one code point: in the tier a run resolved to, then\n * in the dynamic tier, then nowhere. A miss never draws a stand-in `?`, which\n * is indistinguishable from one the author wrote, and the BmFont atlas format\n * has no `.notdef` to draw.\n */\nimport type { BmFont, BmFontChar } from './FontAtlas';\nimport { hasEmojiPresentation } from './emojiCluster';\nimport type { FontStyle } from './fontStyle';\nimport { metricsOf, usableGlyph, type GlyphMetrics } from './glyphMetrics';\nimport { warnMissingGlyph } from './glyphWarnings';\nimport { resolveGlyphFallback, type ResolveResult } from './registerFont';\n\n/** A code point's glyph, and the tier that ended up serving it. */\nexport interface ResolvedGlyph {\n /** The tier serving the glyph. Not the run's own when the code point\n * escalated to the dynamic tier. */\n resolved: ResolveResult;\n /** That tier's metrics, which are what `advance` is measured in. */\n metrics: GlyphMetrics;\n advance: number;\n /** The atlas to sample and the glyph's record in it. Both null on the\n * outline tier, which has geometry instead. A canvas glyph's record gains\n * its atlas rect in place when its bake lands. */\n font: BmFont | null;\n glyph: BmFontChar | null;\n}\n\n/** The glyph for `cp` in the tier `resolved` landed on, or null when that\n * tier has none. */\nexport function glyphIn(resolved: ResolveResult, cp: number): ResolvedGlyph | null {\n const metrics = metricsOf(resolved);\n if (!metrics) return null;\n if (resolved.outlineFace) {\n const advance = metrics.advanceOf(cp);\n return advance === null ? null : { resolved, metrics, advance, font: null, glyph: null };\n }\n const face = resolved.dynamicFace;\n const font = resolved.entry?.font ?? face?.font;\n const glyph = resolved.entry ? font?.charMap.get(cp) : face?.requestGlyph(cp);\n if (!font || !glyph || (face && !usableGlyph(glyph))) return null;\n return { resolved, metrics, advance: glyph.xadvance, font, glyph };\n}\n\n/**\n * The glyph for `cp` in a run that asked for `(family, weight, style)` and\n * resolved to `resolved`: that tier's own, else the dynamic tier's under the\n * face the run asked for. Null when neither has it, with a warning naming the\n * code point unless `warn` is false.\n */\nexport function resolveGlyph(\n resolved: ResolveResult,\n family: string,\n weight: number,\n style: FontStyle,\n cp: number,\n warn = true,\n): ResolvedGlyph | null {\n const own = glyphIn(resolved, cp);\n if (own) return own;\n const fallback = resolveGlyphFallback(family, weight, style);\n const escalated = fallback && glyphIn(fallback, cp);\n if (escalated) return escalated;\n if (warn) warnMissingGlyph(resolved.resolved.family, cp);\n return null;\n}\n\n/**\n * The color glyph for a whole grapheme cluster with emoji presentation, in a\n * run that asked for `(family, weight, style)` and resolved to `resolved`.\n * The canvas tier is the only one that draws a cluster as one glyph or in its\n * own colors, so it serves these whatever tier serves the run's text. Null for\n * any other cluster, and where the canvas tier is not to be had or draws no\n * color for this one: the cluster is then laid out code point by code point.\n */\nexport function resolveColorCluster(\n resolved: ResolveResult,\n family: string,\n weight: number,\n style: FontStyle,\n cluster: string,\n): ResolvedGlyph | null {\n if (!hasEmojiPresentation(cluster)) return null;\n const tier = resolved.dynamicFace ? resolved : resolveGlyphFallback(family, weight, style);\n const face = tier?.dynamicFace;\n const metrics = tier && metricsOf(tier);\n const glyph = face?.requestCluster(cluster);\n if (!tier || !face || !metrics || !glyph) return null;\n return { resolved: tier, metrics, advance: glyph.xadvance, font: face.font, glyph };\n}\n","/**\n * Local Font Access → outline registrations.\n *\n * The dynamic canvas-SDF tier can render any installed family without ever\n * seeing its bytes: it asks canvas 2D to `fillText` and takes the pixels. The\n * outline tier cannot — it needs the actual font file — and the only web API\n * that hands one over is `window.queryLocalFonts()`, which is Chromium-only\n * and gated behind a permission prompt that requires a user gesture.\n *\n * So this is the one part of the tier a consumer has to *ask* for, from a\n * click. A refusal is ordinary: no registrations, and large text keeps\n * rendering from the SDF tier. Safari and Firefox have no equivalent API at\n * all, so \"denied\" is the permanent state for most of the web.\n *\n * Registration is eager but reading is lazy: every matching face gets a thunk\n * that calls `FontData.blob()` at first glyph request. A machine can have\n * hundreds of faces and a `.ttc` runs to tens of megabytes.\n */\n\nimport type { FontStyle } from '../fontStyle';\nimport { createOpenTypeParser } from './opentypeParser';\nimport { registerFontOutlines } from './outlineRegistry';\n\n/**\n * The slice of the Local Font Access API this module uses. `queryLocalFonts`\n * is not in TypeScript's DOM types.\n */\ninterface LocalFontData {\n postscriptName: string;\n fullName: string;\n family: string;\n style: string;\n blob(): Promise<Blob>;\n}\n\ntype LocalFontWindow = typeof globalThis & {\n queryLocalFonts?: (opts?: { postscriptNames?: string[] }) => Promise<LocalFontData[]>;\n};\n\n/** Is the Local Font Access API present at all? False on every non-Chromium\n * browser and in any non-secure context. */\nexport function canQueryLocalFonts(): boolean {\n return typeof globalThis !== 'undefined'\n && typeof (globalThis as LocalFontWindow).queryLocalFonts === 'function';\n}\n\n/**\n * CSS weights for the weight words that appear in a `style` string.\n *\n * Ordered longest-first where one name contains another (\"ExtraBold\" before\n * \"Bold\", \"DemiBold\" before \"Bold\"), because the match is a substring scan\n * and \"Bold\" would otherwise claim every heavier face for 700.\n */\nconst WEIGHT_WORDS: readonly (readonly [string, number])[] = [\n ['extrablack', 950], ['ultrablack', 950],\n ['extrabold', 800], ['ultrabold', 800],\n ['extralight', 200], ['ultralight', 200],\n ['semibold', 600], ['demibold', 600],\n ['semilight', 350],\n ['black', 900], ['heavy', 900],\n ['bold', 700],\n ['medium', 500],\n ['light', 300],\n ['thin', 100], ['hairline', 100],\n ['book', 400], ['regular', 400], ['normal', 400],\n];\n\n/** Lowercased, with a split weight name closed up: \"Demi Bold\" and\n * \"Semi-Bold\" read as the single words the table above lists. */\nfunction joinWeightWords(style: string): string {\n return style.toLowerCase().replace(/\\b(extra|ultra|semi|demi)[\\s_-]+/g, '$1');\n}\n\n/**\n * Parse a `FontData.style` string — \"Regular\", \"Bold Italic\", \"Condensed\n * ExtraLight Oblique\" — into the CSS variant the kit's registry is keyed by.\n *\n * Anything unrecognized lands on 400/normal.\n */\nexport function parseFontStyle(style: string): { weight: number; style: FontStyle } {\n const s = joinWeightWords(style);\n const italic = s.includes('italic') || s.includes('oblique');\n const weight = WEIGHT_WORDS.find(([word]) => s.includes(word))?.[1] ?? 400;\n return { weight, style: italic ? 'italic' : 'normal' };\n}\n\n/**\n * How many words of a `FontData.style` string are *not* weight or slant —\n * \"Condensed\", \"Display\", \"Poster\". A face with none of them is the plain\n * member of its slot, and the one to keep when two faces collide.\n */\nfunction styleQualifierCount(style: string): number {\n const words = joinWeightWords(style).split(/[\\s_-]+/).filter(Boolean);\n return words.filter((w) => w !== 'italic' && w !== 'oblique'\n && !WEIGHT_WORDS.some(([word]) => word === w)).length;\n}\n\n/** Options for `enableLocalFontOutlines`. */\nexport interface LocalFontOutlinesOptions {\n /**\n * Restrict registration to these families. Defaults to every installed\n * family, which can be hundreds of registry entries.\n */\n families?: readonly string[];\n}\n\n/** What `enableLocalFontOutlines` registered. */\nexport interface LocalFontOutlinesResult {\n /** Families that now have at least one outline face, in menu order. */\n families: readonly string[];\n /** Total faces registered across those families. */\n faces: number;\n}\n\n/**\n * Ask for permission to read installed fonts, and register outline sources\n * for the faces it returns. **Must be called from a user gesture** — the\n * permission prompt requires one, and without it the call rejects.\n *\n * Rejects when the API is missing or the user denies; neither leaves the\n * registry in a partial state.\n */\nexport async function enableLocalFontOutlines(\n opts: LocalFontOutlinesOptions = {},\n): Promise<LocalFontOutlinesResult> {\n const query = (globalThis as LocalFontWindow).queryLocalFonts;\n if (typeof query !== 'function') {\n throw new Error(\n 'weasel enableLocalFontOutlines: this browser has no Local Font Access API ' +\n '(queryLocalFonts). Large text keeps rendering from the SDF tier.',\n );\n }\n\n const wanted = opts.families ? new Set(opts.families) : null;\n const data = await query.call(globalThis);\n\n // Several installed faces can land on one (family, weight, style) slot —\n // \"Helvetica Neue Condensed Bold\" reduces to 700/normal exactly like\n // \"Helvetica Neue Bold\". Pick the least-qualified name for each slot.\n const chosen = new Map<string, { font: LocalFontData; qualifiers: number }>();\n for (const font of data) {\n if (wanted && !wanted.has(font.family)) continue;\n const { weight, style } = parseFontStyle(font.style);\n const key = `${font.family}|${weight}|${style}`;\n const qualifiers = styleQualifierCount(font.style);\n const prev = chosen.get(key);\n if (prev && prev.qualifiers <= qualifiers) continue;\n chosen.set(key, { font, qualifiers });\n }\n\n const families = new Set<string>();\n for (const { font } of chosen.values()) {\n registerFontOutlines(font.family, parseFontStyle(font.style), () => font.blob(), {\n // The PostScript name is how a member is picked out of a `.ttc`\n // collection, which is what most macOS system families ship as.\n parser: createOpenTypeParser(font.postscriptName),\n // The DOM already has every installed face; naming it avoids reading\n // the collection file a second time.\n cssSrc: `local(${JSON.stringify(font.postscriptName)})`,\n });\n families.add(font.family);\n }\n return {\n families: [...families].sort((a, b) => a.localeCompare(b)),\n faces: chosen.size,\n };\n}\n","/**\n * The GLSL that turns an atlas sample into glyph coverage, and the constants\n * naming which kind of atlas a sample came from.\n *\n * Not a program: glyphs stage into the renderer's batch alongside solid\n * geometry and image quads, and the batch shader pastes this in.\n *\n * MSDF channel layout: msdf-bmfont-xml writes R, G, and B as independent\n * signed-distance fields covering different edge directions, and the true\n * field is their median. The runtime canvas bake writes one channel that *is*\n * the field. Both encode the edge at 0.5, which is what lets one threshold\n * serve them.\n */\n\nimport type { FontStyle } from './fontStyle';\nimport { getFont } from './registerFont';\nimport { PAGE_SIZE, SDF_RADIUS } from './dynamic/dynamicAtlas';\nimport { BAKE_SIZE } from './dynamic/glyphRasterizer';\n\n/** `a_paintMode` value for glyphs off an MSDF atlas — the median of R,G,B. */\nexport const GLYPH_MODE_MSDF = 1;\n/** `a_paintMode` value for glyphs off a runtime canvas bake — `.r` alone. */\nexport const GLYPH_MODE_R8 = 2;\n\n/**\n * Glyph coverage from one atlas sample, as GLSL for a program to paste in.\n *\n * `mode` says which field the sample carries: `GLYPH_MODE_MSDF` takes the\n * median of R,G,B, `GLYPH_MODE_R8` reads `.r` alone.\n *\n * The antialiasing band has to be one *screen* pixel wide: `fieldPerUv` is how\n * far the field runs per unit of texture coordinate on each axis (see\n * `glyphFieldScale`), and the screen derivatives of `uv` turn that into field\n * units per screen pixel.\n *\n * Not `fwidth(field)`: that is the field's slope only where the field is a\n * straight ramp. Across a stem narrower than a pixel the two columns of a 2x2\n * quad sample either side of the ridge at equal values, the derivative reads\n * flat, and the stem is thresholded away. The derivative of `uv` is constant\n * across a glyph quad, so it cannot do that. Nor a constant band, which is\n * right at one scale only.\n *\n * **Nothing here branches, and the caller must not branch around it.**\n * A derivative in non-uniform control flow is undefined, so it has to\n * be taken before anything selects on paint mode. A merged program therefore\n * runs the glyph math on fragments that are not glyphs and discards the result.\n *\n * `synthBold` shifts the threshold to thicken strokes where the resolver fell\n * back from a missing bold variant to the regular atlas.\n */\nexport const GLYPH_COVERAGE_GLSL = /* glsl */ `\nfloat median(float r, float g, float b) {\n return max(min(r, g), min(max(r, g), b));\n}\n\nfloat glyphCoverage(vec4 texel, float mode, float synthBold, vec2 uv, vec2 fieldPerUv) {\n float field = mix(median(texel.r, texel.g, texel.b), texel.r, step(1.5, mode));\n float fieldPerPx = 0.5 * (length(dFdx(uv) * fieldPerUv) + length(dFdy(uv) * fieldPerUv));\n float aaW = max(0.5 * fieldPerPx, 0.0005);\n float threshold = 0.5 - synthBold;\n return smoothstep(threshold - aaW, threshold + aaW, field);\n}\n`;\n\n/** msdf-bmfont-xml's default `distanceRange`, for an atlas that records none. */\nconst DEFAULT_MSDF_RANGE = 4;\n\n/** A runtime canvas page: `PAGE_SIZE` texels, field 0 to 1 over `SDF_RADIUS` of them. */\nconst CANVAS_FIELD_SCALE = [PAGE_SIZE / SDF_RADIUS, PAGE_SIZE / SDF_RADIUS] as const;\n\n/**\n * Field units per unit of texture coordinate along u and v for the atlas a\n * glyph group samples — the `fieldPerUv` that `glyphCoverage` takes. A baked\n * MSDF atlas answers from its page size and `distanceRange`, a runtime canvas\n * page from the dynamic tier's page size and bake radius. `undefined` for an\n * atlas that is not registered.\n */\nexport function glyphFieldScale(\n source: 'atlas' | 'canvas', family: string, weight: number, style: FontStyle,\n): readonly [number, number] | undefined {\n if (source === 'canvas') return CANVAS_FIELD_SCALE;\n const font = getFont(family, weight, style)?.font;\n if (!font) return undefined;\n const range = font.distanceRange ?? DEFAULT_MSDF_RANGE;\n return [font.common.scaleW / range, font.common.scaleH / range];\n}\n\n/**\n * How far the field threshold drops for a glyph faked bold from a lighter\n * face. Tuned on Inter: it thickens strokes about a pixel at 16px without\n * breaking glyph topology.\n */\nexport const SYNTH_BOLD_AMOUNT = 0.08;\n\n/**\n * How far synthetic bold moves each edge of a glyph outward, in em, for the\n * atlas that serves it: `SYNTH_BOLD_AMOUNT` of the field's span, which is\n * `distanceRange` texels in a baked MSDF atlas and `SDF_RADIUS` in a runtime\n * canvas page, over the atlas's texels per em. `undefined` for an atlas that\n * is not registered.\n */\nexport function syntheticBoldGrowth(\n source: 'atlas' | 'canvas', family: string, weight: number, style: FontStyle,\n): number | undefined {\n if (source === 'canvas') return (SYNTH_BOLD_AMOUNT * SDF_RADIUS) / BAKE_SIZE;\n const font = getFont(family, weight, style)?.font;\n if (!font) return undefined;\n return (SYNTH_BOLD_AMOUNT * (font.distanceRange ?? DEFAULT_MSDF_RANGE)) / font.info.size;\n}\n"]}
@@ -0,0 +1,14 @@
1
+ export { a as BmFont, B as BmFontChar, b as BmFontCommon, c as BmFontInfo, d as BmFontKerning, e as FIXTURE_FONT, f as FaceMetricTables, g as FaceMetrics, h as FaceRuleMetrics, i as FaceScriptMetrics, F as FontStyle, j as faceMetricsFromTables, k as faceMetricsOf, p as parseBmFont, v as verticalMetricsFromTables } from './fontStyle-D1y21YZ9.js';
2
+
3
+ /**
4
+ * `family` as it must appear in a CSS `font` shorthand or `font-family`.
5
+ *
6
+ * A generic keyword has to stay bare: quoted, `"sans-serif"` names a font
7
+ * called sans-serif, which no machine has, and the browser falls back to its
8
+ * default serif. A single named family is quoted so a name like `Font 3D`
9
+ * still parses. Anything already holding a comma or a quote is a CSS family
10
+ * list the caller wrote, and passes through as written.
11
+ */
12
+ declare function cssFamilyName(family: string): string;
13
+
14
+ export { cssFamilyName };
@@ -0,0 +1,3 @@
1
+ export { FIXTURE_FONT, cssFamilyName, faceMetricsFromTables, faceMetricsOf, parseBmFont, verticalMetricsFromTables } from './chunk-XQPRAGHF.js';
2
+ //# sourceMappingURL=metrics.js.map
3
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"metrics.js"}
@@ -1,14 +1,5 @@
1
1
  import { Reflection } from '@weasel-js/registry';
2
-
3
- /**
4
- * The style axis of a font variant, one declaration for the whole stack.
5
- *
6
- * Written inline as `'normal' | 'italic'` in half a dozen places before this,
7
- * across the font/text package line — identical value sets, so nothing could
8
- * drift until someone added `'oblique'`, and then it would have drifted
9
- * silently in every place that had not been found.
10
- */
11
- type FontStyle = 'normal' | 'italic';
2
+ import { F as FontStyle, a as BmFont, B as BmFontChar, g as FaceMetrics$1 } from './fontStyle-D1y21YZ9.js';
12
3
 
13
4
  /**
14
5
  * The only thing the glyph tier needs from a GL texture cache. Core's
@@ -37,10 +28,13 @@ interface GlyphTextureSink {
37
28
 
38
29
  /**
39
30
  * The dynamic glyph atlas's pages: their CPU-side bytes, and bringing each
40
- * renderer's texture of one up to date. Two sets: distance fields, one byte a
41
- * texel, and color glyphs, four.
31
+ * renderer's texture of one up to date. Three sets: distance fields, one byte a
32
+ * texel; color glyphs, four; and color glyphs baked again larger, four, on
33
+ * pages that fill and empty on their own.
42
34
  */
43
35
 
36
+ /** Side of every dynamic-atlas page, in texels: a glyph's rect over it is its uv. */
37
+ declare const PAGE_SIZE = 1024;
44
38
  interface DynamicPage {
45
39
  data: Uint8Array;
46
40
  /** Bumps on every write; each write appends a patch with seq = version. */
@@ -57,12 +51,16 @@ interface DynamicPage {
57
51
  declare function dynamicPageTextureId(page: number): string;
58
52
  /** Texture-cache key for a page of color glyphs. */
59
53
  declare function colorPageTextureId(page: number): string;
54
+ /** Texture-cache key for a page of color glyphs baked larger. */
55
+ declare function sharpColorPageTextureId(page: number): string;
60
56
  /** Bring `cache`'s copy of field page `pageIndex` up to date: full R8 upload
61
57
  * the first time, `texSubImage2D` patches after. Returns false if the page
62
58
  * doesn't exist yet. */
63
59
  declare function syncDynamicPageTexture(cache: GlyphTextureSink, pageIndex: number): boolean;
64
60
  /** The same for a page of color glyphs, whose texels are premultiplied RGBA. */
65
61
  declare function syncColorPageTexture(cache: GlyphTextureSink, pageIndex: number): boolean;
62
+ /** The same for a page of color glyphs baked larger. */
63
+ declare function syncSharpColorPageTexture(cache: GlyphTextureSink, pageIndex: number): boolean;
66
64
  /** @internal test seam — inspect CPU-side field pages. */
67
65
  declare function _getPagesForTests(): readonly DynamicPage[];
68
66
 
@@ -92,7 +90,7 @@ declare function _getPagesForTests(): readonly DynamicPage[];
92
90
  * can encode, so more taps, a bigger bake, or mipmaps buy nothing.
93
91
  */
94
92
 
95
- interface FaceMetrics$1 {
93
+ interface FaceMetrics {
96
94
  ascent: number;
97
95
  descent: number;
98
96
  }
@@ -115,11 +113,12 @@ interface RasterizedColorGlyph {
115
113
  advance: number;
116
114
  }
117
115
  interface GlyphRasterizer {
118
- faceMetrics(family: string, weight: number, style: FontStyle): FaceMetrics$1;
116
+ faceMetrics(family: string, weight: number, style: FontStyle): FaceMetrics;
119
117
  rasterize(family: string, weight: number, style: FontStyle, codepoint: number): RasterizedGlyph;
120
118
  /** `text`, one grapheme cluster, as the color glyph the platform draws for
121
- * it, or null when it draws none: no ink, or ink in the fill color alone. */
122
- rasterizeColor?(family: string, weight: number, style: FontStyle, text: string): RasterizedColorGlyph | null;
119
+ * it, or null when it draws none: no ink, or ink in the fill color alone.
120
+ * Drawn at `size` px, `BAKE_SIZE` when omitted, and measured in its px. */
121
+ rasterizeColor?(family: string, weight: number, style: FontStyle, text: string, size?: number): RasterizedColorGlyph | null;
123
122
  }
124
123
 
125
124
  /**
@@ -139,7 +138,8 @@ interface GlyphRasterizer {
139
138
  *
140
139
  * An emoji is the one glyph kept as the canvas drew it: `requestCluster`
141
140
  * rasterizes a whole grapheme cluster in its own colors onto a second set of
142
- * pages, RGBA, that fill and empty with the field pages.
141
+ * pages, RGBA, that fill and empty with the field pages, and bakes it again
142
+ * larger for drawing past its size (`sharpColor.ts`).
143
143
  */
144
144
 
145
145
  /** A face whose glyphs are rasterized on demand into a shared atlas page, rather than shipped as a prebuilt one. */
@@ -247,7 +247,7 @@ interface OutlineFace {
247
247
  * `faceMetricsFromTables` so they match an atlas baked from the same
248
248
  * font. Optional: a custom parser that omits it leaves layout on its
249
249
  * defaults. */
250
- faceMetrics?: FaceMetrics;
250
+ faceMetrics?: FaceMetrics$1;
251
251
  /**
252
252
  * Em-space SVG path data for `cp`, or `null` when this face has no glyph
253
253
  * for the codepoint *or* the glyph has no contours (a space).
@@ -261,6 +261,36 @@ interface OutlineFace {
261
261
  /** Kerning adjustment between two codepoints, in em units — negative pulls
262
262
  * them together. `0` when the face kerns neither this pair nor at all. */
263
263
  kernOf(left: number, right: number): number;
264
+ /** The face's raw material for embedding it whole in another format (a PDF).
265
+ * Optional: a custom parser that has no sfnt behind it omits it, and so
266
+ * does the built-in one for a WOFF file. */
267
+ embedding?(): FaceEmbedding;
268
+ }
269
+ /** What a document format needs to embed one face as a font program. */
270
+ interface FaceEmbedding {
271
+ /** One face's sfnt: a collection member is cut out of its collection. */
272
+ sfnt: ArrayBuffer;
273
+ postScriptName: string;
274
+ unitsPerEm: number;
275
+ /** `'cff'` when the face has a `CFF ` table and no `glyf`. */
276
+ outlines: 'truetype' | 'cff';
277
+ /** False when OS/2 fsType forbids embedding (bit 1, 0x0002) or allows
278
+ * bitmaps only (bit 9, 0x0200). */
279
+ embeddable: boolean;
280
+ /** Glyph index for a codepoint; `null` for `.notdef` (0). */
281
+ glyphIndexOf(cp: number): number | null;
282
+ /** Advance of a glyph, in font units. */
283
+ advanceOfGlyph(gid: number): number;
284
+ /** Font units. */
285
+ ascent: number;
286
+ /** Font units, negative below the baseline. */
287
+ descent: number;
288
+ /** Font units. */
289
+ capHeight: number;
290
+ /** Degrees counterclockwise from vertical; negative leans right. */
291
+ italicAngle: number;
292
+ /** `[xMin, yMin, xMax, yMax]` in font units. */
293
+ bbox: readonly [number, number, number, number];
264
294
  }
265
295
  /**
266
296
  * Turn font file bytes into a face. May be async: the default implementation
@@ -299,7 +329,7 @@ declare function getFont(family: string, weight?: number, style?: FontStyle): Fo
299
329
  * An idle outline face starts loading, as it would for layout.
300
330
  * A run already resolved for layout reads `faceMetricsOf(result)` instead.
301
331
  */
302
- declare function faceMetricsFor(family: string, weight?: number, style?: FontStyle): FaceMetrics | undefined;
332
+ declare function faceMetricsFor(family: string, weight?: number, style?: FontStyle): FaceMetrics$1 | undefined;
303
333
  /** One family in the registry and the variants registered for it — what a
304
334
  * font picker can honestly offer. */
305
335
  interface RegisteredFont {
@@ -456,204 +486,6 @@ declare function resolveFontVariant(family: string, weight: number, style: FontS
456
486
  */
457
487
  declare function resolveGlyphFallback(family: string, weight: number, style: FontStyle): ResolveResult | null;
458
488
 
459
- /**
460
- * The line, decoration and script metrics a font states for itself, in the
461
- * kit's units: em fractions, with a rule's `offset` measured *down* from the
462
- * baseline to its top edge (y grows down) and a script's `shift` positive
463
- * for a rise.
464
- *
465
- * Both glyph tiers derive the record through {@link faceMetricsFromTables},
466
- * so one font yields one set of numbers whichever tier serves a run.
467
- */
468
-
469
- /** One rule: the top edge's distance below the baseline, and its weight. */
470
- interface FaceRuleMetrics {
471
- readonly offset: number;
472
- readonly thickness: number;
473
- }
474
- /** One script preset: `size` scales the inherited font size, `shift` raises
475
- * (positive) or lowers (negative) the baseline, in inherited ems. */
476
- interface FaceScriptMetrics {
477
- readonly size: number;
478
- readonly shift: number;
479
- }
480
- /** What a face says about its line, rules, scripts and letter heights.
481
- * Every field is optional: a font may carry some of these tables and not
482
- * others. */
483
- interface FaceMetrics {
484
- /** Baseline to the top of the face's line box, in ems — the ascent a
485
- * browser sets this face with. See {@link verticalMetricsFromTables}. */
486
- readonly ascent?: number;
487
- /** Baseline to the bottom of the face's line box, in ems, positive
488
- * downward. Present exactly when `ascent` is. */
489
- readonly descent?: number;
490
- readonly underline?: FaceRuleMetrics;
491
- readonly strikethrough?: FaceRuleMetrics;
492
- readonly superscript?: FaceScriptMetrics;
493
- readonly subscript?: FaceScriptMetrics;
494
- /** Height of a flat lowercase letter above the baseline, in ems
495
- * (`OS/2.sxHeight`). */
496
- readonly xHeight?: number;
497
- /** Height of a flat capital above the baseline, in ems
498
- * (`OS/2.sCapHeight`). */
499
- readonly capHeight?: number;
500
- }
501
- /** The table fields {@link faceMetricsFromTables} reads, in font units, as
502
- * the OpenType `post` and `OS/2` tables state them. */
503
- interface FaceMetricTables {
504
- unitsPerEm: number;
505
- hhea?: {
506
- ascender?: number;
507
- descender?: number;
508
- };
509
- post?: {
510
- underlinePosition?: number;
511
- underlineThickness?: number;
512
- };
513
- os2?: {
514
- fsSelection?: number;
515
- sTypoAscender?: number;
516
- sTypoDescender?: number;
517
- usWinAscent?: number;
518
- usWinDescent?: number;
519
- yStrikeoutPosition?: number;
520
- yStrikeoutSize?: number;
521
- ySuperscriptYSize?: number;
522
- ySuperscriptYOffset?: number;
523
- ySubscriptYSize?: number;
524
- ySubscriptYOffset?: number;
525
- sxHeight?: number;
526
- sCapHeight?: number;
527
- };
528
- }
529
- /**
530
- * The ascent and descent a browser sets this face with, in ems, descent
531
- * positive downward.
532
- *
533
- * The OpenType rule: `OS/2` `sTypoAscender`/`sTypoDescender` when the font
534
- * sets `USE_TYPO_METRICS`, otherwise `hhea`. Firefox, and every engine on
535
- * Linux, follow it; Chromium and WebKit on macOS read `hhea` even when the
536
- * flag is set, and the DOM edit overlay's measured baseline correction
537
- * absorbs the difference there.
538
- *
539
- * Falls back to the typo pair, then the win pair, when `hhea` is absent or
540
- * zero.
541
- */
542
- declare function verticalMetricsFromTables(t: FaceMetricTables): {
543
- ascent: number;
544
- descent: number;
545
- } | undefined;
546
- /**
547
- * Convert raw table values to {@link FaceMetrics}.
548
- *
549
- * `post.underlinePosition` and `OS/2.yStrikeoutPosition` are the rule's top
550
- * edge, positive above the baseline. `ySubscriptYOffset` is positive
551
- * *downward*, unlike its superscript twin. A zero weight or size is a font
552
- * that left the field blank, so that entry is dropped and the caller's
553
- * default applies.
554
- */
555
- declare function faceMetricsFromTables(t: FaceMetricTables): FaceMetrics | undefined;
556
- /**
557
- * The face metrics behind a resolved run — the outline face's on the outline
558
- * tier, the atlas's on the atlas tier. The canvas tier has none, so callers
559
- * fall back to their defaults.
560
- */
561
- declare function faceMetricsOf(result: ResolveResult): FaceMetrics | undefined;
562
-
563
- /**
564
- * BmFont JSON types and parser for the MSDF atlas format produced by
565
- * msdf-bmfont-xml. The parser builds accelerator maps (charMap, kerningMap)
566
- * for O(1) glyph and kerning lookup during layout.
567
- */
568
-
569
- /** The face an atlas was generated from, and the size in pixels it was generated at. */
570
- interface BmFontInfo {
571
- face: string;
572
- size: number;
573
- }
574
- /** Font-wide vertical metrics and the atlas page dimensions, in pixels. */
575
- interface BmFontCommon {
576
- lineHeight: number;
577
- base: number;
578
- scaleW: number;
579
- scaleH: number;
580
- }
581
- /** One glyph: where it sits in the atlas page (`x`/`y`/`width`/`height`),
582
- * where to draw it relative to the pen (`xoffset`/`yoffset`), and how far the
583
- * pen then advances. */
584
- interface BmFontChar {
585
- id: number;
586
- x: number;
587
- y: number;
588
- width: number;
589
- height: number;
590
- xoffset: number;
591
- yoffset: number;
592
- xadvance: number;
593
- page: number;
594
- /** Set on a glyph whose atlas rect holds its own colors, on a page of the
595
- * dynamic tier's color set: an emoji. It is painted as it is, untinted. */
596
- color?: true;
597
- }
598
- /** A kerning pair: `amount` px to add to the advance when `second` follows
599
- * `first`. Codepoints, not glyph indices. */
600
- interface BmFontKerning {
601
- first: number;
602
- second: number;
603
- amount: number;
604
- }
605
- /** A parsed MSDF atlas: the BmFont record plus the `charMap` / `kerningMap`
606
- * accelerators layout reads per glyph. */
607
- interface BmFont {
608
- info: BmFontInfo;
609
- common: BmFontCommon;
610
- chars: BmFontChar[];
611
- kernings: BmFontKerning[];
612
- charMap: Map<number, BmFontChar>;
613
- kerningMap: Map<number, Map<number, number>>;
614
- /** The font's own decoration and script metrics, when the bake recorded
615
- * them. Not part of the BMFont format: `gen-font` adds it as a top-level
616
- * `faceMetrics` block, and an atlas without one leaves layout on its
617
- * defaults. */
618
- faceMetrics?: FaceMetrics;
619
- /** Atlas texels over which the distance field runs from 0 to 1, edge at
620
- * 0.5 — msdf-bmfont-xml's `distanceField.distanceRange`. Absent when the
621
- * file does not record it. */
622
- distanceRange?: number;
623
- }
624
- /** Two-glyph fixture for unit tests. */
625
- declare const FIXTURE_FONT: {
626
- info: {
627
- face: string;
628
- size: number;
629
- };
630
- common: {
631
- lineHeight: number;
632
- base: number;
633
- scaleW: number;
634
- scaleH: number;
635
- };
636
- chars: {
637
- id: number;
638
- x: number;
639
- y: number;
640
- width: number;
641
- height: number;
642
- xoffset: number;
643
- yoffset: number;
644
- xadvance: number;
645
- page: number;
646
- }[];
647
- kernings: {
648
- first: number;
649
- second: number;
650
- amount: number;
651
- }[];
652
- };
653
- /** Validate a BmFont JSON document and build its lookup maps. Throws with the
654
- * missing or malformed field named. */
655
- declare function parseBmFont(raw: unknown): BmFont;
656
-
657
489
  /**
658
490
  * Cross-family fallback policy. The per-family chain in `resolveFontVariant`
659
491
  * (weight → style → synthetic) has always been rich; what was missing is what
@@ -858,4 +690,4 @@ declare function loadFontOutlines(family: string, variant?: OutlineVariant): Pro
858
690
  /** @internal Test seam — registry and warn-once keys are module state. */
859
691
  declare function _resetFontOutlinesForTests(): void;
860
692
 
861
- export { outlineMetrics as $, ensureFontTexture as A, type BmFont as B, type CanvasFontEntry as C, type DynamicFace as D, faceMetricsFor as E, type FontStyle as F, type GlyphTextureSink as G, faceMetricsFromTables as H, faceMetricsOf as I, fontOutlineRegistry as J, fontPending as K, fontRegistry as L, getDefaultFontFamily as M, getFont as N, type OutlineFace as O, getFontFallbackPolicy as P, glyphOutline as Q, type ResolveResult as R, hasFontOutlines as S, type TexSource as T, isCanvasFont as U, listCanvasFonts as V, listFontOutlines as W, listFontWeights as X, listFonts as Y, loadFontOutlines as Z, markAllFontsNotUploaded as _, type BmFontChar as a, outlineStatus as a0, parseBmFont as a1, refreshCanvasFont as a2, registerCanvasFont as a3, registerFont as a4, registerFontOutlines as a5, resolveFontVariant as a6, resolveGlyphFallback as a7, setDefaultFontFamily as a8, setFontFallbackPolicy as a9, syncColorPageTexture as aa, syncDynamicPageTexture as ab, textureCacheKey as ac, unregisterCanvasFont as ad, unregisterFontOutlines as ae, verticalMetricsFromTables as af, warmFonts as ag, __setGlyphRasterizerForTests as ah, _getPagesForTests as ai, _resetDomFacesForTests as aj, _resetDynamicFontsForTests as ak, _resetFallbackForTests as al, _resetFontOutlinesForTests as am, _resetFontRegistryForTests as an, type BmFontCommon as b, type BmFontInfo as c, type BmFontKerning as d, FIXTURE_FONT as e, type FaceMetricTables as f, type FaceMetrics as g, type FaceRuleMetrics as h, type FaceScriptMetrics as i, type FontEntry as j, type FontFallbackPolicy as k, type FontFamilyFaces as l, type FontRequest as m, type FontVariant as n, type OutlineFaceInfo as o, type OutlineFontOptions as p, type OutlineParser as q, type OutlineSource as r, type OutlineStatus as s, type OutlineVariant as t, type RegisterFontOptions as u, type RegisteredFont as v, colorPageTextureId as w, cssFontFamily as x, cssFontFamilyLoading as y, dynamicPageTextureId as z };
693
+ export { syncColorPageTexture as $, hasFontOutlines as A, isCanvasFont as B, type CanvasFontEntry as C, type DynamicFace as D, listCanvasFonts as E, type FaceEmbedding as F, type GlyphTextureSink as G, listFontOutlines as H, listFontWeights as I, listFonts as J, loadFontOutlines as K, markAllFontsNotUploaded as L, outlineMetrics as M, outlineStatus as N, type OutlineFace as O, PAGE_SIZE as P, refreshCanvasFont as Q, type ResolveResult as R, registerCanvasFont as S, type TexSource as T, registerFont as U, registerFontOutlines as V, resolveFontVariant as W, resolveGlyphFallback as X, setDefaultFontFamily as Y, setFontFallbackPolicy as Z, sharpColorPageTextureId as _, type FontEntry as a, syncDynamicPageTexture as a0, syncSharpColorPageTexture as a1, textureCacheKey as a2, unregisterCanvasFont as a3, unregisterFontOutlines as a4, warmFonts as a5, __setGlyphRasterizerForTests as a6, _getPagesForTests as a7, _resetDomFacesForTests as a8, _resetDynamicFontsForTests as a9, _resetFallbackForTests as aa, _resetFontOutlinesForTests as ab, _resetFontRegistryForTests as ac, type FontFallbackPolicy as b, type FontFamilyFaces as c, type FontRequest as d, type FontVariant as e, type OutlineFaceInfo as f, type OutlineFontOptions as g, type OutlineParser as h, type OutlineSource as i, type OutlineStatus as j, type OutlineVariant as k, type RegisterFontOptions as l, type RegisteredFont as m, colorPageTextureId as n, cssFontFamily as o, cssFontFamilyLoading as p, dynamicPageTextureId as q, ensureFontTexture as r, faceMetricsFor as s, fontOutlineRegistry as t, fontPending as u, fontRegistry as v, getDefaultFontFamily as w, getFont as x, getFontFallbackPolicy as y, glyphOutline as z };
@@ -1,2 +1,3 @@
1
- export { ah as __setGlyphRasterizerForTests, ai as _getPagesForTests, aj as _resetDomFacesForTests, ak as _resetDynamicFontsForTests, al as _resetFallbackForTests, am as _resetFontOutlinesForTests, an as _resetFontRegistryForTests } from './test-seams-BvULFsbo.js';
1
+ export { a6 as __setGlyphRasterizerForTests, a7 as _getPagesForTests, a8 as _resetDomFacesForTests, a9 as _resetDynamicFontsForTests, aa as _resetFallbackForTests, ab as _resetFontOutlinesForTests, ac as _resetFontRegistryForTests } from './test-seams-CjRs8Yvp.js';
2
2
  import '@weasel-js/registry';
3
+ import './fontStyle-D1y21YZ9.js';
@@ -1,3 +1,4 @@
1
- export { __setGlyphRasterizerForTests, _getPagesForTests, _resetDomFacesForTests, _resetDynamicFontsForTests, _resetFallbackForTests, _resetFontOutlinesForTests, _resetFontRegistryForTests } from './chunk-NM6K6M4Y.js';
1
+ export { __setGlyphRasterizerForTests, _getPagesForTests, _resetDomFacesForTests, _resetDynamicFontsForTests, _resetFallbackForTests, _resetFontOutlinesForTests, _resetFontRegistryForTests } from './chunk-GQJL6VVQ.js';
2
+ import './chunk-XQPRAGHF.js';
2
3
  //# sourceMappingURL=test-seams.js.map
3
4
  //# sourceMappingURL=test-seams.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weasel-js/font",
3
- "version": "1.10.1",
3
+ "version": "1.11.1",
4
4
  "description": "MSDF font atlases, glyph metrics, and runtime glyph rasterization for @weasel-js/core. Registry, kerning-aware glyph layout, and the SDF text shader source.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -13,6 +13,10 @@
13
13
  "import": "./dist/index.js",
14
14
  "types": "./dist/index.d.ts"
15
15
  },
16
+ "./metrics": {
17
+ "import": "./dist/metrics.js",
18
+ "types": "./dist/metrics.d.ts"
19
+ },
16
20
  "./test-seams": {
17
21
  "import": "./dist/test-seams.js",
18
22
  "types": "./dist/test-seams.d.ts"
@@ -46,7 +50,7 @@
46
50
  "provenance": true
47
51
  },
48
52
  "dependencies": {
49
- "@weasel-js/registry": "1.10.1",
53
+ "@weasel-js/registry": "1.11.1",
50
54
  "opentype.js": "2.0.0"
51
55
  }
52
56
  }