oasis-editor 0.0.98 → 0.0.100

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-DBzN897A.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-BuxYvaSC.js";
5
5
  function cloneStyle(style2) {
6
6
  return style2 ? { ...style2 } : void 0;
7
7
  }
@@ -36309,6 +36309,13 @@ async function drawFloatingImagesForParagraph(options) {
36309
36309
  }
36310
36310
  }
36311
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
+ }
36312
36319
  function drawFragmentHighlight(writer, pageIndex, line, fragment, originX, originY, styles) {
36313
36320
  if (!styles.highlight) {
36314
36321
  return;
@@ -36707,13 +36714,18 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
36707
36714
  styles.color ?? "#000000"
36708
36715
  );
36709
36716
  const paragraphAlign = resolveEffectiveParagraphStyle(paragraph.style, document2.styles).align ?? "left";
36717
+ const fontFeatures = resolveOpenTypeFeatureTags(
36718
+ styles,
36719
+ textStyleToFontSizePt(styles)
36720
+ );
36710
36721
  const baseTextOptions = {
36711
36722
  fontSize: fontSizePt,
36712
36723
  bold: styles.bold,
36713
36724
  italic: styles.italic,
36714
36725
  fontResourceName: fontFace.writerResourceName,
36715
36726
  characterSpacing: styles.characterSpacing ?? 0,
36716
- horizontalScale: styles.characterScale ?? 100
36727
+ horizontalScale: styles.characterScale ?? 100,
36728
+ fontFeatures
36717
36729
  };
36718
36730
  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";
36719
36731
  const offsetPt = pxToPt$1(1);
@@ -36728,6 +36740,30 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
36728
36740
  color: "#BFBFBF"
36729
36741
  });
36730
36742
  }
36743
+ if (styles.glow) {
36744
+ const gl = styles.glow;
36745
+ const r = pxToPt$1(gl.radiusPt * PX_PER_POINT) * 0.5;
36746
+ const glowColor = blendColorWithWhite(gl.color, (gl.alpha ?? 0.5) * 0.4);
36747
+ const dirs = [
36748
+ [r, 0],
36749
+ [-r, 0],
36750
+ [0, r],
36751
+ [0, -r],
36752
+ [r * 0.7, r * 0.7],
36753
+ [-r * 0.7, r * 0.7],
36754
+ [r * 0.7, -r * 0.7],
36755
+ [-r * 0.7, -r * 0.7]
36756
+ ];
36757
+ for (const [dx, dy] of dirs) {
36758
+ writer.drawText(pageIndex, {
36759
+ ...baseTextOptions,
36760
+ x: pxToPt$1(leftPx) + dx,
36761
+ y: pxToPt$1(baselineY) + dy,
36762
+ text: text2,
36763
+ color: glowColor
36764
+ });
36765
+ }
36766
+ }
36731
36767
  if (styles.textShadow) {
36732
36768
  const ts = styles.textShadow;
36733
36769
  const dirRad = ts.dirDeg * Math.PI / 180;
@@ -36748,6 +36784,18 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
36748
36784
  color: "#808080"
36749
36785
  });
36750
36786
  }
36787
+ if (styles.reflection) {
36788
+ const ref = styles.reflection;
36789
+ const avgAlpha = (ref.startAlpha + ref.endAlpha) / 2;
36790
+ const refColor = blendColorWithWhite(mainColor, avgAlpha * 0.6);
36791
+ writer.drawText(pageIndex, {
36792
+ ...baseTextOptions,
36793
+ x: pxToPt$1(leftPx),
36794
+ y: pxToPt$1(baselineY) + pxToPt$1(ref.distPt * PX_PER_POINT) + fontSizePt,
36795
+ text: text2,
36796
+ color: refColor
36797
+ });
36798
+ }
36751
36799
  const textOutline = styles.textOutline;
