oasis-editor 0.0.97 → 0.0.99

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,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { v as visitRun, a as assertNever, c as createSignal, b as createEditorStateFromDocument, d as createInitialEditorState, S as STANDARD_FONT_SIZES_PT, f as fontSizePxToPt, p as probeLocalFontFamilies, e as createEditorParagraphFromRuns, i as isInlineObjectRun, g as createEditorStyledRun, h as getParagraphLength, j as getParagraphText, k as getActiveZone, l as getDocumentSections, m as getActiveSectionIndex, n as getParagraphs, o as normalizeSelection, q as positionToParagraphOffset, r as paragraphOffsetToPosition, s as clampPosition, t as findParagraphIndex, u as createCollapsedSelection, w as isSelectionCollapsed, x as parseFontSizePtToPx, y as formatFontSizePt, z as createEditorParagraph, A as getBlockParagraphs, B as findParagraphTableLocation, C as buildTableCellLayout, P as PT_PER_PX, D as createEditorTableCell, E as createEditorTableRow, F as createEditorTable, G as underlineStyleToCssDecorationStyle, H as resolveImageSrc, I as listKindForTag, J as isParagraphTag, K as collectInlineRuns, L as parseParagraphStyle, M as getRunImage, N as InlineShell, O as BalloonShell, Q as DocumentShell, R as createMemo, T as buildCanvasLayoutSnapshot, U as getCaretRectFromSnapshot, V as getParagraphRectFromSnapshot, W as createComponent, X as CaretOverlay, Y as Show, Z as createRenderEffect, _ as style, $ as setAttribute, a0 as setStyleProperty, a1 as memo, a2 as template, a3 as useI18n, a4 as createEffect, a5 as insert, a6 as use, a7 as addEventListener, a8 as Dialog, a9 as delegateEvents, aa as className, ab as For, ac as UNDERLINE_STYLE_OPTIONS, ad as Tabs, ae as onMount, af as onCleanup, ag as PluginUiHost, ah as OasisEditorEditor, ai as OasisBrandMark, aj as setPreciseFontPreference, ak as setWelcomeSeen, al as enablePreciseFontMode, am as TWIPS_PER_POINT, an as PX_PER_INCH, ao as TWIPS_PER_INCH, ap as resolveEffectiveParagraphStyle, aq as resolveEffectiveTextStyleForParagraph, ar as EMU_PER_PX, as as EMU_PER_PT, at as getRunFootnoteReference, au as getRunEndnoteReference, av as iterateFootnoteReferenceRuns, aw as iterateEndnoteReferenceRuns, ax as imageContentTypeDefaults, ay as getRunFieldChar, az as getRunFieldInstruction, aA as createEditorRun, aB as JSZip, aC as imageExtensionFromMime, aD as pxToPt$1, aE as resolveFloatingObjectRect, aF as getTextBoxFloatingGeometry, aG as getPresetPathSegments, aH as projectBlocksLayout, aI as buildListLabels, aJ as getImageFloatingGeometry, aK as textStyleToFontSizePt, aL as PX_PER_POINT, aM as DEFAULT_FONT_SIZE_PX, aN as isDoubleUnderlineStyle, aO as isWavyUnderlineStyle, aP as underlineStyleLineWidthPx, aQ as underlineStyleDashArray, aR as resolveListLabel, aS as getListLabelInset, aT as getAlignedListLabelInset, aU as getParagraphBorderInsets, aV as buildSegmentTable, aW as buildCanvasTableLayout, aX as resolveCanvasTableWidth, aY as resolveFloatingTableRect, aZ as normalizeFamily, a_ as ROBOTO_FONT_FILES, a$ as loadFontAsset, b0 as OFFICE_COMPAT_FONT_FAMILIES, b1 as buildSfnt, b2 as defaultFontDecoderRegistry, b3 as SfntFontProgram, b4 as collectPdfFontFamilies, b5 as projectDocumentLayout, b6 as getPageContentWidth, b7 as getPageHeaderZoneTop, b8 as getPageBodyTop, b9 as getPageColumnRects, ba as findFootnoteReference, bb as FOOTNOTE_MARKER_GUTTER_PX, bc as resolveImporterForFile, bd as getDocumentSectionsCanonical, be as getDocumentParagraphsCanonical, bf as getDocumentParagraphs, bg as getDocumentPageSettings, bh as getTableCellContentWidthForParagraph, bi as on, bj as debounce, bk as unwrap, bl as perfTimer, bm as getRunTextBox, bn as createEditorDocument, bo as resolveResizedDimensions, bp as resolveTextBoxRenderHeight, bq as getToolbarStyleState, br as getCachedCanvasImage, bs as measureParagraphMinContentWidthPx, bt as getEditableBlocksForZone, bu as findParagraphLocation, bv as createSectionBoundaryParagraph, bw as normalizePageSettings, bx as DEFAULT_EDITOR_PAGE_SETTINGS, by as markStart, bz as markEnd, bA as getParagraphEntries, bB as getParagraphById, bC as createEditorFootnote, bD as createFootnoteReferenceRun, bE as renumberFootnotes, bF as getFootnoteDisplayMarker, bG as getHeadingLevel, bH as preciseFontModeVersion, bI as isPreciseFontModeEnabled, bJ as resolveNamedTextStyle, bK as togglePreciseFontMode, bL as nextFontSizePt, bM as previousFontSizePt, bN as fontSizePtToPx, bO as createDefaultToolbarPreset, bP as MenuRegistry, bQ as createToolbarRegistry, bR as Editor, bS as resolveCommandRef, bT as commandRefName, bU as createOasisEditorClient, bV as createEditorZoom, bW as startLongTaskObserver, bX as installGlobalReport, bY as applyStoredPreciseFontPreference, bZ as getWelcomeSeen, b_ as isLocalFontAccessSupported, b$ as EDITOR_SCROLL_PADDING_PX, c0 as Toolbar, c1 as OasisEditorLoading, c2 as I18nProvider, c3 as createTranslator, c4 as createEditorLogger, c5 as registerDomStatsSurface } from "./index-BiTnt_yD.js";
4
+ import { v as visitRun, a as assertNever, c as createSignal, b as createEditorStateFromDocument, d as createInitialEditorState, S as STANDARD_FONT_SIZES_PT, f as fontSizePxToPt, p as probeLocalFontFamilies, e as createEditorParagraphFromRuns, i as isInlineObjectRun, g as createEditorStyledRun, h as getParagraphLength, j as getParagraphText, k as getActiveZone, l as getDocumentSections, m as getActiveSectionIndex, n as getParagraphs, o as normalizeSelection, q as positionToParagraphOffset, r as paragraphOffsetToPosition, s as clampPosition, t as findParagraphIndex, u as createCollapsedSelection, w as isSelectionCollapsed, x as parseFontSizePtToPx, y as formatFontSizePt, z as createEditorParagraph, A as getBlockParagraphs, B as findParagraphTableLocation, C as buildTableCellLayout, P as PT_PER_PX, D as createEditorTableCell, E as createEditorTableRow, F as createEditorTable, G as underlineStyleToCssDecorationStyle, H as resolveImageSrc, I as listKindForTag, J as isParagraphTag, K as collectInlineRuns, L as parseParagraphStyle, M as getRunImage, N as InlineShell, O as BalloonShell, Q as DocumentShell, R as createMemo, T as buildCanvasLayoutSnapshot, U as getCaretRectFromSnapshot, V as getParagraphRectFromSnapshot, W as createComponent, X as CaretOverlay, Y as Show, Z as createRenderEffect, _ as style, $ as setAttribute, a0 as setStyleProperty, a1 as memo, a2 as template, a3 as useI18n, a4 as createEffect, a5 as insert, a6 as use, a7 as addEventListener, a8 as Dialog, a9 as delegateEvents, aa as className, ab as For, ac as UNDERLINE_STYLE_OPTIONS, ad as Tabs, ae as onMount, af as onCleanup, ag as PluginUiHost, ah as OasisEditorEditor, ai as OasisBrandMark, aj as setPreciseFontPreference, ak as setWelcomeSeen, al as enablePreciseFontMode, am as TWIPS_PER_POINT, an as PX_PER_INCH, ao as TWIPS_PER_INCH, ap as resolveEffectiveParagraphStyle, aq as resolveEffectiveTextStyleForParagraph, ar as EMU_PER_PX, as as EMU_PER_PT, at as getRunFootnoteReference, au as getRunEndnoteReference, av as iterateFootnoteReferenceRuns, aw as iterateEndnoteReferenceRuns, ax as imageContentTypeDefaults, ay as getRunFieldChar, az as getRunFieldInstruction, aA as createEditorRun, aB as JSZip, aC as imageExtensionFromMime, aD as pxToPt$1, aE as resolveFloatingObjectRect, aF as getTextBoxFloatingGeometry, aG as getPresetPathSegments, aH as projectBlocksLayout, aI as buildListLabels, aJ as getImageFloatingGeometry, aK as textStyleToFontSizePt, aL as PX_PER_POINT, aM as resolveOpenTypeFeatureTags, aN as DEFAULT_FONT_SIZE_PX, aO as isDoubleUnderlineStyle, aP as isWavyUnderlineStyle, aQ as underlineStyleLineWidthPx, aR as underlineStyleDashArray, aS as resolveListLabel, aT as getListLabelInset, aU as getAlignedListLabelInset, aV as getParagraphBorderInsets, aW as buildSegmentTable, aX as buildCanvasTableLayout, aY as resolveCanvasTableWidth, aZ as resolveFloatingTableRect, a_ as normalizeFamily, a$ as ROBOTO_FONT_FILES, b0 as loadFontAsset, b1 as OFFICE_COMPAT_FONT_FAMILIES, b2 as BinaryReader, b3 as buildSfnt, b4 as defaultFontDecoderRegistry, b5 as SfntFontProgram, b6 as collectPdfFontFamilies, b7 as projectDocumentLayout, b8 as getPageContentWidth, b9 as getPageHeaderZoneTop, ba as getPageBodyTop, bb as getPageColumnRects, bc as findFootnoteReference, bd as FOOTNOTE_MARKER_GUTTER_PX, be as resolveImporterForFile, bf as getDocumentSectionsCanonical, bg as getDocumentParagraphsCanonical, bh as getDocumentParagraphs, bi as getDocumentPageSettings, bj as getTableCellContentWidthForParagraph, bk as on, bl as debounce, bm as unwrap, bn as perfTimer, bo as getRunTextBox, bp as createEditorDocument, bq as resolveResizedDimensions, br as resolveTextBoxRenderHeight, bs as getToolbarStyleState, bt as getCachedCanvasImage, bu as measureParagraphMinContentWidthPx, bv as getEditableBlocksForZone, bw as findParagraphLocation, bx as createSectionBoundaryParagraph, by as normalizePageSettings, bz as DEFAULT_EDITOR_PAGE_SETTINGS, bA as markStart, bB as markEnd, bC as getParagraphEntries, bD as getParagraphById, bE as createEditorFootnote, bF as createFootnoteReferenceRun, bG as renumberFootnotes, bH as getFootnoteDisplayMarker, bI as getHeadingLevel, bJ as preciseFontModeVersion, bK as isPreciseFontModeEnabled, bL as resolveNamedTextStyle, bM as togglePreciseFontMode, bN as nextFontSizePt, bO as previousFontSizePt, bP as fontSizePtToPx, bQ as createDefaultToolbarPreset, bR as MenuRegistry, bS as createToolbarRegistry, bT as Editor, bU as resolveCommandRef, bV as commandRefName, bW as createOasisEditorClient, bX as createEditorZoom, bY as startLongTaskObserver, bZ as installGlobalReport, b_ as applyStoredPreciseFontPreference, b$ as getWelcomeSeen, c0 as isLocalFontAccessSupported, c1 as EDITOR_SCROLL_PADDING_PX, c2 as Toolbar, c3 as OasisEditorLoading, c4 as I18nProvider, c5 as createTranslator, c6 as createEditorLogger, c7 as registerDomStatsSurface } from "./index-CNuDp_SC.js";
5
5
  function cloneStyle(style2) {
6
6
  return style2 ? { ...style2 } : void 0;
7
7
  }
