oasis-editor 0.0.98 → 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.
- package/dist/{OasisEditorApp-QVHCHhPa.js → OasisEditorApp-kxR9P2nm.js} +692 -10
- package/dist/core/textStyleMappings.d.ts +7 -0
- package/dist/export/pdf/writer/PdfFontTable.d.ts +1 -1
- package/dist/export/pdf/writer/pdfTypes.d.ts +5 -0
- package/dist/{index-DBzN897A.js → index-CNuDp_SC.js} +170 -123
- package/dist/oasis-editor.js +50 -50
- package/dist/oasis-editor.umd.cjs +3 -3
- package/dist/text/fonts/core/types.d.ts +6 -1
- package/dist/text/fonts/layout/OpenTypeLayouter.d.ts +21 -0
- package/dist/text/fonts/opentype/GsubTable.d.ts +41 -0
- package/package.json +1 -1
|
@@ -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
|
|
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
|
}
|
|
@@ -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,15 @@ 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(styles);
|
|
36710
36718
|
const baseTextOptions = {
|
|
36711
36719
|
fontSize: fontSizePt,
|
|
36712
36720
|
bold: styles.bold,
|
|
36713
36721
|
italic: styles.italic,
|
|
36714
36722
|
fontResourceName: fontFace.writerResourceName,
|
|
36715
36723
|
characterSpacing: styles.characterSpacing ?? 0,
|
|
36716
|
-
horizontalScale: styles.characterScale ?? 100
|
|
36724
|
+
horizontalScale: styles.characterScale ?? 100,
|
|
36725
|
+
fontFeatures
|
|
36717
36726
|
};
|
|
36718
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";
|
|
36719
36728
|
const offsetPt = pxToPt$1(1);
|
|
@@ -36728,6 +36737,30 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
|
|
|
36728
36737
|
color: "#BFBFBF"
|
|
36729
36738
|
});
|
|
36730
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
|
+
}
|
|
36731
36764
|
if (styles.textShadow) {
|
|
36732
36765
|
const ts = styles.textShadow;
|
|
36733
36766
|
const dirRad = ts.dirDeg * Math.PI / 180;
|
|
@@ -36748,6 +36781,18 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
|
|
|
36748
36781
|
color: "#808080"
|
|
36749
36782
|
});
|
|
36750
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
|
+
}
|
|
36751
36796
|
const textOutline = styles.textOutline;
|
|
36752
36797
|
writer.drawText(pageIndex, {
|
|
36753
36798
|
...baseTextOptions,
|
|
@@ -37739,7 +37784,11 @@ class PdfContentStream {
|
|
|
37739
37784
|
}
|
|
37740
37785
|
const fontResourceName = this.fonts.resolveAndMarkFontName(options);
|
|
37741
37786
|
const unicodeState = this.fonts.getUnicodeState(fontResourceName);
|
|
37742
|
-
const showCommand = unicodeState ? this.fonts.buildUnicodeShowCommand(
|
|
37787
|
+
const showCommand = unicodeState ? this.fonts.buildUnicodeShowCommand(
|
|
37788
|
+
unicodeState,
|
|
37789
|
+
options.text,
|
|
37790
|
+
options.fontFeatures
|
|
37791
|
+
) : `<${encodePdfHexString(options.text)}> Tj`;
|
|
37743
37792
|
if (showCommand === null) {
|
|
37744
37793
|
return;
|
|
37745
37794
|
}
|
|
@@ -37826,6 +37875,636 @@ class PdfContentStream {
|
|
|
37826
37875
|
);
|
|
37827
37876
|
}
|
|
37828
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
|
+
}
|
|
37829
38508
|
class SimpleTextLayouter {
|
|
37830
38509
|
constructor(font) {
|
|
37831
38510
|
this.font = font;
|
|
@@ -38041,9 +38720,11 @@ function parseEmbeddedFontSync(sourceBytes) {
|
|
|
38041
38720
|
return SfntFontProgram.parse(sfntBytes);
|
|
38042
38721
|
}
|
|
38043
38722
|
function createPdfEmbeddableFont(program) {
|
|
38723
|
+
const shaper = new OpenTypeLayouter(program);
|
|
38724
|
+
const layouter = shaper.hasGsub ? shaper : new SimpleTextLayouter(program);
|
|
38044
38725
|
return {
|
|
38045
38726
|
program,
|
|
38046
|
-
layouter
|
|
38727
|
+
layouter,
|
|
38047
38728
|
subsetter: new TrueTypePdfFontSubsetter()
|
|
38048
38729
|
};
|
|
38049
38730
|
}
|
|
@@ -38091,8 +38772,8 @@ class PdfFontTable {
|
|
|
38091
38772
|
* Unicode run, recording the glyphs used for subsetting. Returns `null` when
|
|
38092
38773
|
* the run produced no glyphs so the caller can skip emission entirely.
|
|
38093
38774
|
*/
|
|
38094
|
-
buildUnicodeShowCommand(state, text) {
|
|
38095
|
-
const run = this.layoutUnicodeText(state, text);
|
|
38775
|
+
buildUnicodeShowCommand(state, text, features) {
|
|
38776
|
+
const run = this.layoutUnicodeText(state, text, features);
|
|
38096
38777
|
const encoded = this.encodeUnicodeGlyphRun(state, run);
|
|
38097
38778
|
if (encoded.length === 0) {
|
|
38098
38779
|
return null;
|
|
@@ -38126,13 +38807,14 @@ class PdfFontTable {
|
|
|
38126
38807
|
const resourceXml = fontResourceEntries.map((font, index) => `/${font.resourceName} ${fontObjectIds[index]} 0 R`).join(" ");
|
|
38127
38808
|
return { resourceXml };
|
|
38128
38809
|
}
|
|
38129
|
-
layoutUnicodeText(state, text) {
|
|
38130
|
-
const
|
|
38810
|
+
layoutUnicodeText(state, text, features) {
|
|
38811
|
+
const cacheKey = `${(features == null ? void 0 : features.join(",")) ?? ""}\0${text}`;
|
|
38812
|
+
const cached = state.layoutCache.get(cacheKey);
|
|
38131
38813
|
if (cached) {
|
|
38132
38814
|
return cached;
|
|
38133
38815
|
}
|
|
38134
|
-
const run = state.font.layouter.layout(text);
|
|
38135
|
-
state.layoutCache.set(
|
|
38816
|
+
const run = state.font.layouter.layout(text, features);
|
|
38817
|
+
state.layoutCache.set(cacheKey, run);
|
|
38136
38818
|
return run;
|
|
38137
38819
|
}
|
|
38138
38820
|
encodeUnicodeGlyphRun(state, run) {
|