36752
36800
  writer.drawText(pageIndex, {
36753
36801
  ...baseTextOptions,
@@ -37739,7 +37787,11 @@ class PdfContentStream {
37739
37787
  }
37740
37788
  const fontResourceName = this.fonts.resolveAndMarkFontName(options);
37741
37789
  const unicodeState = this.fonts.getUnicodeState(fontResourceName);
37742
- const showCommand = unicodeState ? this.fonts.buildUnicodeShowCommand(unicodeState, options.text) : `<${encodePdfHexString(options.text)}> Tj`;
37790
+ const showCommand = unicodeState ? this.fonts.buildUnicodeShowCommand(
37791
+ unicodeState,
37792
+ options.text,
37793
+ options.fontFeatures
37794
+ ) : `<${encodePdfHexString(options.text)}> Tj`;
37743
37795
  if (showCommand === null) {
37744
37796
  return;
37745
37797
  }
@@ -37826,6 +37878,917 @@ class PdfContentStream {
37826
37878
  );
37827
37879
  }
37828
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 parseFeatureList(reader, featureListOffset) {
37948
+ reader.seek(featureListOffset);
37949
+ const featureCount = reader.u16();
37950
+ const records = [];
37951
+ for (let i = 0; i < featureCount; i += 1) {
37952
+ const tag = reader.tag();
37953
+ const offset = featureListOffset + reader.u16();
37954
+ records.push({ tag, offset });
37955
+ }
37956
+ return records.map(({ tag, offset }) => {
37957
+ reader.seek(offset);
37958
+ reader.skip(2);
37959
+ const lookupIndexCount = reader.u16();
37960
+ const lookupIndices = new Array(lookupIndexCount);
37961
+ for (let i = 0; i < lookupIndexCount; i += 1) {
37962
+ lookupIndices[i] = reader.u16();
37963
+ }
37964
+ return { tag, lookupIndices };
37965
+ });
37966
+ }
37967
+ function parseScriptList(reader, scriptListOffset) {
37968
+ reader.seek(scriptListOffset);
37969
+ const scriptCount = reader.u16();
37970
+ const scripts = [];
37971
+ for (let i = 0; i < scriptCount; i += 1) {
37972
+ const tag = reader.tag();
37973
+ const offset = scriptListOffset + reader.u16();
37974
+ scripts.push({ tag, offset });
37975
+ }
37976
+ if (scripts.length === 0) return [];
37977
+ const chosen = scripts.find((s) => s.tag === "latn") ?? scripts.find((s) => s.tag === "DFLT") ?? scripts[0];
37978
+ reader.seek(chosen.offset);
37979
+ const defaultLangSysOffset = reader.u16();
37980
+ if (defaultLangSysOffset === 0) return [];
37981
+ reader.seek(chosen.offset + defaultLangSysOffset);
37982
+ reader.skip(2);
37983
+ const requiredFeatureIndex = reader.u16();
37984
+ const featureIndexCount = reader.u16();
37985
+ const featureIndices = [];
37986
+ if (requiredFeatureIndex !== 65535) {
37987
+ featureIndices.push(requiredFeatureIndex);
37988
+ }
37989
+ for (let i = 0; i < featureIndexCount; i += 1) {
37990
+ featureIndices.push(reader.u16());
37991
+ }
37992
+ return featureIndices;
37993
+ }
37994
+ function parseLayoutTableHeader(reader) {
37995
+ reader.seek(0);
37996
+ reader.skip(2);
37997
+ reader.skip(2);
37998
+ const scriptListOffset = reader.u16();
37999
+ const featureListOffset = reader.u16();
38000
+ const lookupListOffset = reader.u16();
38001
+ const featureRecords = parseFeatureList(reader, featureListOffset);
38002
+ const activeFeatureIndices = parseScriptList(reader, scriptListOffset);
38003
+ const featureToLookups = /* @__PURE__ */ new Map();
38004
+ for (const featureIndex of activeFeatureIndices) {
38005
+ const record = featureRecords[featureIndex];
38006
+ if (!record) continue;
38007
+ const existing = featureToLookups.get(record.tag) ?? [];
38008
+ for (const lookupIndex of record.lookupIndices) {
38009
+ if (!existing.includes(lookupIndex)) existing.push(lookupIndex);
38010
+ }
38011
+ featureToLookups.set(record.tag, existing);
38012
+ }
38013
+ return { featureToLookups, lookupListOffset };
38014
+ }
38015
+ function collectLookupIndices(featureToLookups, tags) {
38016
+ const indices = /* @__PURE__ */ new Set();
38017
+ for (const tag of tags) {
38018
+ for (const lookupIndex of featureToLookups.get(tag) ?? []) {
38019
+ indices.add(lookupIndex);
38020
+ }
38021
+ }
38022
+ return Array.from(indices).sort((a, b) => a - b);
38023
+ }
38024
+ function parseSingleSubst(reader, offset) {
38025
+ reader.seek(offset);
38026
+ const format = reader.u16();
38027
+ const coverageOffset = offset + reader.u16();
38028
+ if (format === 1) {
38029
+ const delta = reader.i16();
38030
+ const coverage = parseCoverage(reader, coverageOffset);
38031
+ return {
38032
+ apply(glyphs, pos) {
38033
+ const glyph = glyphs[pos];
38034
+ if (!glyph || coverage(glyph.id) < 0) return null;
38035
+ glyph.id = glyph.id + delta & 65535;
38036
+ return 1;
38037
+ }
38038
+ };
38039
+ }
38040
+ if (format === 2) {
38041
+ const glyphCount = reader.u16();
38042
+ const substitutes = new Array(glyphCount);
38043
+ for (let i = 0; i < glyphCount; i += 1) {
38044
+ substitutes[i] = reader.u16();
38045
+ }
38046
+ const coverage = parseCoverage(reader, coverageOffset);
38047
+ return {
38048
+ apply(glyphs, pos) {
38049
+ const glyph = glyphs[pos];
38050
+ if (!glyph) return null;
38051
+ const index = coverage(glyph.id);
38052
+ if (index < 0 || index >= glyphCount) return null;
38053
+ glyph.id = substitutes[index];
38054
+ return 1;
38055
+ }
38056
+ };
38057
+ }
38058
+ return null;
38059
+ }
38060
+ function parseMultipleSubst(reader, offset) {
38061
+ reader.seek(offset);
38062
+ const format = reader.u16();
38063
+ if (format !== 1) return null;
38064
+ const coverageOffset = offset + reader.u16();
38065
+ const sequenceCount = reader.u16();
38066
+ const sequenceOffsets = new Array(sequenceCount);
38067
+ for (let i = 0; i < sequenceCount; i += 1) {
38068
+ sequenceOffsets[i] = offset + reader.u16();
38069
+ }
38070
+ const sequences = sequenceOffsets.map((seqOffset) => {
38071
+ reader.seek(seqOffset);
38072
+ const glyphCount = reader.u16();
38073
+ const ids = new Array(glyphCount);
38074
+ for (let i = 0; i < glyphCount; i += 1) ids[i] = reader.u16();
38075
+ return ids;
38076
+ });
38077
+ const coverage = parseCoverage(reader, coverageOffset);
38078
+ return {
38079
+ apply(glyphs, pos) {
38080
+ const glyph = glyphs[pos];
38081
+ if (!glyph) return null;
38082
+ const index = coverage(glyph.id);
38083
+ if (index < 0 || index >= sequences.length) return null;
38084
+ const ids = sequences[index];
38085
+ if (ids.length === 0) return null;
38086
+ const codePoints = glyph.codePoints;
38087
+ const replacement = ids.map((id, i) => ({
38088
+ id,
38089
+ codePoints: i === 0 ? codePoints : []
38090
+ }));
38091
+ glyphs.splice(pos, 1, ...replacement);
38092
+ return ids.length;
38093
+ }
38094
+ };
38095
+ }
38096
+ function parseAlternateSubst(reader, offset) {
38097
+ reader.seek(offset);
38098
+ const format = reader.u16();
38099
+ if (format !== 1) return null;
38100
+ const coverageOffset = offset + reader.u16();
38101
+ const altSetCount = reader.u16();
38102
+ const altSetOffsets = new Array(altSetCount);
38103
+ for (let i = 0; i < altSetCount; i += 1) {
38104
+ altSetOffsets[i] = offset + reader.u16();
38105
+ }
38106
+ const altSets = altSetOffsets.map((setOffset) => {
38107
+ reader.seek(setOffset);
38108
+ const glyphCount = reader.u16();
38109
+ const ids = new Array(glyphCount);
38110
+ for (let i = 0; i < glyphCount; i += 1) ids[i] = reader.u16();
38111
+ return ids;
38112
+ });
38113
+ const coverage = parseCoverage(reader, coverageOffset);
38114
+ return {
38115
+ apply(glyphs, pos) {
38116
+ const glyph = glyphs[pos];
38117
+ if (!glyph) return null;
38118
+ const index = coverage(glyph.id);
38119
+ if (index < 0 || index >= altSets.length) return null;
38120
+ const alts = altSets[index];
38121
+ if (alts.length === 0) return null;
38122
+ glyph.id = alts[0];
38123
+ return 1;
38124
+ }
38125
+ };
38126
+ }
38127
+ function parseLigatureSubst(reader, offset) {
38128
+ reader.seek(offset);
38129
+ const format = reader.u16();
38130
+ if (format !== 1) return null;
38131
+ const coverageOffset = offset + reader.u16();
38132
+ const ligSetCount = reader.u16();
38133
+ const ligSetOffsets = new Array(ligSetCount);
38134
+ for (let i = 0; i < ligSetCount; i += 1) {
38135
+ ligSetOffsets[i] = offset + reader.u16();
38136
+ }
38137
+ const ligatureSets = ligSetOffsets.map((setOffset) => {
38138
+ reader.seek(setOffset);
38139
+ const ligCount = reader.u16();
38140
+ const ligOffsets = new Array(ligCount);
38141
+ for (let i = 0; i < ligCount; i += 1) {
38142
+ ligOffsets[i] = setOffset + reader.u16();
38143
+ }
38144
+ return ligOffsets.map((ligOffset) => {
38145
+ reader.seek(ligOffset);
38146
+ const ligatureGlyph = reader.u16();
38147
+ const componentCount = reader.u16();
38148
+ const components = new Array(Math.max(0, componentCount - 1));
38149
+ for (let i = 0; i < components.length; i += 1) {
38150
+ components[i] = reader.u16();
38151
+ }
38152
+ return { ligatureGlyph, components };
38153
+ });
38154
+ });
38155
+ const coverage = parseCoverage(reader, coverageOffset);
38156
+ return {
38157
+ apply(glyphs, pos) {
38158
+ const first = glyphs[pos];
38159
+ if (!first) return null;
38160
+ const setIndex = coverage(first.id);
38161
+ if (setIndex < 0 || setIndex >= ligatureSets.length) return null;
38162
+ for (const ligature of ligatureSets[setIndex]) {
38163
+ const { components } = ligature;
38164
+ if (pos + components.length > glyphs.length - 1) continue;
38165
+ let matched = true;
38166
+ for (let i = 0; i < components.length; i += 1) {
38167
+ if (glyphs[pos + 1 + i].id !== components[i]) {
38168
+ matched = false;
38169
+ break;
38170
+ }
38171
+ }
38172
+ if (!matched) continue;
38173
+ const total = components.length + 1;
38174
+ const mergedCodePoints = [];
38175
+ for (let i = 0; i < total; i += 1) {
38176
+ mergedCodePoints.push(...glyphs[pos + i].codePoints);
38177
+ }
38178
+ glyphs.splice(pos, total, {
38179
+ id: ligature.ligatureGlyph,
38180
+ codePoints: mergedCodePoints
38181
+ });
38182
+ return 1;
38183
+ }
38184
+ return null;
38185
+ }
38186
+ };
38187
+ }
38188
+ function readSubstLookupRecords(reader, count) {
38189
+ const records = new Array(count);
38190
+ for (let i = 0; i < count; i += 1) {
38191
+ const sequenceIndex = reader.u16();
38192
+ const lookupListIndex = reader.u16();
38193
+ records[i] = { sequenceIndex, lookupListIndex };
38194
+ }
38195
+ return records;
38196
+ }
38197
+ function applySubstRecords(glyphs, pos, records, engine) {
38198
+ for (const record of records) {
38199
+ engine.applyLookupIndexAt(
38200
+ record.lookupListIndex,
38201
+ glyphs,
38202
+ pos + record.sequenceIndex
38203
+ );
38204
+ }
38205
+ }
38206
+ function parseChainContextSubst(reader, offset) {
38207
+ reader.seek(offset);
38208
+ const format = reader.u16();
38209
+ if (format === 3) {
38210
+ const backtrackCount = reader.u16();
38211
+ const backtrackCoverageOffsets = new Array(backtrackCount);
38212
+ for (let i = 0; i < backtrackCount; i += 1) {
38213
+ backtrackCoverageOffsets[i] = offset + reader.u16();
38214
+ }
38215
+ const inputCount = reader.u16();
38216
+ const inputCoverageOffsets = new Array(inputCount);
38217
+ for (let i = 0; i < inputCount; i += 1) {
38218
+ inputCoverageOffsets[i] = offset + reader.u16();
38219
+ }
38220
+ const lookaheadCount = reader.u16();
38221
+ const lookaheadCoverageOffsets = new Array(lookaheadCount);
38222
+ for (let i = 0; i < lookaheadCount; i += 1) {
38223
+ lookaheadCoverageOffsets[i] = offset + reader.u16();
38224
+ }
38225
+ const substCount = reader.u16();
38226
+ const records = readSubstLookupRecords(reader, substCount);
38227
+ const backtrack = backtrackCoverageOffsets.map(
38228
+ (o) => parseCoverage(reader, o)
38229
+ );
38230
+ const input = inputCoverageOffsets.map((o) => parseCoverage(reader, o));
38231
+ const lookahead = lookaheadCoverageOffsets.map(
38232
+ (o) => parseCoverage(reader, o)
38233
+ );
38234
+ return {
38235
+ apply(glyphs, pos, engine) {
38236
+ if (input.length === 0) return null;
38237
+ if (pos + input.length > glyphs.length) return null;
38238
+ for (let i = 0; i < input.length; i += 1) {
38239
+ if (input[i](glyphs[pos + i].id) < 0) return null;
38240
+ }
38241
+ for (let i = 0; i < backtrack.length; i += 1) {
38242
+ const g = glyphs[pos - 1 - i];
38243
+ if (!g || backtrack[i](g.id) < 0) return null;
38244
+ }
38245
+ for (let i = 0; i < lookahead.length; i += 1) {
38246
+ const g = glyphs[pos + input.length + i];
38247
+ if (!g || lookahead[i](g.id) < 0) return null;
38248
+ }
38249
+ applySubstRecords(glyphs, pos, records, engine);
38250
+ return input.length;
38251
+ }
38252
+ };
38253
+ }
38254
+ if (format === 1) {
38255
+ const coverageOffset = offset + reader.u16();
38256
+ const ruleSetCount = reader.u16();
38257
+ const ruleSetOffsets = new Array(ruleSetCount);
38258
+ for (let i = 0; i < ruleSetCount; i += 1) {
38259
+ ruleSetOffsets[i] = offset + reader.u16();
38260
+ }
38261
+ const coverage = parseCoverage(reader, coverageOffset);
38262
+ const ruleSets = ruleSetOffsets.map(
38263
+ (setOffset) => parseChainRuleSet(reader, setOffset)
38264
+ );
38265
+ return {
38266
+ apply(glyphs, pos, engine) {
38267
+ const glyph = glyphs[pos];
38268
+ if (!glyph) return null;
38269
+ const index = coverage(glyph.id);
38270
+ if (index < 0 || index >= ruleSets.length) return null;
38271
+ return applyChainRules(ruleSets[index], glyphs, pos, engine, null);
38272
+ }
38273
+ };
38274
+ }
38275
+ if (format === 2) {
38276
+ const coverageOffset = offset + reader.u16();
38277
+ const backtrackClassOffset = offset + reader.u16();
38278
+ const inputClassOffset = offset + reader.u16();
38279
+ const lookaheadClassOffset = offset + reader.u16();
38280
+ const classSetCount = reader.u16();
38281
+ const classSetOffsets = new Array(classSetCount);
38282
+ for (let i = 0; i < classSetCount; i += 1) {
38283
+ const raw = reader.u16();
38284
+ classSetOffsets[i] = raw === 0 ? 0 : offset + raw;
38285
+ }
38286
+ const coverage = parseCoverage(reader, coverageOffset);
38287
+ const backtrackClass = parseClassDef(reader, backtrackClassOffset);
38288
+ const inputClass = parseClassDef(reader, inputClassOffset);
38289
+ const lookaheadClass = parseClassDef(reader, lookaheadClassOffset);
38290
+ const classSets = classSetOffsets.map(
38291
+ (setOffset) => setOffset === 0 ? [] : parseChainRuleSet(reader, setOffset)
38292
+ );
38293
+ return {
38294
+ apply(glyphs, pos, engine) {
38295
+ const glyph = glyphs[pos];
38296
+ if (!glyph) return null;
38297
+ if (coverage(glyph.id) < 0) return null;
38298
+ const classIndex = inputClass(glyph.id);
38299
+ const ruleSet = classSets[classIndex];
38300
+ if (!ruleSet) return null;
38301
+ return applyChainRules(ruleSet, glyphs, pos, engine, {
38302
+ backtrack: backtrackClass,
38303
+ input: inputClass,
38304
+ lookahead: lookaheadClass
38305
+ });
38306
+ }
38307
+ };
38308
+ }
38309
+ return null;
38310
+ }
38311
+ function parseChainRuleSet(reader, setOffset) {
38312
+ reader.seek(setOffset);
38313
+ const ruleCount = reader.u16();
38314
+ const ruleOffsets = new Array(ruleCount);
38315
+ for (let i = 0; i < ruleCount; i += 1) {
38316
+ ruleOffsets[i] = setOffset + reader.u16();
38317
+ }
38318
+ return ruleOffsets.map((ruleOffset) => {
38319
+ reader.seek(ruleOffset);
38320
+ const backtrackCount = reader.u16();
38321
+ const backtrack = new Array(backtrackCount);
38322
+ for (let i = 0; i < backtrackCount; i += 1) backtrack[i] = reader.u16();
38323
+ const inputCount = reader.u16();
38324
+ const input = new Array(Math.max(0, inputCount - 1));
38325
+ for (let i = 0; i < input.length; i += 1) input[i] = reader.u16();
38326
+ const lookaheadCount = reader.u16();
38327
+ const lookahead = new Array(lookaheadCount);
38328
+ for (let i = 0; i < lookaheadCount; i += 1) lookahead[i] = reader.u16();
38329
+ const substCount = reader.u16();
38330
+ const records = readSubstLookupRecords(reader, substCount);
38331
+ return { backtrack, input, lookahead, records };
38332
+ });
38333
+ }
38334
+ function applyChainRules(rules, glyphs, pos, engine, resolver) {
38335
+ const valueAt = (index, kind) => {
38336
+ const g = glyphs[index];
38337
+ if (!g) return null;
38338
+ if (!resolver) return g.id;
38339
+ if (kind === "b") return resolver.backtrack(g.id);
38340
+ if (kind === "l") return resolver.lookahead(g.id);
38341
+ return resolver.input(g.id);
38342
+ };
38343
+ for (const rule of rules) {
38344
+ const inputLen = rule.input.length + 1;
38345
+ if (pos + inputLen > glyphs.length) continue;
38346
+ let matched = true;
38347
+ for (let i = 0; i < rule.input.length; i += 1) {
38348
+ if (valueAt(pos + 1 + i, "i") !== rule.input[i]) {
38349
+ matched = false;
38350
+ break;
38351
+ }
38352
+ }
38353
+ if (!matched) continue;
38354
+ for (let i = 0; i < rule.backtrack.length; i += 1) {
38355
+ if (valueAt(pos - 1 - i, "b") !== rule.backtrack[i]) {
38356
+ matched = false;
38357
+ break;
38358
+ }
38359
+ }
38360
+ if (!matched) continue;
38361
+ for (let i = 0; i < rule.lookahead.length; i += 1) {
38362
+ if (valueAt(pos + inputLen + i, "l") !== rule.lookahead[i]) {
38363
+ matched = false;
38364
+ break;
38365
+ }
38366
+ }
38367
+ if (!matched) continue;
38368
+ applySubstRecords(glyphs, pos, rule.records, engine);
38369
+ return inputLen;
38370
+ }
38371
+ return null;
38372
+ }
38373
+ function parseSubtable$1(reader, offset, lookupType) {
38374
+ switch (lookupType) {
38375
+ case 1:
38376
+ return parseSingleSubst(reader, offset);
38377
+ case 2:
38378
+ return parseMultipleSubst(reader, offset);
38379
+ case 3:
38380
+ return parseAlternateSubst(reader, offset);
38381
+ case 4:
38382
+ return parseLigatureSubst(reader, offset);
38383
+ case 6:
38384
+ return parseChainContextSubst(reader, offset);
38385
+ case 7: {
38386
+ reader.seek(offset);
38387
+ const format = reader.u16();
38388
+ if (format !== 1) return null;
38389
+ const extensionType = reader.u16();
38390
+ const extensionOffset = offset + reader.u32();
38391
+ return parseSubtable$1(reader, extensionOffset, extensionType);
38392
+ }
38393
+ default:
38394
+ return null;
38395
+ }
38396
+ }
38397
+ class GsubTable {
38398
+ constructor(lookups, featureToLookups) {
38399
+ this.lookups = lookups;
38400
+ this.featureToLookups = featureToLookups;
38401
+ }
38402
+ /** Parses GSUB bytes (table-relative). Returns null on any structural failure. */
38403
+ static parse(bytes) {
38404
+ try {
38405
+ const reader = new BinaryReader(bytes);
38406
+ const { featureToLookups, lookupListOffset } = parseLayoutTableHeader(reader);
38407
+ const lookups = parseLookupList$1(reader, lookupListOffset);
38408
+ return new GsubTable(lookups, featureToLookups);
38409
+ } catch {
38410
+ return null;
38411
+ }
38412
+ }
38413
+ /** True when at least one of the requested feature tags exists in this font. */
38414
+ hasAnyFeature(tags) {
38415
+ return tags.some((tag) => this.featureToLookups.has(tag));
38416
+ }
38417
+ /** Ordered (ascending) unique lookup indices referenced by the given tags. */
38418
+ collectLookupIndices(tags) {
38419
+ return collectLookupIndices(this.featureToLookups, tags);
38420
+ }
38421
+ /**
38422
+ * Applies one lookup (by index) at a single position. Tries each subtable in
38423
+ * order; the first that matches wins. Returns the advance (output glyph count)
38424
+ * or null when nothing matched. Used both by the shaping walk and by nested
38425
+ * chaining-contextual substitution records.
38426
+ */
38427
+ applyLookupIndexAt(lookupIndex, glyphs, pos) {
38428
+ const lookup = this.lookups[lookupIndex];
38429
+ if (!lookup || pos < 0 || pos >= glyphs.length) return null;
38430
+ for (const subtable of lookup.subtables) {
38431
+ const advance = subtable.apply(glyphs, pos, this);
38432
+ if (advance !== null) return advance;
38433
+ }
38434
+ return null;
38435
+ }
38436
+ /** Applies the lookups for the given features across the whole buffer. */
38437
+ shape(glyphs, tags) {
38438
+ const lookupIndices = this.collectLookupIndices(tags);
38439
+ for (const lookupIndex of lookupIndices) {
38440
+ let pos = 0;
38441
+ while (pos < glyphs.length) {
38442
+ const advance = this.applyLookupIndexAt(lookupIndex, glyphs, pos);
38443
+ pos += advance && advance > 0 ? advance : 1;
38444
+ }
38445
+ }
38446
+ }
38447
+ }
38448
+ function parseLookupList$1(reader, lookupListOffset) {
38449
+ reader.seek(lookupListOffset);
38450
+ const lookupCount = reader.u16();
38451
+ const lookupOffsets = new Array(lookupCount);
38452
+ for (let i = 0; i < lookupCount; i += 1) {
38453
+ lookupOffsets[i] = lookupListOffset + reader.u16();
38454
+ }
38455
+ return lookupOffsets.map((lookupOffset) => {
38456
+ reader.seek(lookupOffset);
38457
+ const lookupType = reader.u16();
38458
+ reader.skip(2);
38459
+ const subTableCount = reader.u16();
38460
+ const subtableOffsets = new Array(subTableCount);
38461
+ for (let i = 0; i < subTableCount; i += 1) {
38462
+ subtableOffsets[i] = lookupOffset + reader.u16();
38463
+ }
38464
+ const subtables = [];
38465
+ for (const subtableOffset of subtableOffsets) {
38466
+ try {
38467
+ const subtable = parseSubtable$1(reader, subtableOffset, lookupType);
38468
+ if (subtable) subtables.push(subtable);
38469
+ } catch {
38470
+ }
38471
+ }
38472
+ return { lookupType, subtables };
38473
+ });
38474
+ }
38475
+ const X_PLACEMENT = 1;
38476
+ const Y_PLACEMENT = 2;
38477
+ const X_ADVANCE = 4;
38478
+ const Y_ADVANCE = 8;
38479
+ const X_PLACEMENT_DEVICE = 16;
38480
+ const Y_PLACEMENT_DEVICE = 32;
38481
+ const X_ADVANCE_DEVICE = 64;
38482
+ const Y_ADVANCE_DEVICE = 128;
38483
+ function readValueRecordXAdvance(reader, valueFormat) {
38484
+ let xAdvance = 0;
38485
+ if (valueFormat & X_PLACEMENT) reader.i16();
38486
+ if (valueFormat & Y_PLACEMENT) reader.i16();
38487
+ if (valueFormat & X_ADVANCE) xAdvance = reader.i16();
38488
+ if (valueFormat & Y_ADVANCE) reader.i16();
38489
+ if (valueFormat & X_PLACEMENT_DEVICE) reader.u16();
38490
+ if (valueFormat & Y_PLACEMENT_DEVICE) reader.u16();
38491
+ if (valueFormat & X_ADVANCE_DEVICE) reader.u16();
38492
+ if (valueFormat & Y_ADVANCE_DEVICE) reader.u16();
38493
+ return xAdvance;
38494
+ }
38495
+ function valueRecordSize(valueFormat) {
38496
+ let count = 0;
38497
+ for (let bit = 0; bit < 8; bit += 1) {
38498
+ if (valueFormat & 1 << bit) count += 1;
38499
+ }
38500
+ return count * 2;
38501
+ }
38502
+ function parseSinglePos(reader, offset) {
38503
+ reader.seek(offset);
38504
+ const format = reader.u16();
38505
+ const coverageOffset = offset + reader.u16();
38506
+ if (format === 1) {
38507
+ const valueFormat = reader.u16();
38508
+ const xAdvance = readValueRecordXAdvance(reader, valueFormat);
38509
+ const coverage = parseCoverage(reader, coverageOffset);
38510
+ if (xAdvance === 0) return null;
38511
+ return {
38512
+ apply(glyphIds, advances, pos) {
38513
+ if (coverage(glyphIds[pos]) < 0) return null;
38514
+ advances[pos] += xAdvance;
38515
+ return 1;
38516
+ }
38517
+ };
38518
+ }
38519
+ if (format === 2) {
38520
+ const valueFormat = reader.u16();
38521
+ const valueCount = reader.u16();
38522
+ const recordSize = valueRecordSize(valueFormat);
38523
+ const recordsStart = reader.tell();
38524
+ const xAdvances = new Array(valueCount);
38525
+ for (let i = 0; i < valueCount; i += 1) {
38526
+ reader.seek(recordsStart + i * recordSize);
38527
+ xAdvances[i] = readValueRecordXAdvance(reader, valueFormat);
38528
+ }
38529
+ const coverage = parseCoverage(reader, coverageOffset);
38530
+ return {
38531
+ apply(glyphIds, advances, pos) {
38532
+ const index = coverage(glyphIds[pos]);
38533
+ if (index < 0 || index >= valueCount) return null;
38534
+ const delta = xAdvances[index];
38535
+ if (delta === 0) return null;
38536
+ advances[pos] += delta;
38537
+ return 1;
38538
+ }
38539
+ };
38540
+ }
38541
+ return null;
38542
+ }
38543
+ function parsePairPos(reader, offset) {
38544
+ reader.seek(offset);
38545
+ const format = reader.u16();
38546
+ const coverageOffset = offset + reader.u16();
38547
+ const valueFormat1 = reader.u16();
38548
+ const valueFormat2 = reader.u16();
38549
+ const hasSecondAdjustment = (valueFormat2 & X_ADVANCE) !== 0;
38550
+ if (format === 1) {
38551
+ const pairSetCount = reader.u16();
38552
+ const pairSetOffsets = new Array(pairSetCount);
38553
+ for (let i = 0; i < pairSetCount; i += 1) {
38554
+ pairSetOffsets[i] = offset + reader.u16();
38555
+ }
38556
+ const pairSets = pairSetOffsets.map(
38557
+ (setOffset) => {
38558
+ reader.seek(setOffset);
38559
+ const pairValueCount = reader.u16();
38560
+ const pairs = /* @__PURE__ */ new Map();
38561
+ for (let i = 0; i < pairValueCount; i += 1) {
38562
+ const secondGlyph = reader.u16();
38563
+ const value1XAdvance = readValueRecordXAdvance(reader, valueFormat1);
38564
+ const value2XAdvance = readValueRecordXAdvance(reader, valueFormat2);
38565
+ pairs.set(secondGlyph, {
38566
+ secondGlyph,
38567
+ value1XAdvance,
38568
+ value2XAdvance
38569
+ });
38570
+ }
38571
+ return pairs;
38572
+ }
38573
+ );
38574
+ const coverage = parseCoverage(reader, coverageOffset);
38575
+ return {
38576
+ apply(glyphIds, advances, pos) {
38577
+ const second = glyphIds[pos + 1];
38578
+ if (second === void 0) return null;
38579
+ const setIndex = coverage(glyphIds[pos]);
38580
+ if (setIndex < 0 || setIndex >= pairSets.length) return null;
38581
+ const pair = pairSets[setIndex].get(second);
38582
+ if (!pair) return null;
38583
+ advances[pos] += pair.value1XAdvance;
38584
+ advances[pos + 1] += pair.value2XAdvance;
38585
+ return hasSecondAdjustment ? 2 : 1;
38586
+ }
38587
+ };
38588
+ }
38589
+ if (format === 2) {
38590
+ const classDef1Offset = offset + reader.u16();
38591
+ const classDef2Offset = offset + reader.u16();
38592
+ const class1Count = reader.u16();
38593
+ const class2Count = reader.u16();
38594
+ const value1Size = valueRecordSize(valueFormat1);
38595
+ const value2Size = valueRecordSize(valueFormat2);
38596
+ const cellSize = value1Size + value2Size;
38597
+ const matrixStart = reader.tell();
38598
+ const value1 = new Array(class1Count * class2Count);
38599
+ const value2 = new Array(class1Count * class2Count);
38600
+ for (let c1 = 0; c1 < class1Count; c1 += 1) {
38601
+ for (let c2 = 0; c2 < class2Count; c2 += 1) {
38602
+ const cell = (c1 * class2Count + c2) * cellSize;
38603
+ reader.seek(matrixStart + cell);
38604
+ value1[c1 * class2Count + c2] = readValueRecordXAdvance(
38605
+ reader,
38606
+ valueFormat1
38607
+ );
38608
+ reader.seek(matrixStart + cell + value1Size);
38609
+ value2[c1 * class2Count + c2] = readValueRecordXAdvance(
38610
+ reader,
38611
+ valueFormat2
38612
+ );
38613
+ }
38614
+ }
38615
+ const coverage = parseCoverage(reader, coverageOffset);
38616
+ const classDef1 = parseClassDef(reader, classDef1Offset);
38617
+ const classDef2 = parseClassDef(reader, classDef2Offset);
38618
+ return {
38619
+ apply(glyphIds, advances, pos) {
38620
+ const first = glyphIds[pos];
38621
+ const second = glyphIds[pos + 1];
38622
+ if (second === void 0) return null;
38623
+ if (coverage(first) < 0) return null;
38624
+ const c1 = classDef1(first);
38625
+ const c2 = classDef2(second);
38626
+ if (c1 >= class1Count || c2 >= class2Count) return null;
38627
+ const cellIndex = c1 * class2Count + c2;
38628
+ const v1 = value1[cellIndex];
38629
+ const v2 = value2[cellIndex];
38630
+ if (v1 === 0 && v2 === 0) return null;
38631
+ advances[pos] += v1;
38632
+ advances[pos + 1] += v2;
38633
+ return hasSecondAdjustment ? 2 : 1;
38634
+ }
38635
+ };
38636
+ }
38637
+ return null;
38638
+ }
38639
+ function parseSubtable(reader, offset, lookupType) {
38640
+ switch (lookupType) {
38641
+ case 1:
38642
+ return parseSinglePos(reader, offset);
38643
+ case 2:
38644
+ return parsePairPos(reader, offset);
38645
+ case 9: {
38646
+ reader.seek(offset);
38647
+ const format = reader.u16();
38648
+ if (format !== 1) return null;
38649
+ const extensionType = reader.u16();
38650
+ const extensionOffset = offset + reader.u32();
38651
+ return parseSubtable(reader, extensionOffset, extensionType);
38652
+ }
38653
+ default:
38654
+ return null;
38655
+ }
38656
+ }
38657
+ function parseLookupList(reader, lookupListOffset) {
38658
+ reader.seek(lookupListOffset);
38659
+ const lookupCount = reader.u16();
38660
+ const lookupOffsets = new Array(lookupCount);
38661
+ for (let i = 0; i < lookupCount; i += 1) {
38662
+ lookupOffsets[i] = lookupListOffset + reader.u16();
38663
+ }
38664
+ return lookupOffsets.map((lookupOffset) => {
38665
+ reader.seek(lookupOffset);
38666
+ const lookupType = reader.u16();
38667
+ reader.skip(2);
38668
+ const subTableCount = reader.u16();
38669
+ const subtableOffsets = new Array(subTableCount);
38670
+ for (let i = 0; i < subTableCount; i += 1) {
38671
+ subtableOffsets[i] = lookupOffset + reader.u16();
38672
+ }
38673
+ const subtables = [];
38674
+ for (const subtableOffset of subtableOffsets) {
38675
+ try {
38676
+ const subtable = parseSubtable(reader, subtableOffset, lookupType);
38677
+ if (subtable) subtables.push(subtable);
38678
+ } catch {
38679
+ }
38680
+ }
38681
+ return { lookupType, subtables };
38682
+ });
38683
+ }
38684
+ class GposTable {
38685
+ constructor(lookups, featureToLookups) {
38686
+ this.lookups = lookups;
38687
+ this.featureToLookups = featureToLookups;
38688
+ }
38689
+ /** Parses GPOS bytes (table-relative). Returns null on any structural failure. */
38690
+ static parse(bytes) {
38691
+ try {
38692
+ const reader = new BinaryReader(bytes);
38693
+ const { featureToLookups, lookupListOffset } = parseLayoutTableHeader(reader);
38694
+ const lookups = parseLookupList(reader, lookupListOffset);
38695
+ return new GposTable(lookups, featureToLookups);
38696
+ } catch {
38697
+ return null;
38698
+ }
38699
+ }
38700
+ /** True when at least one of the requested feature tags exists in this font. */
38701
+ hasAnyFeature(tags) {
38702
+ return tags.some((tag) => this.featureToLookups.has(tag));
38703
+ }
38704
+ /**
38705
+ * Applies the positioning lookups for the given features across `advances`
38706
+ * (font design units), mutating it in place. `glyphIds[i]` is the glyph at
38707
+ * advance slot `i`; the arrays are parallel and equal length.
38708
+ */
38709
+ position(glyphIds, advances, tags) {
38710
+ const lookupIndices = collectLookupIndices(this.featureToLookups, tags);
38711
+ for (const lookupIndex of lookupIndices) {
38712
+ const lookup = this.lookups[lookupIndex];
38713
+ if (!lookup) continue;
38714
+ let pos = 0;
38715
+ while (pos < glyphIds.length) {
38716
+ let advance = null;
38717
+ for (const subtable of lookup.subtables) {
38718
+ advance = subtable.apply(glyphIds, advances, pos);
38719
+ if (advance !== null) break;
38720
+ }
38721
+ pos += advance && advance > 0 ? advance : 1;
38722
+ }
38723
+ }
38724
+ }
38725
+ }
38726
+ const GPOS_FEATURE_TAGS = /* @__PURE__ */ new Set(["kern"]);
38727
+ class OpenTypeLayouter {
38728
+ constructor(font) {
38729
+ __publicField(this, "gsub");
38730
+ __publicField(this, "gpos");
38731
+ this.font = font;
38732
+ const gsubRaw = font.getRawTableData("GSUB");
38733
+ this.gsub = gsubRaw ? GsubTable.parse(gsubRaw) : null;
38734
+ const gposRaw = font.getRawTableData("GPOS");
38735
+ this.gpos = gposRaw ? GposTable.parse(gposRaw) : null;
38736
+ }
38737
+ /** True when the font exposes a GSUB table the substitution shaper could use. */
38738
+ get hasGsub() {
38739
+ return this.gsub !== null;
38740
+ }
38741
+ /** True when the font exposes a GPOS table the kerning shaper could use. */
38742
+ get hasGpos() {
38743
+ return this.gpos !== null;
38744
+ }
38745
+ /** True when either shaping table is present (so this layouter adds value). */
38746
+ get hasShaping() {
38747
+ return this.gsub !== null || this.gpos !== null;
38748
+ }
38749
+ layout(text, features) {
38750
+ const buffer = [];
38751
+ for (const char of text) {
38752
+ const codePoint = char.codePointAt(0) ?? 65533;
38753
+ const glyphId = this.font.glyphForCodePoint(codePoint);
38754
+ buffer.push({
38755
+ id: glyphId,
38756
+ codePoints: glyphId === 0 ? [65533] : [codePoint]
38757
+ });
38758
+ }
38759
+ const gsubTags = (features == null ? void 0 : features.filter((tag) => !GPOS_FEATURE_TAGS.has(tag))) ?? [];
38760
+ const gposTags = (features == null ? void 0 : features.filter((tag) => GPOS_FEATURE_TAGS.has(tag))) ?? [];
38761
+ if (this.gsub && gsubTags.length > 0) {
38762
+ this.gsub.shape(buffer, gsubTags);
38763
+ }
38764
+ const advances = buffer.map(
38765
+ (glyph) => this.font.advanceWidthForGlyph(glyph.id)
38766
+ );
38767
+ if (this.gpos && gposTags.length > 0) {
38768
+ this.gpos.position(
38769
+ buffer.map((glyph) => glyph.id),
38770
+ advances,
38771
+ gposTags
38772
+ );
38773
+ }
38774
+ const glyphs = [];
38775
+ const positions = [];
38776
+ let advanceWidth = 0;
38777
+ for (let i = 0; i < buffer.length; i += 1) {
38778
+ const glyph = buffer[i];
38779
+ const nominalAdvance = this.font.advanceWidthForGlyph(glyph.id);
38780
+ const positionedAdvance = advances[i];
38781
+ glyphs.push({
38782
+ id: glyph.id,
38783
+ codePoints: glyph.codePoints,
38784
+ advanceWidth: nominalAdvance
38785
+ });
38786
+ positions.push({ xAdvance: positionedAdvance });
38787
+ advanceWidth += positionedAdvance;
38788
+ }
38789
+ return { glyphs, positions, advanceWidth };
38790
+ }
38791
+ }
37829
38792
  class SimpleTextLayouter {
37830
38793
  constructor(font) {
37831
38794
  this.font = font;
@@ -38041,9 +39004,11 @@ function parseEmbeddedFontSync(sourceBytes) {
38041
39004
  return SfntFontProgram.parse(sfntBytes);
38042
39005
  }
38043
39006
  function createPdfEmbeddableFont(program) {
39007
+ const shaper = new OpenTypeLayouter(program);
39008
+ const layouter = shaper.hasShaping ? shaper : new SimpleTextLayouter(program);
38044
39009
  return {
38045
39010
  program,
38046
- layouter: new SimpleTextLayouter(program),
39011
+ layouter,
38047
39012
  subsetter: new TrueTypePdfFontSubsetter()
38048
39013
  };
38049
39014
  }
@@ -38091,8 +39056,8 @@ class PdfFontTable {
38091
39056
  * Unicode run, recording the glyphs used for subsetting. Returns `null` when
38092
39057
  * the run produced no glyphs so the caller can skip emission entirely.
38093
39058
  */
38094
- buildUnicodeShowCommand(state, text) {
38095
- const run = this.layoutUnicodeText(state, text);
39059
+ buildUnicodeShowCommand(state, text, features) {
39060
+ const run = this.layoutUnicodeText(state, text, features);
38096
39061
  const encoded = this.encodeUnicodeGlyphRun(state, run);
38097
39062
  if (encoded.length === 0) {
38098
39063
  return null;
@@ -38126,13 +39091,14 @@ class PdfFontTable {
38126
39091
  const resourceXml = fontResourceEntries.map((font, index) => `/${font.resourceName} ${fontObjectIds[index]} 0 R`).join(" ");
38127
39092
  return { resourceXml };
38128
39093
  }
38129
- layoutUnicodeText(state, text) {
38130
- const cached = state.layoutCache.get(text);
39094
+ layoutUnicodeText(state, text, features) {
39095
+ const cacheKey = `${(features == null ? void 0 : features.join(",")) ?? ""}\0${text}`;
39096
+ const cached = state.layoutCache.get(cacheKey);
38131
39097
  if (cached) {
38132
39098
  return cached;
38133
39099
  }
38134
- const run = state.font.layouter.layout(text);
38135
- state.layoutCache.set(text, run);
39100
+ const run = state.font.layouter.layout(text, features);
39101
+ state.layoutCache.set(cacheKey, run);
38136
39102
  return run;
38137
39103
  }
38138
39104
  encodeUnicodeGlyphRun(state, run) {