reamkit 1.34.0 → 1.35.0

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.
@@ -1,8 +1,9 @@
1
- import { resolveFamilyStyle } from "../core/fonts/remote-fonts.js";
1
+ import { companionOf, companionScale, resolveFamilyStyle } from "../core/fonts/remote-fonts.js";
2
2
  import { fontSlotOf, scriptForCodepoint } from "../core/fonts/scripts.js";
3
3
  import { hasSubstitutable, substituteLetters } from "../core/font/ligatures.js";
4
4
  import { hasIndic, syllableStarts } from "../core/font/indic-shaper.js";
5
- import { createFontMeasure, shapeGlyphs } from "../core/font/measure.js";
5
+ import { hasCombiningMark, isCombiningMark } from "../core/font/mark-shaper.js";
6
+ import { createFontMeasure, shapeGlyphs, sharedGlyphCodes } from "../core/font/measure.js";
6
7
  import "../core/font/index.js";
7
8
  import { pageItemBounds } from "./page-doc.js";
8
9
  import { halfPtToPt, pt } from "../core/ir/units.js";
@@ -2916,9 +2917,20 @@ function runFontKeyAndParsed(options, ascii, bold, italic) {
2916
2917
  * @param italic …and whether it is italic.
2917
2918
  * @returns The face to draw it with, or `undefined` when nobody has it.
2918
2919
  */
2919
- function fallbackFaceKey(options, cp, bold, italic) {
2920
+ function fallbackFaceKey(options, cp, bold, italic, own) {
2920
2921
  const byFamily = options.registriesByFamily;
2921
2922
  if (!byFamily || byFamily.size === 0) return void 0;
2923
+ const companion = own === void 0 ? void 0 : companionOf(own);
2924
+ const borrowed = companion ? byFamily.get(companion.family) : void 0;
2925
+ if (companion && borrowed) {
2926
+ const { variant, parsed } = borrowed.resolveByStyle(bold, italic);
2927
+ const widthScale = companionScale(companion, cp, variant);
2928
+ if (widthScale !== void 0 && parsed.glyphForCodepoint(cp) !== 0) return {
2929
+ fontKey: `${companion.family}:${variant}`,
2930
+ parsed,
2931
+ widthScale
2932
+ };
2933
+ }
2922
2934
  const hanFace = byFamily.has("jp") ? "jp" : byFamily.has("kr") ? "kr" : "sc";
2923
2935
  const script = scriptForCodepoint(cp, hanFace);
2924
2936
  const order = script ? [script] : [];
@@ -2955,17 +2967,60 @@ function fallbackFaceKey(options, cp, bold, italic) {
2955
2967
  * @returns The text to set, which is `text` itself unless something was undrawable.
2956
2968
  */
2957
2969
  function drawableText(faces, text) {
2958
- if (!hasSubstitutable(text)) return text;
2959
- let out = "";
2960
- for (const ch of text) {
2961
- const cp = ch.codePointAt(0);
2962
- const letters = substituteLetters(cp);
2963
- const drawable = letters === void 0 || faces(cp).parsed.glyphForCodepoint(cp) !== 0;
2964
- out += drawable ? ch : letters;
2970
+ if (!hasSubstitutable(text) && !hasCombiningMark(text)) return text;
2971
+ return drawnPieces(faces, text).join("");
2972
+ }
2973
+ /**
2974
+ * What is drawn for each character of `text` — itself, the letters of a
2975
+ * ligature nobody can draw (see {@link drawableText}), a composed letter in
2976
+ * place of a letter and the marks written apart after it, and nothing for those
2977
+ * marks.
2978
+ *
2979
+ * A letter of the Latin, Greek or Cyrillic alphabets followed by combining
2980
+ * marks is set as the letter that composes them (Unicode canonical
2981
+ * composition, NFC) where the face that would draw it has one: и + U+0306 is
2982
+ * drawn as й, а + U+0306 as Chuvash ӑ, о + U+0308 as Altai ӧ. Set as two
2983
+ * glyphs the mark stood at its own default place, beside the letter and above
2984
+ * it; Word draws the two exactly as it draws the composed letter. What has no
2985
+ * composed form — the stressed а, а and U+0301 — is left to the face's anchors (see
2986
+ * `shapeMarks`).
2987
+ *
2988
+ * @param faces The face each character of the run is drawn with ({@link runFaces}).
2989
+ * @param text The run's text.
2990
+ * @returns One string per code point of `text`, empty where a mark went into its letter.
2991
+ */
2992
+ function drawnPieces(faces, text) {
2993
+ const cps = [...text].map((ch) => ch.codePointAt(0));
2994
+ const out = cps.map((cp) => String.fromCodePoint(cp));
2995
+ for (let i = 0; i < cps.length; i++) {
2996
+ let end = i + 1;
2997
+ while (end < cps.length && isCombiningMark(cps[end])) end++;
2998
+ if (end > i + 1 && composesInPlace(cps[i])) {
2999
+ const cluster = String.fromCodePoint(...cps.slice(i, end));
3000
+ const composed = cluster.normalize("NFC");
3001
+ const drawable = [...composed].every((ch) => faces(ch.codePointAt(0)).parsed.glyphForCodepoint(ch.codePointAt(0)) !== 0);
3002
+ if (composed !== cluster && drawable) {
3003
+ out[i] = composed;
3004
+ for (let k = i + 1; k < end; k++) out[k] = "";
3005
+ }
3006
+ i = end - 1;
3007
+ continue;
3008
+ }
3009
+ const letters = substituteLetters(cps[i]);
3010
+ if (letters !== void 0 && faces(cps[i]).parsed.glyphForCodepoint(cps[i]) === 0) out[i] = letters;
2965
3011
  }
2966
3012
  return out;
2967
3013
  }
2968
3014
  /**
3015
+ * Whether a letter's marks are composed into it by the layout: the Latin,
3016
+ * Greek and Cyrillic alphabets. The scripts of India compose and reorder in
3017
+ * their own shaper, and a CJK compatibility ideograph is not a letter to
3018
+ * recompose at all.
3019
+ */
3020
+ function composesInPlace(cp) {
3021
+ return cp < 1328 || cp >= 7680 && cp <= 8191;
3022
+ }
3023
+ /**
2969
3024
  * §9.2.2 — the face a request had to settle for, and what is left to draw.
2970
3025
  *
2971
3026
  * A registry may hold one face and be asked for four: `FontBytesByVariant`
@@ -3082,14 +3137,15 @@ function slotFaces(options, resolved, primary) {
3082
3137
  * slant: a script face is fetched in the regular weight only, and a bold run in
3083
3138
  * it is stroked, which is what F3's synthesis is for.
3084
3139
  */
3085
- function fallbackFace(options, cp, resolved) {
3086
- const face = fallbackFaceKey(options, cp, resolved.bold, resolved.italic);
3087
- if (!face) return void 0;
3140
+ function fallbackFace(options, cp, resolved, own) {
3141
+ const found = fallbackFaceKey(options, cp, resolved.bold, resolved.italic, own);
3142
+ if (!found) return void 0;
3143
+ const { widthScale, ...face } = found;
3088
3144
  const variant = face.fontKey.split(":")[1];
3089
3145
  const got = variant === "bold" || variant === "boldItalic" || variant === "italic" ? variant : "regular";
3090
3146
  return {
3091
3147
  ...face,
3092
- ...syntheticFace(got, resolved.bold, resolved.italic)
3148
+ ...syntheticFace(got, resolved.bold, resolved.italic, widthScale)
3093
3149
  };
3094
3150
  }
3095
3151
  /**
@@ -3119,11 +3175,41 @@ function runFaces(options, resolved, primary, text) {
3119
3175
  const own = slots?.get(fontSlotOf(cp, resolved.rtl)) ?? primary;
3120
3176
  if (own.parsed.glyphForCodepoint(cp) !== 0) return own;
3121
3177
  if (own !== primary && primary.parsed.glyphForCodepoint(cp) !== 0) return primary;
3122
- return fallbackFace(options, cp, resolved) ?? own;
3178
+ return fallbackFace(options, cp, resolved, own.fontKey.split(":")[0]) ?? own;
3123
3179
  };
3180
+ const marks = markFaces(drawing, text);
3181
+ const withMarks = marks ? (cp) => marks.get(cp) ?? drawing(cp) : drawing;
3124
3182
  const indic = indicFace(drawing, text);
3125
- if (!indic) return drawing;
3126
- return (cp) => INDIC_JOINS.has(cp) && indic.parsed.glyphForCodepoint(cp) !== 0 ? indic : drawing(cp);
3183
+ if (!indic) return withMarks;
3184
+ return (cp) => INDIC_JOINS.has(cp) && indic.parsed.glyphForCodepoint(cp) !== 0 ? indic : withMarks(cp);
3185
+ }
3186
+ /**
3187
+ * The face each combining mark of `text` is drawn in, where it is not its
3188
+ * own: the face of the letter it sits on, when that face has it. A mark is
3189
+ * placed on its letter by the anchors of ONE face (see `shapeMarks`), and a
3190
+ * Cambria run sets its Cyrillic in the face Caladea borrows it from while
3191
+ * Caladea has an acute of its own — so the acute of a stressed а was drawn by Caladea
3192
+ * beside a letter drawn by Noto Serif, and missed it. One face per mark per
3193
+ * run: the letter it first sits on decides.
3194
+ *
3195
+ * @param drawing The face each character is drawn in, letter by letter.
3196
+ * @param text The run's text.
3197
+ * @returns The marks to draw in another face, or `undefined` when none.
3198
+ */
3199
+ function markFaces(drawing, text) {
3200
+ if (!hasCombiningMark(text)) return void 0;
3201
+ let out;
3202
+ let base;
3203
+ for (const ch of text) {
3204
+ const cp = ch.codePointAt(0);
3205
+ if (!isCombiningMark(cp)) {
3206
+ base = drawing(cp);
3207
+ continue;
3208
+ }
3209
+ if (!base || out?.has(cp) === true) continue;
3210
+ if (drawing(cp).fontKey !== base.fontKey && base.parsed.glyphForCodepoint(cp) !== 0) (out ??= /* @__PURE__ */ new Map()).set(cp, base);
3211
+ }
3212
+ return out;
3127
3213
  }
3128
3214
  /** The face that draws the first letter of the scripts of India in `text`. */
3129
3215
  function indicFace(drawing, text) {
@@ -3225,6 +3311,7 @@ function collectFontResources(body, headersFooters, options, imageResources) {
3225
3311
  sink.glyphText ??= /* @__PURE__ */ new Map();
3226
3312
  if (!sink.glyphText.has(g)) sink.glyphText.set(g, t);
3227
3313
  }
3314
+ if (!shaped.glyphText) countSharedGlyphs(sink, span.parsed, shown);
3228
3315
  }
3229
3316
  if (run.text.includes(" ")) for (const stop of resolveParagraphProperties(para.properties, options.styles).tabs) {
3230
3317
  const ch = stop.leader === void 0 ? void 0 : TAB_LEADER_CHARS.get(stop.leader);
@@ -3390,12 +3477,52 @@ function collectFontResources(body, headersFooters, options, imageResources) {
3390
3477
  parsed: info.parsed,
3391
3478
  measure: createFontMeasure(info.parsed, options.layoutProfile !== "word"),
3392
3479
  gids: info.gids,
3393
- ...info.glyphText ? { glyphText: info.glyphText } : {}
3480
+ ...info.glyphText ? { glyphText: info.glyphText } : {},
3481
+ ...info.drawnAs ? { glyphChars: mostDrawn(info.drawnAs) } : {}
3394
3482
  });
3395
3483
  }
3396
3484
  return out;
3397
3485
  }
3398
3486
  /**
3487
+ * Count, for each glyph of `text` that several characters share in the face's
3488
+ * `cmap`, which of them drew it (see `sharedGlyphCodes`).
3489
+ *
3490
+ * @param sink The face's collected glyphs, which the counts are kept with.
3491
+ * @param parsed The face.
3492
+ * @param text The text the face draws.
3493
+ */
3494
+ function countSharedGlyphs(sink, parsed, text) {
3495
+ for (const ch of text) {
3496
+ const cp = ch.codePointAt(0);
3497
+ if (cp < 128) continue;
3498
+ const gid = parsed.glyphForCodepoint(cp);
3499
+ if (gid === 0 || sharedGlyphCodes(parsed, gid) === void 0) continue;
3500
+ sink.drawnAs ??= /* @__PURE__ */ new Map();
3501
+ let counts = sink.drawnAs.get(gid);
3502
+ if (!counts) sink.drawnAs.set(gid, counts = /* @__PURE__ */ new Map());
3503
+ counts.set(cp, (counts.get(cp) ?? 0) + 1);
3504
+ }
3505
+ }
3506
+ /**
3507
+ * The character each shared glyph was drawn for most often; the first drawn
3508
+ * where two tie. One /ToUnicode entry cannot name both, and a document that
3509
+ * spells the same word in both alphabets with one glyph is rarer than one
3510
+ * that does not.
3511
+ */
3512
+ function mostDrawn(drawnAs) {
3513
+ const out = /* @__PURE__ */ new Map();
3514
+ for (const [gid, counts] of drawnAs) {
3515
+ let best = -1;
3516
+ let most = 0;
3517
+ for (const [cp, n] of counts) if (n > most) {
3518
+ best = cp;
3519
+ most = n;
3520
+ }
3521
+ out.set(gid, best);
3522
+ }
3523
+ return out;
3524
+ }
3525
+ /**
3399
3526
  * The one line an empty paragraph still occupies — no tokens, but as tall as
3400
3527
  * the paragraph mark's own font (§17.3.1.31 `w:rPr` on the `w:pPr`, which is
3401
3528
  * exactly the run properties Word keeps for a paragraph that holds no runs).
@@ -3682,17 +3809,18 @@ function mendSplitSyllables(runs) {
3682
3809
  function charColours(options, paragraph, faces, resolved, run, drawn) {
3683
3810
  const from = run.charProperties;
3684
3811
  if (!from) return void 0;
3812
+ const pieces = drawn === run.text ? void 0 : drawnPieces(faces, run.text);
3685
3813
  const hexOf = /* @__PURE__ */ new Map([[run.properties, resolved.colorHex]]);
3686
3814
  const out = [];
3687
- let i = 0;
3688
- for (const ch of run.text) {
3689
- const props = from[i++] ?? run.properties;
3815
+ const count = [...run.text].length;
3816
+ for (let i = 0; i < count; i++) {
3817
+ const props = from[i] ?? run.properties;
3690
3818
  let hex = hexOf.get(props);
3691
3819
  if (hex === void 0) {
3692
3820
  hex = resolveRunProperties(props, paragraph.properties, options.styles).colorHex;
3693
3821
  hexOf.set(props, hex);
3694
3822
  }
3695
- const letters = drawn === run.text ? 1 : [...drawableText(faces, ch)].length;
3823
+ const letters = pieces ? [...pieces[i]].length : 1;
3696
3824
  for (let k = 0; k < letters; k++) out.push(hex);
3697
3825
  }
3698
3826
  return out.some((hex) => hex !== resolved.colorHex) ? out : void 0;
@@ -4207,7 +4335,7 @@ function lastCodePoint(s) {
4207
4335
  const chars = [...s];
4208
4336
  return chars.length > 0 ? chars[chars.length - 1].codePointAt(0) : 0;
4209
4337
  }
4210
- function paragraphItemStream(tokens, hyphenator, maxLineWidthPt) {
4338
+ function paragraphItemStream(tokens, hyphenator, maxLineWidthPt, squeeze = true) {
4211
4339
  const entries = [];
4212
4340
  let prevBoxEndCp = null;
4213
4341
  let prevWasImage = false;
@@ -4254,7 +4382,7 @@ function paragraphItemStream(tokens, hyphenator, maxLineWidthPt) {
4254
4382
  type: "glue",
4255
4383
  width: tok.widthPt,
4256
4384
  stretch: tok.widthPt * .6,
4257
- shrink: tok.widthPt * GLUE_SHRINK_RATIO
4385
+ shrink: squeeze ? tok.widthPt * GLUE_SHRINK_RATIO : 0
4258
4386
  } : {
4259
4387
  type: "box",
4260
4388
  width: tok.widthPt
@@ -4606,7 +4734,9 @@ function withLeadingTabWidths(tokens, resolved, firstLineWidth) {
4606
4734
  function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator, profile, leading, noExpandShiftReturn, lineWidths) {
4607
4735
  if (tokens.length === 0) return [];
4608
4736
  const widths = lineWidths ?? [firstLineWidth, otherWidth];
4609
- const entries = paragraphItemStream(withLeadingTabWidths(tokens, resolved, widths[0] ?? firstLineWidth), hyphenator, Math.max(0, ...widths));
4737
+ const measured = withLeadingTabWidths(tokens, resolved, widths[0] ?? firstLineWidth);
4738
+ const justified = resolved.alignment === "both" || resolved.alignment === "distribute";
4739
+ const entries = paragraphItemStream(measured, hyphenator, Math.max(0, ...widths), justified);
4610
4740
  const items = entries.map((e) => e.item);
4611
4741
  const breaks = profile === "ream" ? breakLines(items, widths).breaks : greedyBreakLines(items, widths);
4612
4742
  const buildLines = (at) => {
@@ -15,6 +15,12 @@ export interface EmbedTtfOptions {
15
15
  * sign's contextual form — so that /ToUnicode names them too.
16
16
  */
17
17
  readonly glyphText?: ReadonlyMap<number, string>;
18
+ /**
19
+ * The character each glyph that several characters share was drawn for —
20
+ * the Cyrillic schwa, say, where the face draws the Latin one with the same
21
+ * glyph — so that /ToUnicode names the one the page means.
22
+ */
23
+ readonly glyphChars?: ReadonlyMap<number, number>;
18
24
  }
19
25
  /** The result of {@link embedTtfFont}: the Type 0 font reference plus measurement/encoding helpers. */
20
26
  export interface EmbeddedFont {
@@ -58,7 +58,7 @@ function embedTtfFont(doc, parsed, options = {}) {
58
58
  W: widthRuns.flatMap(([first, ws]) => [first, ws]),
59
59
  CIDToGIDMap: name("Identity")
60
60
  }));
61
- const toUnicodeRef = doc.add(deflatedStream({}, buildToUnicodeCMap(parsed, subsetGids, options.glyphText)));
61
+ const toUnicodeRef = doc.add(deflatedStream({}, buildToUnicodeCMap(parsed, subsetGids, options.glyphText, options.glyphChars)));
62
62
  const type0Ref = doc.add(dict({
63
63
  Type: name("Font"),
64
64
  Subtype: name("Type0"),
@@ -127,7 +127,7 @@ function codeRank(cp) {
127
127
  if (cp >= 64256 && cp <= 65023 || cp >= 65136 && cp <= 65279) return 1;
128
128
  return 0;
129
129
  }
130
- function buildToUnicodeCMap(parsed, subsetGids, glyphText) {
130
+ function buildToUnicodeCMap(parsed, subsetGids, glyphText, glyphChars) {
131
131
  const inSubset = subsetGids ? new Set(subsetGids) : void 0;
132
132
  const gidToCps = /* @__PURE__ */ new Map();
133
133
  const order = [];
@@ -143,6 +143,7 @@ function buildToUnicodeCMap(parsed, subsetGids, glyphText) {
143
143
  gidToCps.set(gid, [cp]);
144
144
  order.push(gid);
145
145
  }
146
+ for (const [gid, cp] of glyphChars ?? []) if (gidToCps.get(gid)?.length === 1 && parsed.glyphForCodepoint(cp) === gid) gidToCps.set(gid, [cp]);
146
147
  for (const [gid, cps] of gidToCps) {
147
148
  const letters = cps.length === 1 ? lettersForLigature(cps[0]) : void 0;
148
149
  if (letters !== void 0) gidToCps.set(gid, [...letters].map((c) => c.codePointAt(0)));
@@ -524,7 +524,8 @@ function embedFontResources(doc, laid, pdfaProfile) {
524
524
  out.set(variant, embedTtfFont(doc, res.parsed, {
525
525
  usedGids: res.gids,
526
526
  cidSet,
527
- ...res.glyphText ? { glyphText: res.glyphText } : {}
527
+ ...res.glyphText ? { glyphText: res.glyphText } : {},
528
+ ...res.glyphChars ? { glyphChars: res.glyphChars } : {}
528
529
  }));
529
530
  }
530
531
  return out;
@@ -682,7 +683,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
682
683
  let lastTz = 100;
683
684
  /** A run's letter spacing, in the text space `Tz` scales (§9.4.4). */
684
685
  const spacingOf = (tok) => {
685
- const tz = Math.round((tok.synthetic?.widthScale ?? 1) * (tok.resolvedRun.widthScale ?? 1) * 100);
686
+ const tz = Math.round((tok.synthetic?.widthScale ?? 1) * (tok.resolvedRun.widthScale ?? 1) * 1e3) / 10;
686
687
  return (tok.resolvedRun.letterSpacingPt ?? 0) / (tz > 0 ? tz / 100 : 1);
687
688
  };
688
689
  /**
@@ -744,7 +745,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
744
745
  lastStroke = outline.colorHex;
745
746
  }
746
747
  const scale = (tok.synthetic?.widthScale ?? 1) * (tok.resolvedRun.widthScale ?? 1);
747
- const tz = Math.round(scale * 100);
748
+ const tz = Math.round(scale * 1e3) / 10;
748
749
  if (tz !== lastTz) {
749
750
  out.push(`${formatNumber(tz)} Tz`);
750
751
  lastTz = tz;
@@ -547,6 +547,335 @@ var GLYPHS = {
547
547
  zero: "0",
548
548
  zeta: "ζ"
549
549
  };
550
+ var CYRILLIC = /* @__PURE__ */ new Map([
551
+ ["afii10023", 1025],
552
+ ["Iocyrillic", 1025],
553
+ ["afii10051", 1026],
554
+ ["Djecyrillic", 1026],
555
+ ["afii10052", 1027],
556
+ ["Gjecyrillic", 1027],
557
+ ["afii10053", 1028],
558
+ ["Ecyrillic", 1028],
559
+ ["afii10054", 1029],
560
+ ["Dzecyrillic", 1029],
561
+ ["afii10055", 1030],
562
+ ["Icyrillic", 1030],
563
+ ["afii10056", 1031],
564
+ ["Yicyrillic", 1031],
565
+ ["afii10057", 1032],
566
+ ["Jecyrillic", 1032],
567
+ ["afii10058", 1033],
568
+ ["Ljecyrillic", 1033],
569
+ ["afii10059", 1034],
570
+ ["Njecyrillic", 1034],
571
+ ["afii10060", 1035],
572
+ ["Tshecyrillic", 1035],
573
+ ["afii10061", 1036],
574
+ ["Kjecyrillic", 1036],
575
+ ["afii10062", 1038],
576
+ ["Ushortcyrillic", 1038],
577
+ ["afii10145", 1039],
578
+ ["Dzhecyrillic", 1039],
579
+ ["Acyrillic", 1040],
580
+ ["afii10017", 1040],
581
+ ["afii10018", 1041],
582
+ ["Becyrillic", 1041],
583
+ ["afii10019", 1042],
584
+ ["Vecyrillic", 1042],
585
+ ["afii10020", 1043],
586
+ ["Gecyrillic", 1043],
587
+ ["afii10021", 1044],
588
+ ["Decyrillic", 1044],
589
+ ["afii10022", 1045],
590
+ ["Iecyrillic", 1045],
591
+ ["afii10024", 1046],
592
+ ["Zhecyrillic", 1046],
593
+ ["afii10025", 1047],
594
+ ["Zecyrillic", 1047],
595
+ ["afii10026", 1048],
596
+ ["Iicyrillic", 1048],
597
+ ["afii10027", 1049],
598
+ ["Iishortcyrillic", 1049],
599
+ ["afii10028", 1050],
600
+ ["Kacyrillic", 1050],
601
+ ["afii10029", 1051],
602
+ ["Elcyrillic", 1051],
603
+ ["afii10030", 1052],
604
+ ["Emcyrillic", 1052],
605
+ ["afii10031", 1053],
606
+ ["Encyrillic", 1053],
607
+ ["afii10032", 1054],
608
+ ["Ocyrillic", 1054],
609
+ ["afii10033", 1055],
610
+ ["Pecyrillic", 1055],
611
+ ["afii10034", 1056],
612
+ ["Ercyrillic", 1056],
613
+ ["afii10035", 1057],
614
+ ["Escyrillic", 1057],
615
+ ["afii10036", 1058],
616
+ ["Tecyrillic", 1058],
617
+ ["afii10037", 1059],
618
+ ["Ucyrillic", 1059],
619
+ ["afii10038", 1060],
620
+ ["Efcyrillic", 1060],
621
+ ["afii10039", 1061],
622
+ ["Khacyrillic", 1061],
623
+ ["afii10040", 1062],
624
+ ["Tsecyrillic", 1062],
625
+ ["afii10041", 1063],
626
+ ["Checyrillic", 1063],
627
+ ["afii10042", 1064],
628
+ ["Shacyrillic", 1064],
629
+ ["afii10043", 1065],
630
+ ["Shchacyrillic", 1065],
631
+ ["afii10044", 1066],
632
+ ["Hardsigncyrillic", 1066],
633
+ ["afii10045", 1067],
634
+ ["Yericyrillic", 1067],
635
+ ["afii10046", 1068],
636
+ ["Softsigncyrillic", 1068],
637
+ ["afii10047", 1069],
638
+ ["Ereversedcyrillic", 1069],
639
+ ["afii10048", 1070],
640
+ ["IUcyrillic", 1070],
641
+ ["afii10049", 1071],
642
+ ["IAcyrillic", 1071],
643
+ ["acyrillic", 1072],
644
+ ["afii10065", 1072],
645
+ ["afii10066", 1073],
646
+ ["becyrillic", 1073],
647
+ ["afii10067", 1074],
648
+ ["vecyrillic", 1074],
649
+ ["afii10068", 1075],
650
+ ["gecyrillic", 1075],
651
+ ["afii10069", 1076],
652
+ ["decyrillic", 1076],
653
+ ["afii10070", 1077],
654
+ ["iecyrillic", 1077],
655
+ ["afii10072", 1078],
656
+ ["zhecyrillic", 1078],
657
+ ["afii10073", 1079],
658
+ ["zecyrillic", 1079],
659
+ ["afii10074", 1080],
660
+ ["iicyrillic", 1080],
661
+ ["afii10075", 1081],
662
+ ["iishortcyrillic", 1081],
663
+ ["afii10076", 1082],
664
+ ["kacyrillic", 1082],
665
+ ["afii10077", 1083],
666
+ ["elcyrillic", 1083],
667
+ ["afii10078", 1084],
668
+ ["emcyrillic", 1084],
669
+ ["afii10079", 1085],
670
+ ["encyrillic", 1085],
671
+ ["afii10080", 1086],
672
+ ["ocyrillic", 1086],
673
+ ["afii10081", 1087],
674
+ ["pecyrillic", 1087],
675
+ ["afii10082", 1088],
676
+ ["ercyrillic", 1088],
677
+ ["afii10083", 1089],
678
+ ["escyrillic", 1089],
679
+ ["afii10084", 1090],
680
+ ["tecyrillic", 1090],
681
+ ["afii10085", 1091],
682
+ ["ucyrillic", 1091],
683
+ ["afii10086", 1092],
684
+ ["efcyrillic", 1092],
685
+ ["afii10087", 1093],
686
+ ["khacyrillic", 1093],
687
+ ["afii10088", 1094],
688
+ ["tsecyrillic", 1094],
689
+ ["afii10089", 1095],
690
+ ["checyrillic", 1095],
691
+ ["afii10090", 1096],
692
+ ["shacyrillic", 1096],
693
+ ["afii10091", 1097],
694
+ ["shchacyrillic", 1097],
695
+ ["afii10092", 1098],
696
+ ["hardsigncyrillic", 1098],
697
+ ["afii10093", 1099],
698
+ ["yericyrillic", 1099],
699
+ ["afii10094", 1100],
700
+ ["softsigncyrillic", 1100],
701
+ ["afii10095", 1101],
702
+ ["ereversedcyrillic", 1101],
703
+ ["afii10096", 1102],
704
+ ["iucyrillic", 1102],
705
+ ["afii10097", 1103],
706
+ ["iacyrillic", 1103],
707
+ ["afii10071", 1105],
708
+ ["iocyrillic", 1105],
709
+ ["afii10099", 1106],
710
+ ["djecyrillic", 1106],
711
+ ["afii10100", 1107],
712
+ ["gjecyrillic", 1107],
713
+ ["afii10101", 1108],
714
+ ["ecyrillic", 1108],
715
+ ["afii10102", 1109],
716
+ ["dzecyrillic", 1109],
717
+ ["afii10103", 1110],
718
+ ["icyrillic", 1110],
719
+ ["afii10104", 1111],
720
+ ["yicyrillic", 1111],
721
+ ["afii10105", 1112],
722
+ ["jecyrillic", 1112],
723
+ ["afii10106", 1113],
724
+ ["ljecyrillic", 1113],
725
+ ["afii10107", 1114],
726
+ ["njecyrillic", 1114],
727
+ ["afii10108", 1115],
728
+ ["tshecyrillic", 1115],
729
+ ["afii10109", 1116],
730
+ ["kjecyrillic", 1116],
731
+ ["afii10110", 1118],
732
+ ["ushortcyrillic", 1118],
733
+ ["afii10193", 1119],
734
+ ["dzhecyrillic", 1119],
735
+ ["Omegacyrillic", 1120],
736
+ ["omegacyrillic", 1121],
737
+ ["afii10146", 1122],
738
+ ["Yatcyrillic", 1122],
739
+ ["afii10194", 1123],
740
+ ["yatcyrillic", 1123],
741
+ ["Eiotifiedcyrillic", 1124],
742
+ ["eiotifiedcyrillic", 1125],
743
+ ["Yuslittlecyrillic", 1126],
744
+ ["yuslittlecyrillic", 1127],
745
+ ["Yuslittleiotifiedcyrillic", 1128],
746
+ ["yuslittleiotifiedcyrillic", 1129],
747
+ ["Yusbigcyrillic", 1130],
748
+ ["yusbigcyrillic", 1131],
749
+ ["Yusbigiotifiedcyrillic", 1132],
750
+ ["yusbigiotifiedcyrillic", 1133],
751
+ ["Ksicyrillic", 1134],
752
+ ["ksicyrillic", 1135],
753
+ ["Psicyrillic", 1136],
754
+ ["psicyrillic", 1137],
755
+ ["afii10147", 1138],
756
+ ["Fitacyrillic", 1138],
757
+ ["afii10195", 1139],
758
+ ["fitacyrillic", 1139],
759
+ ["afii10148", 1140],
760
+ ["Izhitsacyrillic", 1140],
761
+ ["afii10196", 1141],
762
+ ["izhitsacyrillic", 1141],
763
+ ["Izhitsadblgravecyrillic", 1142],
764
+ ["izhitsadblgravecyrillic", 1143],
765
+ ["Ukcyrillic", 1144],
766
+ ["ukcyrillic", 1145],
767
+ ["Omegaroundcyrillic", 1146],
768
+ ["omegaroundcyrillic", 1147],
769
+ ["Omegatitlocyrillic", 1148],
770
+ ["omegatitlocyrillic", 1149],
771
+ ["Otcyrillic", 1150],
772
+ ["otcyrillic", 1151],
773
+ ["Koppacyrillic", 1152],
774
+ ["koppacyrillic", 1153],
775
+ ["thousandcyrillic", 1154],
776
+ ["titlocyrilliccmb", 1155],
777
+ ["palatalizationcyrilliccmb", 1156],
778
+ ["dasiapneumatacyrilliccmb", 1157],
779
+ ["psilipneumatacyrilliccmb", 1158],
780
+ ["afii10050", 1168],
781
+ ["Gheupturncyrillic", 1168],
782
+ ["afii10098", 1169],
783
+ ["gheupturncyrillic", 1169],
784
+ ["Ghestrokecyrillic", 1170],
785
+ ["ghestrokecyrillic", 1171],
786
+ ["Ghemiddlehookcyrillic", 1172],
787
+ ["ghemiddlehookcyrillic", 1173],
788
+ ["Zhedescendercyrillic", 1174],
789
+ ["zhedescendercyrillic", 1175],
790
+ ["Zedescendercyrillic", 1176],
791
+ ["zedescendercyrillic", 1177],
792
+ ["Kadescendercyrillic", 1178],
793
+ ["kadescendercyrillic", 1179],
794
+ ["Kaverticalstrokecyrillic", 1180],
795
+ ["kaverticalstrokecyrillic", 1181],
796
+ ["Kastrokecyrillic", 1182],
797
+ ["kastrokecyrillic", 1183],
798
+ ["Kabashkircyrillic", 1184],
799
+ ["kabashkircyrillic", 1185],
800
+ ["Endescendercyrillic", 1186],
801
+ ["endescendercyrillic", 1187],
802
+ ["Enghecyrillic", 1188],
803
+ ["enghecyrillic", 1189],
804
+ ["Pemiddlehookcyrillic", 1190],
805
+ ["pemiddlehookcyrillic", 1191],
806
+ ["Haabkhasiancyrillic", 1192],
807
+ ["haabkhasiancyrillic", 1193],
808
+ ["Esdescendercyrillic", 1194],
809
+ ["esdescendercyrillic", 1195],
810
+ ["Tedescendercyrillic", 1196],
811
+ ["tedescendercyrillic", 1197],
812
+ ["Ustraightcyrillic", 1198],
813
+ ["ustraightcyrillic", 1199],
814
+ ["Ustraightstrokecyrillic", 1200],
815
+ ["ustraightstrokecyrillic", 1201],
816
+ ["Hadescendercyrillic", 1202],
817
+ ["hadescendercyrillic", 1203],
818
+ ["Tetsecyrillic", 1204],
819
+ ["tetsecyrillic", 1205],
820
+ ["Chedescendercyrillic", 1206],
821
+ ["chedescendercyrillic", 1207],
822
+ ["Cheverticalstrokecyrillic", 1208],
823
+ ["cheverticalstrokecyrillic", 1209],
824
+ ["Shhacyrillic", 1210],
825
+ ["shhacyrillic", 1211],
826
+ ["Cheabkhasiancyrillic", 1212],
827
+ ["cheabkhasiancyrillic", 1213],
828
+ ["Chedescenderabkhasiancyrillic", 1214],
829
+ ["chedescenderabkhasiancyrillic", 1215],
830
+ ["palochkacyrillic", 1216],
831
+ ["Zhebrevecyrillic", 1217],
832
+ ["zhebrevecyrillic", 1218],
833
+ ["Kahookcyrillic", 1219],
834
+ ["kahookcyrillic", 1220],
835
+ ["Enhookcyrillic", 1223],
836
+ ["enhookcyrillic", 1224],
837
+ ["Chekhakassiancyrillic", 1227],
838
+ ["chekhakassiancyrillic", 1228],
839
+ ["Abrevecyrillic", 1232],
840
+ ["abrevecyrillic", 1233],
841
+ ["Adieresiscyrillic", 1234],
842
+ ["adieresiscyrillic", 1235],
843
+ ["Aiecyrillic", 1236],
844
+ ["aiecyrillic", 1237],
845
+ ["Iebrevecyrillic", 1238],
846
+ ["iebrevecyrillic", 1239],
847
+ ["Schwacyrillic", 1240],
848
+ ["afii10846", 1241],
849
+ ["schwacyrillic", 1241],
850
+ ["Schwadieresiscyrillic", 1242],
851
+ ["schwadieresiscyrillic", 1243],
852
+ ["Zhedieresiscyrillic", 1244],
853
+ ["zhedieresiscyrillic", 1245],
854
+ ["Zedieresiscyrillic", 1246],
855
+ ["zedieresiscyrillic", 1247],
856
+ ["Dzeabkhasiancyrillic", 1248],
857
+ ["dzeabkhasiancyrillic", 1249],
858
+ ["Imacroncyrillic", 1250],
859
+ ["imacroncyrillic", 1251],
860
+ ["Idieresiscyrillic", 1252],
861
+ ["idieresiscyrillic", 1253],
862
+ ["Odieresiscyrillic", 1254],
863
+ ["odieresiscyrillic", 1255],
864
+ ["Obarredcyrillic", 1256],
865
+ ["obarredcyrillic", 1257],
866
+ ["Obarreddieresiscyrillic", 1258],
867
+ ["obarreddieresiscyrillic", 1259],
868
+ ["Umacroncyrillic", 1262],
869
+ ["umacroncyrillic", 1263],
870
+ ["Udieresiscyrillic", 1264],
871
+ ["udieresiscyrillic", 1265],
872
+ ["Uhungarumlautcyrillic", 1266],
873
+ ["uhungarumlautcyrillic", 1267],
874
+ ["Chedieresiscyrillic", 1268],
875
+ ["chedieresiscyrillic", 1269],
876
+ ["Yerudieresiscyrillic", 1272],
877
+ ["yerudieresiscyrillic", 1273]
878
+ ]);
550
879
  /**
551
880
  * The text a glyph name stands for, or `undefined` when the name says nothing.
552
881
  *
@@ -561,6 +890,8 @@ function textForGlyphName(name) {
561
890
  }
562
891
  const known = GLYPHS[base];
563
892
  if (known !== void 0) return known;
893
+ const cyrillic = CYRILLIC.get(base);
894
+ if (cyrillic !== void 0) return String.fromCodePoint(cyrillic);
564
895
  if (base.length === 1) return base;
565
896
  const uni = /^uni((?:[0-9A-Fa-f]{4})+)$/u.exec(base);
566
897
  if (uni) {