@@ -33105,6 +33105,7 @@ function serializeBookmarkEvent(event) {
33105
33105
  )}"${col}/>`;
33106
33106
  }
33107
33107
  const WORD15_NS = "http://schemas.microsoft.com/office/word/2012/wordml";
33108
+ const WORD16DU_NS = "http://schemas.microsoft.com/office/word/2023/wordml/word16du";
33108
33109
  function buildCommentExportPlan(document2) {
33109
33110
  const registry = document2.comments;
33110
33111
  if (!registry || registry.order.length === 0) {
@@ -33180,7 +33181,7 @@ function serializeCommentRangeEvent(event) {
33180
33181
  return `<w:r><w:commentReference w:id="${event.wId}"/></w:r>`;
33181
33182
  }
33182
33183
  }
33183
- const COMMENTS_XMLNS = `xmlns:w="${WORD_NS$1}" xmlns:w14="${WORD14_NS}" xmlns:w15="${WORD15_NS}"`;
33184
+ const COMMENTS_XMLNS = `xmlns:w="${WORD_NS$1}" xmlns:w14="${WORD14_NS}" xmlns:w15="${WORD15_NS}" xmlns:w16du="${WORD16DU_NS}"`;
33184
33185
  function serializeCommentBody(text) {
33185
33186
  const lines = text.split("\n");
33186
33187
  const runs = lines.map((line, i) => {
@@ -33192,8 +33193,9 @@ function serializeCommentBody(text) {
33192
33193
  function buildCommentsPartXml(plan) {
33193
33194
  const body = plan.comments.map(({ comment, wId, paraId }) => {
33194
33195
  const dateAttr = comment.date !== void 0 ? ` w:date="${new Date(comment.date).toISOString().replace(/\.\d{3}Z$/, "Z")}"` : "";
33196
+ const dateUtcAttr = comment.dateUtc !== void 0 ? ` w16du:dateUtc="${new Date(comment.dateUtc).toISOString().replace(/\.\d{3}Z$/, "Z")}"` : "";
33195
33197
  const initialsAttr = comment.initials ? ` w:initials="${escapeXml(comment.initials)}"` : "";
33196
- return `<w:comment w:id="${wId}" w:author="${escapeXml(comment.author)}"${dateAttr}${initialsAttr}><w:p w14:paraId="${paraId}">${serializeCommentBody(comment.text)}</w:p></w:comment>`;
33198
+ return `<w:comment w:id="${wId}" w:author="${escapeXml(comment.author)}"${dateAttr}${dateUtcAttr}${initialsAttr}><w:p w14:paraId="${paraId}">${serializeCommentBody(comment.text)}</w:p></w:comment>`;
33197
33199
  }).join("");
33198
33200
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:comments ${COMMENTS_XMLNS}>${body}</w:comments>`;
33199
33201
  }
@@ -33795,6 +33797,8 @@ function materializeRunStyle(run, paragraphStyleId, styles) {
33795
33797
  textShadow: effective.textShadow,
33796
33798
  glow: effective.glow,
33797
33799
  reflection: effective.reflection,
33800
+ scene3dXml: effective.scene3dXml,
33801
+ props3dXml: effective.props3dXml,
33798
33802
  highlight: effective.highlight,
33799
33803
  shading: effective.shading,
33800
33804
  language: effective.language
@@ -34036,6 +34040,9 @@ function serializeReflectionMC(reflection) {
34036
34040
  const distEmu = Math.round(Math.max(0, reflection.distPt) * 12700);
34037
34041
  return `<mc:AlternateContent xmlns:mc="${MC_NS}"><mc:Choice Requires="w14"><w14:reflection w14:blurRad="${blurEmu}" w14:stA="${stA}" w14:stPos="${stPos}" w14:endA="${endA}" w14:endPos="${endPos}" w14:dist="${distEmu}" w14:dir="5400000" w14:fadeDir="5400000" w14:sx="100000" w14:sy="-100000" w14:kx="0" w14:ky="0" w14:algn="bl"/></mc:Choice><mc:Fallback/></mc:AlternateContent>`;
34038
34042
  }
34043
+ function serialize3dBlobMC(blob) {
34044
+ return `<mc:AlternateContent xmlns:mc="${MC_NS}"><mc:Choice Requires="w14">${blob}</mc:Choice><mc:Fallback/></mc:AlternateContent>`;
34045
+ }
34039
34046
  function pointsToSignedTwips(value) {
34040
34047
  if (value === void 0 || value === null || !Number.isFinite(value)) {
34041
34048
  return null;
@@ -34222,6 +34229,12 @@ function serializeRunProperties(styles) {
34222
34229
  if (styles.reflection) {
34223
34230
  parts.push(serializeReflectionMC(styles.reflection));
34224
34231
  }
34232
+ if (styles.scene3dXml) {
34233
+ parts.push(serialize3dBlobMC(styles.scene3dXml));
34234
+ }
34235
+ if (styles.props3dXml) {
34236
+ parts.push(serialize3dBlobMC(styles.props3dXml));
34237
+ }
34225
34238
  if (styles.highlight) {
34226
34239
  parts.push(
34227
34240
  `<w:highlight w:val="${escapeXml(normalizeHighlightForDocx(styles.highlight))}"/>`
@@ -36296,6 +36309,13 @@ async function drawFloatingImagesForParagraph(options) {
36296
36309
  }
36297
36310
  }
36298
36311
  }
36312
+ function blendColorWithWhite(hex, alpha) {
36313
+ const a = Math.max(0, Math.min(1, alpha));
36314
+ const r = Math.round(255 + (Number.parseInt(hex.slice(1, 3), 16) - 255) * a);
36315
+ const g = Math.round(255 + (Number.parseInt(hex.slice(3, 5), 16) - 255) * a);
36316
+ const b = Math.round(255 + (Number.parseInt(hex.slice(5, 7), 16) - 255) * a);
36317
+ return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
36318
+ }
36299
36319
  function drawFragmentHighlight(writer, pageIndex, line, fragment, originX, originY, styles) {
36300
36320
  if (!styles.highlight) {
36301
36321
  return;
@@ -36694,13 +36714,15 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
36694
36714
  styles.color ?? "#000000"
36695
36715
  );
36696
36716
  const paragraphAlign = resolveEffectiveParagraphStyle(paragraph.style, document2.styles).align ?? "left";
36717
+ const fontFeatures = resolveOpenTypeFeatureTags(styles);
36697
36718
  const baseTextOptions = {
36698
36719
  fontSize: fontSizePt,
36699
36720
  bold: styles.bold,
36700
36721
  italic: styles.italic,
36701
36722
  fontResourceName: fontFace.writerResourceName,
36702
36723
  characterSpacing: styles.characterSpacing ?? 0,
36703
- horizontalScale: styles.characterScale ?? 100
36724
+ horizontalScale: styles.characterScale ?? 100,
36725
+ fontFeatures
36704
36726
  };
36705
36727
  const mainColor = ((_b = styles.textFill) == null ? void 0 : _b.type) === "solid" ? styles.textFill.color : ((_c = styles.textFill) == null ? void 0 : _c.type) === "gradient" && styles.textFill.stops[0] ? styles.textFill.stops[0].color : styles.color ?? "#000000";
36706
36728
  const offsetPt = pxToPt$1(1);
@@ -36715,6 +36737,30 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
36715
36737
  color: "#BFBFBF"
36716
36738
  });
36717
36739
  }
36740
+ if (styles.glow) {
36741
+ const gl = styles.glow;
36742
+ const r = pxToPt$1(gl.radiusPt * PX_PER_POINT) * 0.5;
36743
+ const glowColor = blendColorWithWhite(gl.color, (gl.alpha ?? 0.5) * 0.4);
36744
+ const dirs = [
36745
+ [r, 0],
36746
+ [-r, 0],
36747
+ [0, r],
36748
+ [0, -r],
36749
+ [r * 0.7, r * 0.7],
36750
+ [-r * 0.7, r * 0.7],
36751
+ [r * 0.7, -r * 0.7],
36752
+ [-r * 0.7, -r * 0.7]
36753
+ ];
36754
+ for (const [dx, dy] of dirs) {
36755
+ writer.drawText(pageIndex, {
36756
+ ...baseTextOptions,
36757
+ x: pxToPt$1(leftPx) + dx,
36758
+ y: pxToPt$1(baselineY) + dy,
36759
+ text: text2,
36760
+ color: glowColor
36761
+ });
36762
+ }
36763
+ }
36718
36764
  if (styles.textShadow) {
36719
36765
  const ts = styles.textShadow;
36720
36766
  const dirRad = ts.dirDeg * Math.PI / 180;
@@ -36735,6 +36781,18 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
36735
36781
  color: "#808080"
36736
36782
  });
36737
36783
  }
36784
+ if (styles.reflection) {
36785
+ const ref = styles.reflection;
36786
+ const avgAlpha = (ref.startAlpha + ref.endAlpha) / 2;
36787
+ const refColor = blendColorWithWhite(mainColor, avgAlpha * 0.6);
36788
+ writer.drawText(pageIndex, {
36789
+ ...baseTextOptions,
36790
+ x: pxToPt$1(leftPx),
36791
+ y: pxToPt$1(baselineY) + pxToPt$1(ref.distPt * PX_PER_POINT) + fontSizePt,
36792
+ text: text2,
36793
+ color: refColor
36794
+ });
36795
+ }
36738
36796
  const textOutline = styles.textOutline;
36739
36797
  writer.drawText(pageIndex, {
36740
36798
  ...baseTextOptions,
@@ -37726,7 +37784,11 @@ class PdfContentStream {
37726
37784
  }
37727
37785
  const fontResourceName = this.fonts.resolveAndMarkFontName(options);
37728
37786
  const unicodeState = this.fonts.getUnicodeState(fontResourceName);
37729
- const showCommand = unicodeState ? this.fonts.buildUnicodeShowCommand(unicodeState, options.text) : `<${encodePdfHexString(options.text)}> Tj`;
37787
+ const showCommand = unicodeState ? this.fonts.buildUnicodeShowCommand(
37788
+ unicodeState,
37789
+ options.text,
37790
+ options.fontFeatures
37791
+ ) : `<${encodePdfHexString(options.text)}> Tj`;
37730
37792
  if (showCommand === null) {
37731
37793
  return;
37732
37794
  }
@@ -37813,6 +37875,636 @@ class PdfContentStream {
37813
37875
  );
37814
37876
  }
37815
37877
  }
37878
+ function readTag(reader) {
37879
+ return reader.tag();
37880
+ }
37881
+ function parseCoverage(reader, offset) {
37882
+ reader.seek(offset);
37883
+ const format = reader.u16();
37884
+ if (format === 1) {
37885
+ const count = reader.u16();
37886
+ const map = /* @__PURE__ */ new Map();
37887
+ for (let i = 0; i < count; i += 1) {
37888
+ map.set(reader.u16(), i);
37889
+ }
37890
+ return (glyphId) => map.get(glyphId) ?? -1;
37891
+ }
37892
+ if (format === 2) {
37893
+ const rangeCount = reader.u16();
37894
+ const ranges = [];
37895
+ for (let i = 0; i < rangeCount; i += 1) {
37896
+ const start = reader.u16();
37897
+ const end = reader.u16();
37898
+ const startIndex = reader.u16();
37899
+ ranges.push({ start, end, startIndex });
37900
+ }
37901
+ return (glyphId) => {
37902
+ for (const range of ranges) {
37903
+ if (glyphId >= range.start && glyphId <= range.end) {
37904
+ return range.startIndex + (glyphId - range.start);
37905
+ }
37906
+ }
37907
+ return -1;
37908
+ };
37909
+ }
37910
+ return () => -1;
37911
+ }
37912
+ function parseClassDef(reader, offset) {
37913
+ reader.seek(offset);
37914
+ const format = reader.u16();
37915
+ if (format === 1) {
37916
+ const startGlyph = reader.u16();
37917
+ const glyphCount = reader.u16();
37918
+ const classes = new Array(glyphCount);
37919
+ for (let i = 0; i < glyphCount; i += 1) {
37920
+ classes[i] = reader.u16();
37921
+ }
37922
+ return (glyphId) => {
37923
+ const index = glyphId - startGlyph;
37924
+ return index >= 0 && index < glyphCount ? classes[index] : 0;
37925
+ };
37926
+ }
37927
+ if (format === 2) {
37928
+ const rangeCount = reader.u16();
37929
+ const ranges = [];
37930
+ for (let i = 0; i < rangeCount; i += 1) {
37931
+ const start = reader.u16();
37932
+ const end = reader.u16();
37933
+ const classValue = reader.u16();
37934
+ ranges.push({ start, end, classValue });
37935
+ }
37936
+ return (glyphId) => {
37937
+ for (const range of ranges) {
37938
+ if (glyphId >= range.start && glyphId <= range.end) {
37939
+ return range.classValue;
37940
+ }
37941
+ }
37942
+ return 0;
37943
+ };
37944
+ }
37945
+ return () => 0;
37946
+ }
37947
+ function parseSingleSubst(reader, offset) {
37948
+ reader.seek(offset);
37949
+ const format = reader.u16();
37950
+ const coverageOffset = offset + reader.u16();
37951
+ if (format === 1) {
37952
+ const delta = reader.i16();
37953
+ const coverage = parseCoverage(reader, coverageOffset);
37954
+ return {
37955
+ apply(glyphs, pos) {
37956
+ const glyph = glyphs[pos];
37957
+ if (!glyph || coverage(glyph.id) < 0) return null;
37958
+ glyph.id = glyph.id + delta & 65535;
37959
+ return 1;
37960
+ }
37961
+ };
37962
+ }
37963
+ if (format === 2) {
37964
+ const glyphCount = reader.u16();
37965
+ const substitutes = new Array(glyphCount);
37966
+ for (let i = 0; i < glyphCount; i += 1) {
37967
+ substitutes[i] = reader.u16();
37968
+ }
37969
+ const coverage = parseCoverage(reader, coverageOffset);
37970
+ return {
37971
+ apply(glyphs, pos) {
37972
+ const glyph = glyphs[pos];
37973
+ if (!glyph) return null;
37974
+ const index = coverage(glyph.id);
37975
+ if (index < 0 || index >= glyphCount) return null;
37976
+ glyph.id = substitutes[index];
37977
+ return 1;
37978
+ }
37979
+ };
37980
+ }
37981
+ return null;
37982
+ }
37983
+ function parseMultipleSubst(reader, offset) {
37984
+ reader.seek(offset);
37985
+ const format = reader.u16();
37986
+ if (format !== 1) return null;
37987
+ const coverageOffset = offset + reader.u16();
37988
+ const sequenceCount = reader.u16();
37989
+ const sequenceOffsets = new Array(sequenceCount);
37990
+ for (let i = 0; i < sequenceCount; i += 1) {
37991
+ sequenceOffsets[i] = offset + reader.u16();
37992
+ }
37993
+ const sequences = sequenceOffsets.map((seqOffset) => {
37994
+ reader.seek(seqOffset);
37995
+ const glyphCount = reader.u16();
37996
+ const ids = new Array(glyphCount);
37997
+ for (let i = 0; i < glyphCount; i += 1) ids[i] = reader.u16();
37998
+ return ids;
37999
+ });
38000
+ const coverage = parseCoverage(reader, coverageOffset);
38001
+ return {
38002
+ apply(glyphs, pos) {
38003
+ const glyph = glyphs[pos];
38004
+ if (!glyph) return null;
38005
+ const index = coverage(glyph.id);
38006
+ if (index < 0 || index >= sequences.length) return null;
38007
+ const ids = sequences[index];
38008
+ if (ids.length === 0) return null;
38009
+ const codePoints = glyph.codePoints;
38010
+ const replacement = ids.map((id, i) => ({
38011
+ id,
38012
+ codePoints: i === 0 ? codePoints : []
38013
+ }));
38014
+ glyphs.splice(pos, 1, ...replacement);
38015
+ return ids.length;
38016
+ }
38017
+ };
38018
+ }
38019
+ function parseAlternateSubst(reader, offset) {
38020
+ reader.seek(offset);
38021
+ const format = reader.u16();
38022
+ if (format !== 1) return null;
38023
+ const coverageOffset = offset + reader.u16();
38024
+ const altSetCount = reader.u16();
38025
+ const altSetOffsets = new Array(altSetCount);
38026
+ for (let i = 0; i < altSetCount; i += 1) {
38027
+ altSetOffsets[i] = offset + reader.u16();
38028
+ }
38029
+ const altSets = altSetOffsets.map((setOffset) => {
38030
+ reader.seek(setOffset);
38031
+ const glyphCount = reader.u16();
38032
+ const ids = new Array(glyphCount);
38033
+ for (let i = 0; i < glyphCount; i += 1) ids[i] = reader.u16();
38034
+ return ids;
38035
+ });
38036
+ const coverage = parseCoverage(reader, coverageOffset);
38037
+ return {
38038
+ apply(glyphs, pos) {
38039
+ const glyph = glyphs[pos];
38040
+ if (!glyph) return null;
38041
+ const index = coverage(glyph.id);
38042
+ if (index < 0 || index >= altSets.length) return null;
38043
+ const alts = altSets[index];
38044
+ if (alts.length === 0) return null;
38045
+ glyph.id = alts[0];
38046
+ return 1;
38047
+ }
38048
+ };
38049
+ }
38050
+ function parseLigatureSubst(reader, offset) {
38051
+ reader.seek(offset);
38052
+ const format = reader.u16();
38053
+ if (format !== 1) return null;
38054
+ const coverageOffset = offset + reader.u16();
38055
+ const ligSetCount = reader.u16();
38056
+ const ligSetOffsets = new Array(ligSetCount);
38057
+ for (let i = 0; i < ligSetCount; i += 1) {
38058
+ ligSetOffsets[i] = offset + reader.u16();
38059
+ }
38060
+ const ligatureSets = ligSetOffsets.map((setOffset) => {
38061
+ reader.seek(setOffset);
38062
+ const ligCount = reader.u16();
38063
+ const ligOffsets = new Array(ligCount);
38064
+ for (let i = 0; i < ligCount; i += 1) {
38065
+ ligOffsets[i] = setOffset + reader.u16();
38066
+ }
38067
+ return ligOffsets.map((ligOffset) => {
38068
+ reader.seek(ligOffset);
38069
+ const ligatureGlyph = reader.u16();
38070
+ const componentCount = reader.u16();
38071
+ const components = new Array(Math.max(0, componentCount - 1));
38072
+ for (let i = 0; i < components.length; i += 1) {
38073
+ components[i] = reader.u16();
38074
+ }
38075
+ return { ligatureGlyph, components };
38076
+ });
38077
+ });
38078
+ const coverage = parseCoverage(reader, coverageOffset);
38079
+ return {
38080
+ apply(glyphs, pos) {
38081
+ const first = glyphs[pos];
38082
+ if (!first) return null;
38083
+ const setIndex = coverage(first.id);
38084
+ if (setIndex < 0 || setIndex >= ligatureSets.length) return null;
38085
+ for (const ligature of ligatureSets[setIndex]) {
38086
+ const { components } = ligature;
38087
+ if (pos + components.length > glyphs.length - 1) continue;
38088
+ let matched = true;
38089
+ for (let i = 0; i < components.length; i += 1) {
38090
+ if (glyphs[pos + 1 + i].id !== components[i]) {
38091
+ matched = false;
38092
+ break;
38093
+ }
38094
+ }
38095
+ if (!matched) continue;
38096
+ const total = components.length + 1;
38097
+ const mergedCodePoints = [];
38098
+ for (let i = 0; i < total; i += 1) {
38099
+ mergedCodePoints.push(...glyphs[pos + i].codePoints);
38100
+ }
38101
+ glyphs.splice(pos, total, {
38102
+ id: ligature.ligatureGlyph,
38103
+ codePoints: mergedCodePoints
38104
+ });
38105
+ return 1;
38106
+ }
38107
+ return null;
38108
+ }
38109
+ };
38110
+ }
38111
+ function readSubstLookupRecords(reader, count) {
38112
+ const records = new Array(count);
38113
+ for (let i = 0; i < count; i += 1) {
38114
+ const sequenceIndex = reader.u16();
38115
+ const lookupListIndex = reader.u16();
38116
+ records[i] = { sequenceIndex, lookupListIndex };
38117
+ }
38118
+ return records;
38119
+ }
38120
+ function applySubstRecords(glyphs, pos, records, engine) {
38121
+ for (const record of records) {
38122
+ engine.applyLookupIndexAt(
38123
+ record.lookupListIndex,
38124
+ glyphs,
38125
+ pos + record.sequenceIndex
38126
+ );
38127
+ }
38128
+ }
38129
+ function parseChainContextSubst(reader, offset) {
38130
+ reader.seek(offset);
38131
+ const format = reader.u16();
38132
+ if (format === 3) {
38133
+ const backtrackCount = reader.u16();
38134
+ const backtrackCoverageOffsets = new Array(backtrackCount);
38135
+ for (let i = 0; i < backtrackCount; i += 1) {
38136
+ backtrackCoverageOffsets[i] = offset + reader.u16();
38137
+ }
38138
+ const inputCount = reader.u16();
38139
+ const inputCoverageOffsets = new Array(inputCount);
38140
+ for (let i = 0; i < inputCount; i += 1) {
38141
+ inputCoverageOffsets[i] = offset + reader.u16();
38142
+ }
38143
+ const lookaheadCount = reader.u16();
38144
+ const lookaheadCoverageOffsets = new Array(lookaheadCount);
38145
+ for (let i = 0; i < lookaheadCount; i += 1) {
38146
+ lookaheadCoverageOffsets[i] = offset + reader.u16();
38147
+ }
38148
+ const substCount = reader.u16();
38149
+ const records = readSubstLookupRecords(reader, substCount);
38150
+ const backtrack = backtrackCoverageOffsets.map(
38151
+ (o) => parseCoverage(reader, o)
38152
+ );
38153
+ const input = inputCoverageOffsets.map((o) => parseCoverage(reader, o));
38154
+ const lookahead = lookaheadCoverageOffsets.map(
38155
+ (o) => parseCoverage(reader, o)
38156
+ );
38157
+ return {
38158
+ apply(glyphs, pos, engine) {
38159
+ if (input.length === 0) return null;
38160
+ if (pos + input.length > glyphs.length) return null;
38161
+ for (let i = 0; i < input.length; i += 1) {
38162
+ if (input[i](glyphs[pos + i].id) < 0) return null;
38163
+ }
38164
+ for (let i = 0; i < backtrack.length; i += 1) {
38165
+ const g = glyphs[pos - 1 - i];
38166
+ if (!g || backtrack[i](g.id) < 0) return null;
38167
+ }
38168
+ for (let i = 0; i < lookahead.length; i += 1) {
38169
+ const g = glyphs[pos + input.length + i];
38170
+ if (!g || lookahead[i](g.id) < 0) return null;
38171
+ }
38172
+ applySubstRecords(glyphs, pos, records, engine);
38173
+ return input.length;
38174
+ }
38175
+ };
38176
+ }
38177
+ if (format === 1) {
38178
+ const coverageOffset = offset + reader.u16();
38179
+ const ruleSetCount = reader.u16();
38180
+ const ruleSetOffsets = new Array(ruleSetCount);
38181
+ for (let i = 0; i < ruleSetCount; i += 1) {
38182
+ ruleSetOffsets[i] = offset + reader.u16();
38183
+ }
38184
+ const coverage = parseCoverage(reader, coverageOffset);
38185
+ const ruleSets = ruleSetOffsets.map(
38186
+ (setOffset) => parseChainRuleSet(reader, setOffset)
38187
+ );
38188
+ return {
38189
+ apply(glyphs, pos, engine) {
38190
+ const glyph = glyphs[pos];
38191
+ if (!glyph) return null;
38192
+ const index = coverage(glyph.id);
38193
+ if (index < 0 || index >= ruleSets.length) return null;
38194
+ return applyChainRules(ruleSets[index], glyphs, pos, engine, null);
38195
+ }
38196
+ };
38197
+ }
38198
+ if (format === 2) {
38199
+ const coverageOffset = offset + reader.u16();
38200
+ const backtrackClassOffset = offset + reader.u16();
38201
+ const inputClassOffset = offset + reader.u16();
38202
+ const lookaheadClassOffset = offset + reader.u16();
38203
+ const classSetCount = reader.u16();
38204
+ const classSetOffsets = new Array(classSetCount);
38205
+ for (let i = 0; i < classSetCount; i += 1) {
38206
+ const raw = reader.u16();
38207
+ classSetOffsets[i] = raw === 0 ? 0 : offset + raw;
38208
+ }
38209
+ const coverage = parseCoverage(reader, coverageOffset);
38210
+ const backtrackClass = parseClassDef(reader, backtrackClassOffset);
38211
+ const inputClass = parseClassDef(reader, inputClassOffset);
38212
+ const lookaheadClass = parseClassDef(reader, lookaheadClassOffset);
38213
+ const classSets = classSetOffsets.map(
38214
+ (setOffset) => setOffset === 0 ? [] : parseChainRuleSet(reader, setOffset)
38215
+ );
38216
+ return {
38217
+ apply(glyphs, pos, engine) {
38218
+ const glyph = glyphs[pos];
38219
+ if (!glyph) return null;
38220
+ if (coverage(glyph.id) < 0) return null;
38221
+ const classIndex = inputClass(glyph.id);
38222
+ const ruleSet = classSets[classIndex];
38223
+ if (!ruleSet) return null;
38224
+ return applyChainRules(ruleSet, glyphs, pos, engine, {
38225
+ backtrack: backtrackClass,
38226
+ input: inputClass,
38227
+ lookahead: lookaheadClass
38228
+ });
38229
+ }
38230
+ };
38231
+ }
38232
+ return null;
38233
+ }
38234
+ function parseChainRuleSet(reader, setOffset) {
38235
+ reader.seek(setOffset);
38236
+ const ruleCount = reader.u16();
38237
+ const ruleOffsets = new Array(ruleCount);
38238
+ for (let i = 0; i < ruleCount; i += 1) {
38239
+ ruleOffsets[i] = setOffset + reader.u16();
38240
+ }
38241
+ return ruleOffsets.map((ruleOffset) => {
38242
+ reader.seek(ruleOffset);
38243
+ const backtrackCount = reader.u16();
38244
+ const backtrack = new Array(backtrackCount);
38245
+ for (let i = 0; i < backtrackCount; i += 1) backtrack[i] = reader.u16();
38246
+ const inputCount = reader.u16();
38247
+ const input = new Array(Math.max(0, inputCount - 1));
38248
+ for (let i = 0; i < input.length; i += 1) input[i] = reader.u16();
38249
+ const lookaheadCount = reader.u16();
38250
+ const lookahead = new Array(lookaheadCount);
38251
+ for (let i = 0; i < lookaheadCount; i += 1) lookahead[i] = reader.u16();
38252
+ const substCount = reader.u16();
38253
+ const records = readSubstLookupRecords(reader, substCount);
38254
+ return { backtrack, input, lookahead, records };
38255
+ });
38256
+ }
38257
+ function applyChainRules(rules, glyphs, pos, engine, resolver) {
38258
+ const valueAt = (index, kind) => {
38259
+ const g = glyphs[index];
38260
+ if (!g) return null;
38261
+ if (!resolver) return g.id;
38262
+ if (kind === "b") return resolver.backtrack(g.id);
38263
+ if (kind === "l") return resolver.lookahead(g.id);
38264
+ return resolver.input(g.id);
38265
+ };
38266
+ for (const rule of rules) {
38267
+ const inputLen = rule.input.length + 1;
38268
+ if (pos + inputLen > glyphs.length) continue;
38269
+ let matched = true;
38270
+ for (let i = 0; i < rule.input.length; i += 1) {
38271
+ if (valueAt(pos + 1 + i, "i") !== rule.input[i]) {
38272
+ matched = false;
38273
+ break;
38274
+ }
38275
+ }
38276
+ if (!matched) continue;
38277
+ for (let i = 0; i < rule.backtrack.length; i += 1) {
38278
+ if (valueAt(pos - 1 - i, "b") !== rule.backtrack[i]) {
38279
+ matched = false;
38280
+ break;
38281
+ }
38282
+ }
38283
+ if (!matched) continue;
38284
+ for (let i = 0; i < rule.lookahead.length; i += 1) {
38285
+ if (valueAt(pos + inputLen + i, "l") !== rule.lookahead[i]) {
38286
+ matched = false;
38287
+ break;
38288
+ }
38289
+ }
38290
+ if (!matched) continue;
38291
+ applySubstRecords(glyphs, pos, rule.records, engine);
38292
+ return inputLen;
38293
+ }
38294
+ return null;
38295
+ }
38296
+ function parseSubtable(reader, offset, lookupType) {
38297
+ switch (lookupType) {
38298
+ case 1:
38299
+ return parseSingleSubst(reader, offset);
38300
+ case 2:
38301
+ return parseMultipleSubst(reader, offset);
38302
+ case 3:
38303
+ return parseAlternateSubst(reader, offset);
38304
+ case 4:
38305
+ return parseLigatureSubst(reader, offset);
38306
+ case 6:
38307
+ return parseChainContextSubst(reader, offset);
38308
+ case 7: {
38309
+ reader.seek(offset);
38310
+ const format = reader.u16();
38311
+ if (format !== 1) return null;
38312
+ const extensionType = reader.u16();
38313
+ const extensionOffset = offset + reader.u32();
38314
+ return parseSubtable(reader, extensionOffset, extensionType);
38315
+ }
38316
+ default:
38317
+ return null;
38318
+ }
38319
+ }
38320
+ class GsubTable {
38321
+ constructor(lookups, featureToLookups) {
38322
+ this.lookups = lookups;
38323
+ this.featureToLookups = featureToLookups;
38324
+ }
38325
+ /** Parses GSUB bytes (table-relative). Returns null on any structural failure. */
38326
+ static parse(bytes) {
38327
+ try {
38328
+ const reader = new BinaryReader(bytes);
38329
+ reader.seek(0);
38330
+ reader.skip(2);
38331
+ reader.skip(2);
38332
+ const scriptListOffset = reader.u16();
38333
+ const featureListOffset = reader.u16();
38334
+ const lookupListOffset = reader.u16();
38335
+ const featureRecords = parseFeatureList(reader, featureListOffset);
38336
+ const activeFeatureIndices = parseScriptList(reader, scriptListOffset);
38337
+ const lookups = parseLookupList(reader, lookupListOffset);
38338
+ const featureToLookups = /* @__PURE__ */ new Map();
38339
+ for (const featureIndex of activeFeatureIndices) {
38340
+ const record = featureRecords[featureIndex];
38341
+ if (!record) continue;
38342
+ const existing = featureToLookups.get(record.tag) ?? [];
38343
+ for (const lookupIndex of record.lookupIndices) {
38344
+ if (!existing.includes(lookupIndex)) existing.push(lookupIndex);
38345
+ }
38346
+ featureToLookups.set(record.tag, existing);
38347
+ }
38348
+ return new GsubTable(lookups, featureToLookups);
38349
+ } catch {
38350
+ return null;
38351
+ }
38352
+ }
38353
+ /** True when at least one of the requested feature tags exists in this font. */
38354
+ hasAnyFeature(tags) {
38355
+ return tags.some((tag) => this.featureToLookups.has(tag));
38356
+ }
38357
+ /** Ordered (ascending) unique lookup indices referenced by the given tags. */
38358
+ collectLookupIndices(tags) {
38359
+ const indices = /* @__PURE__ */ new Set();
38360
+ for (const tag of tags) {
38361
+ for (const lookupIndex of this.featureToLookups.get(tag) ?? []) {
38362
+ indices.add(lookupIndex);
38363
+ }
38364
+ }
38365
+ return Array.from(indices).sort((a, b) => a - b);
38366
+ }
38367
+ /**
38368
+ * Applies one lookup (by index) at a single position. Tries each subtable in
38369
+ * order; the first that matches wins. Returns the advance (output glyph count)
38370
+ * or null when nothing matched. Used both by the shaping walk and by nested
38371
+ * chaining-contextual substitution records.
38372
+ */
38373
+ applyLookupIndexAt(lookupIndex, glyphs, pos) {
38374
+ const lookup = this.lookups[lookupIndex];
38375
+ if (!lookup || pos < 0 || pos >= glyphs.length) return null;
38376
+ for (const subtable of lookup.subtables) {
38377
+ const advance = subtable.apply(glyphs, pos, this);
38378
+ if (advance !== null) return advance;
38379
+ }
38380
+ return null;
38381
+ }
38382
+ /** Applies the lookups for the given features across the whole buffer. */
38383
+ shape(glyphs, tags) {
38384
+ const lookupIndices = this.collectLookupIndices(tags);
38385
+ for (const lookupIndex of lookupIndices) {
38386
+ let pos = 0;
38387
+ while (pos < glyphs.length) {
38388
+ const advance = this.applyLookupIndexAt(lookupIndex, glyphs, pos);
38389
+ pos += advance && advance > 0 ? advance : 1;
38390
+ }
38391
+ }
38392
+ }
38393
+ }
38394
+ function parseFeatureList(reader, featureListOffset) {
38395
+ reader.seek(featureListOffset);
38396
+ const featureCount = reader.u16();
38397
+ const records = [];
38398
+ for (let i = 0; i < featureCount; i += 1) {
38399
+ const tag = readTag(reader);
38400
+ const offset = featureListOffset + reader.u16();
38401
+ records.push({ tag, offset });
38402
+ }
38403
+ return records.map(({ tag, offset }) => {
38404
+ reader.seek(offset);
38405
+ reader.skip(2);
38406
+ const lookupIndexCount = reader.u16();
38407
+ const lookupIndices = new Array(lookupIndexCount);
38408
+ for (let i = 0; i < lookupIndexCount; i += 1) {
38409
+ lookupIndices[i] = reader.u16();
38410
+ }
38411
+ return { tag, lookupIndices };
38412
+ });
38413
+ }
38414
+ function parseScriptList(reader, scriptListOffset) {
38415
+ reader.seek(scriptListOffset);
38416
+ const scriptCount = reader.u16();
38417
+ const scripts = [];
38418
+ for (let i = 0; i < scriptCount; i += 1) {
38419
+ const tag = readTag(reader);
38420
+ const offset = scriptListOffset + reader.u16();
38421
+ scripts.push({ tag, offset });
38422
+ }
38423
+ if (scripts.length === 0) return [];
38424
+ const chosen = scripts.find((s) => s.tag === "latn") ?? scripts.find((s) => s.tag === "DFLT") ?? scripts[0];
38425
+ reader.seek(chosen.offset);
38426
+ const defaultLangSysOffset = reader.u16();
38427
+ if (defaultLangSysOffset === 0) return [];
38428
+ reader.seek(chosen.offset + defaultLangSysOffset);
38429
+ reader.skip(2);
38430
+ const requiredFeatureIndex = reader.u16();
38431
+ const featureIndexCount = reader.u16();
38432
+ const featureIndices = [];
38433
+ if (requiredFeatureIndex !== 65535) {
38434
+ featureIndices.push(requiredFeatureIndex);
38435
+ }
38436
+ for (let i = 0; i < featureIndexCount; i += 1) {
38437
+ featureIndices.push(reader.u16());
38438
+ }
38439
+ return featureIndices;
38440
+ }
38441
+ function parseLookupList(reader, lookupListOffset) {
38442
+ reader.seek(lookupListOffset);
38443
+ const lookupCount = reader.u16();
38444
+ const lookupOffsets = new Array(lookupCount);
38445
+ for (let i = 0; i < lookupCount; i += 1) {
38446
+ lookupOffsets[i] = lookupListOffset + reader.u16();
38447
+ }
38448
+ return lookupOffsets.map((lookupOffset) => {
38449
+ reader.seek(lookupOffset);
38450
+ const lookupType = reader.u16();
38451
+ reader.skip(2);
38452
+ const subTableCount = reader.u16();
38453
+ const subtableOffsets = new Array(subTableCount);
38454
+ for (let i = 0; i < subTableCount; i += 1) {
38455
+ subtableOffsets[i] = lookupOffset + reader.u16();
38456
+ }
38457
+ const subtables = [];
38458
+ for (const subtableOffset of subtableOffsets) {
38459
+ try {
38460
+ const subtable = parseSubtable(reader, subtableOffset, lookupType);
38461
+ if (subtable) subtables.push(subtable);
38462
+ } catch {
38463
+ }
38464
+ }
38465
+ return { lookupType, subtables };
38466
+ });
38467
+ }
38468
+ class OpenTypeLayouter {
38469
+ constructor(font) {
38470
+ __publicField(this, "gsub");
38471
+ this.font = font;
38472
+ const raw = font.getRawTableData("GSUB");
38473
+ this.gsub = raw ? GsubTable.parse(raw) : null;
38474
+ }
38475
+ /** True when the font exposes a GSUB table the shaper could use. */
38476
+ get hasGsub() {
38477
+ return this.gsub !== null;
38478
+ }
38479
+ layout(text, features) {
38480
+ const buffer = [];
38481
+ for (const char of text) {
38482
+ const codePoint = char.codePointAt(0) ?? 65533;
38483
+ const glyphId = this.font.glyphForCodePoint(codePoint);
38484
+ buffer.push({
38485
+ id: glyphId,
38486
+ codePoints: glyphId === 0 ? [65533] : [codePoint]
38487
+ });
38488
+ }
38489
+ if (this.gsub && features && features.length > 0) {
38490
+ this.gsub.shape(buffer, features);
38491
+ }
38492
+ const glyphs = [];
38493
+ const positions = [];
38494
+ let advanceWidth = 0;
38495
+ for (const glyph of buffer) {
38496
+ const glyphAdvance = this.font.advanceWidthForGlyph(glyph.id);
38497
+ glyphs.push({
38498
+ id: glyph.id,
38499
+ codePoints: glyph.codePoints,
38500
+ advanceWidth: glyphAdvance
38501
+ });
38502
+ positions.push({ xAdvance: glyphAdvance });
38503
+ advanceWidth += glyphAdvance;
38504
+ }
38505
+ return { glyphs, positions, advanceWidth };
38506
+ }
38507
+ }
37816
38508
  class SimpleTextLayouter {
37817
38509
  constructor(font) {
37818
38510
  this.font = font;
@@ -38028,9 +38720,11 @@ function parseEmbeddedFontSync(sourceBytes) {
38028
38720
  return SfntFontProgram.parse(sfntBytes);
38029
38721
  }
38030
38722
  function createPdfEmbeddableFont(program) {
38723
+ const shaper = new OpenTypeLayouter(program);
38724
+ const layouter = shaper.hasGsub ? shaper : new SimpleTextLayouter(program);
38031
38725
  return {
38032
38726
  program,
38033
- layouter: new SimpleTextLayouter(program),
38727
+ layouter,
38034
38728
  subsetter: new TrueTypePdfFontSubsetter()
38035
38729
  };
38036
38730
  }
@@ -38078,8 +38772,8 @@ class PdfFontTable {
38078
38772
  * Unicode run, recording the glyphs used for subsetting. Returns `null` when
38079
38773
  * the run produced no glyphs so the caller can skip emission entirely.
38080
38774
  */
38081
- buildUnicodeShowCommand(state, text) {
38082
- const run = this.layoutUnicodeText(state, text);
38775
+ buildUnicodeShowCommand(state, text, features) {
38776
+ const run = this.layoutUnicodeText(state, text, features);
38083
38777
  const encoded = this.encodeUnicodeGlyphRun(state, run);
38084
38778
  if (encoded.length === 0) {
38085
38779
  return null;
@@ -38113,13 +38807,14 @@ class PdfFontTable {
38113
38807
  const resourceXml = fontResourceEntries.map((font, index) => `/${font.resourceName} ${fontObjectIds[index]} 0 R`).join(" ");
38114
38808
  return { resourceXml };
38115
38809
  }
38116
- layoutUnicodeText(state, text) {
38117
- const cached = state.layoutCache.get(text);
38810
+ layoutUnicodeText(state, text, features) {
38811
+ const cacheKey = `${(features == null ? void 0 : features.join(",")) ?? ""}\0${text}`;
38812
+ const cached = state.layoutCache.get(cacheKey);
38118
38813
  if (cached) {
38119
38814
  return cached;
38120
38815
  }
38121
- const run = state.font.layouter.layout(text);
38122
- state.layoutCache.set(text, run);
38816
+ const run = state.font.layouter.layout(text, features);
38817
+ state.layoutCache.set(cacheKey, run);
38123
38818
  return run;
38124
38819
  }
38125
38820
  encodeUnicodeGlyphRun(state, run) {