oasis-editor 0.0.97 → 0.0.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{OasisEditorApp-m8k4gBW1.js → OasisEditorApp-kxR9P2nm.js} +707 -12
- package/dist/assets/{importDocxWorker-BV6LezJZ.js → importDocxWorker-BulqJDJj.js} +1 -1
- package/dist/core/model/types/documentComments.d.ts +5 -0
- package/dist/core/model/types/styles.d.ts +11 -0
- 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/import/docx/commentsXml.d.ts +2 -0
- package/dist/{index-BiTnt_yD.js → index-CNuDp_SC.js} +197 -124
- package/dist/oasis-editor.js +50 -50
- package/dist/oasis-editor.umd.cjs +4 -4
- 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/dist/ui/canvas/canvasFontResolution.d.ts +28 -0
- package/dist/ui/canvas/canvasParagraphPainter.d.ts +1 -1
- 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
|
}
|
|
@@ -33105,6 +33105,7 @@ function serializeBookmarkEvent(event) {
|
|
|
33105
33105
|
)}"${col}/>`;
|
|
33106
33106
|
}
|
|
33107
33107
|
const WORD15_NS = "http://schemas.microsoft.com/office/word/2012/wordml";
|
|
33108
|
+
const WORD16DU_NS = "http://schemas.microsoft.com/office/word/2023/wordml/word16du";
|
|
33108
33109
|
function buildCommentExportPlan(document2) {
|
|
33109
33110
|
const registry = document2.comments;
|
|
33110
33111
|
if (!registry || registry.order.length === 0) {
|
|
@@ -33180,7 +33181,7 @@ function serializeCommentRangeEvent(event) {
|
|
|
33180
33181
|
return `<w:r><w:commentReference w:id="${event.wId}"/></w:r>`;
|
|
33181
33182
|
}
|
|
33182
33183
|
}
|
|
33183
|
-
const COMMENTS_XMLNS = `xmlns:w="${WORD_NS$1}" xmlns:w14="${WORD14_NS}" xmlns:w15="${WORD15_NS}"`;
|
|
33184
|
+
const COMMENTS_XMLNS = `xmlns:w="${WORD_NS$1}" xmlns:w14="${WORD14_NS}" xmlns:w15="${WORD15_NS}" xmlns:w16du="${WORD16DU_NS}"`;
|
|
33184
33185
|
function serializeCommentBody(text) {
|
|
33185
33186
|
const lines = text.split("\n");
|
|
33186
33187
|
const runs = lines.map((line, i) => {
|
|
@@ -33192,8 +33193,9 @@ function serializeCommentBody(text) {
|
|
|
33192
33193
|
function buildCommentsPartXml(plan) {
|
|
33193
33194
|
const body = plan.comments.map(({ comment, wId, paraId }) => {
|
|
33194
33195
|
const dateAttr = comment.date !== void 0 ? ` w:date="${new Date(comment.date).toISOString().replace(/\.\d{3}Z$/, "Z")}"` : "";
|
|
33196
|
+
const dateUtcAttr = comment.dateUtc !== void 0 ? ` w16du:dateUtc="${new Date(comment.dateUtc).toISOString().replace(/\.\d{3}Z$/, "Z")}"` : "";
|
|
33195
33197
|
const initialsAttr = comment.initials ? ` w:initials="${escapeXml(comment.initials)}"` : "";
|
|
33196
|
-
return `<w:comment w:id="${wId}" w:author="${escapeXml(comment.author)}"${dateAttr}${initialsAttr}><w:p w14:paraId="${paraId}">${serializeCommentBody(comment.text)}</w:p></w:comment>`;
|
|
33198
|
+
return `<w:comment w:id="${wId}" w:author="${escapeXml(comment.author)}"${dateAttr}${dateUtcAttr}${initialsAttr}><w:p w14:paraId="${paraId}">${serializeCommentBody(comment.text)}</w:p></w:comment>`;
|
|
33197
33199
|
}).join("");
|
|
33198
33200
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:comments ${COMMENTS_XMLNS}>${body}</w:comments>`;
|
|
33199
33201
|
}
|
|
@@ -33795,6 +33797,8 @@ function materializeRunStyle(run, paragraphStyleId, styles) {
|
|
|
33795
33797
|
textShadow: effective.textShadow,
|
|
33796
33798
|
glow: effective.glow,
|
|
33797
33799
|
reflection: effective.reflection,
|
|
33800
|
+
scene3dXml: effective.scene3dXml,
|
|
33801
|
+
props3dXml: effective.props3dXml,
|
|
33798
33802
|
highlight: effective.highlight,
|
|
33799
33803
|
shading: effective.shading,
|
|
33800
33804
|
language: effective.language
|
|
@@ -34036,6 +34040,9 @@ function serializeReflectionMC(reflection) {
|
|
|
34036
34040
|
const distEmu = Math.round(Math.max(0, reflection.distPt) * 12700);
|
|
34037
34041
|
return `<mc:AlternateContent xmlns:mc="${MC_NS}"><mc:Choice Requires="w14"><w14:reflection w14:blurRad="${blurEmu}" w14:stA="${stA}" w14:stPos="${stPos}" w14:endA="${endA}" w14:endPos="${endPos}" w14:dist="${distEmu}" w14:dir="5400000" w14:fadeDir="5400000" w14:sx="100000" w14:sy="-100000" w14:kx="0" w14:ky="0" w14:algn="bl"/></mc:Choice><mc:Fallback/></mc:AlternateContent>`;
|
|
34038
34042
|
}
|
|
34043
|
+
function serialize3dBlobMC(blob) {
|
|
34044
|
+
return `<mc:AlternateContent xmlns:mc="${MC_NS}"><mc:Choice Requires="w14">${blob}</mc:Choice><mc:Fallback/></mc:AlternateContent>`;
|
|
34045
|
+
}
|
|
34039
34046
|
function pointsToSignedTwips(value) {
|
|
34040
34047
|
if (value === void 0 || value === null || !Number.isFinite(value)) {
|
|
34041
34048
|
return null;
|
|
@@ -34222,6 +34229,12 @@ function serializeRunProperties(styles) {
|
|
|
34222
34229
|
if (styles.reflection) {
|
|
34223
34230
|
parts.push(serializeReflectionMC(styles.reflection));
|
|
34224
34231
|
}
|
|
34232
|
+
if (styles.scene3dXml) {
|
|
34233
|
+
parts.push(serialize3dBlobMC(styles.scene3dXml));
|
|
34234
|
+
}
|
|
34235
|
+
if (styles.props3dXml) {
|
|
34236
|
+
parts.push(serialize3dBlobMC(styles.props3dXml));
|
|
34237
|
+
}
|
|
34225
34238
|
if (styles.highlight) {
|
|
34226
34239
|
parts.push(
|
|
34227
34240
|
`<w:highlight w:val="${escapeXml(normalizeHighlightForDocx(styles.highlight))}"/>`
|
|
@@ -36296,6 +36309,13 @@ async function drawFloatingImagesForParagraph(options) {
|
|
|
36296
36309
|
}
|
|
36297
36310
|
}
|
|
36298
36311
|
}
|
|
36312
|
+
function blendColorWithWhite(hex, alpha) {
|
|
36313
|
+
const a = Math.max(0, Math.min(1, alpha));
|
|
36314
|
+
const r = Math.round(255 + (Number.parseInt(hex.slice(1, 3), 16) - 255) * a);
|
|
36315
|
+
const g = Math.round(255 + (Number.parseInt(hex.slice(3, 5), 16) - 255) * a);
|
|
36316
|
+
const b = Math.round(255 + (Number.parseInt(hex.slice(5, 7), 16) - 255) * a);
|
|
36317
|
+
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
|
36318
|
+
}
|
|
36299
36319
|
function drawFragmentHighlight(writer, pageIndex, line, fragment, originX, originY, styles) {
|
|
36300
36320
|
if (!styles.highlight) {
|
|
36301
36321
|
return;
|
|
@@ -36694,13 +36714,15 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
|
|
|
36694
36714
|
styles.color ?? "#000000"
|
|
36695
36715
|
);
|
|
36696
36716
|
const paragraphAlign = resolveEffectiveParagraphStyle(paragraph.style, document2.styles).align ?? "left";
|
|
36717
|
+
const fontFeatures = resolveOpenTypeFeatureTags(styles);
|
|
36697
36718
|
const baseTextOptions = {
|
|
36698
36719
|
fontSize: fontSizePt,
|
|
36699
36720
|
bold: styles.bold,
|
|
36700
36721
|
italic: styles.italic,
|
|
36701
36722
|
fontResourceName: fontFace.writerResourceName,
|
|
36702
36723
|
characterSpacing: styles.characterSpacing ?? 0,
|
|
36703
|
-
horizontalScale: styles.characterScale ?? 100
|
|
36724
|
+
horizontalScale: styles.characterScale ?? 100,
|
|
36725
|
+
fontFeatures
|
|
36704
36726
|
};
|
|
36705
36727
|
const mainColor = ((_b = styles.textFill) == null ? void 0 : _b.type) === "solid" ? styles.textFill.color : ((_c = styles.textFill) == null ? void 0 : _c.type) === "gradient" && styles.textFill.stops[0] ? styles.textFill.stops[0].color : styles.color ?? "#000000";
|
|
36706
36728
|
const offsetPt = pxToPt$1(1);
|
|
@@ -36715,6 +36737,30 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
|
|
|
36715
36737
|
color: "#BFBFBF"
|
|
36716
36738
|
});
|
|
36717
36739
|
}
|
|
36740
|
+
if (styles.glow) {
|
|
36741
|
+
const gl = styles.glow;
|
|
36742
|
+
const r = pxToPt$1(gl.radiusPt * PX_PER_POINT) * 0.5;
|
|
36743
|
+
const glowColor = blendColorWithWhite(gl.color, (gl.alpha ?? 0.5) * 0.4);
|
|
36744
|
+
const dirs = [
|
|
36745
|
+
[r, 0],
|
|
36746
|
+
[-r, 0],
|
|
36747
|
+
[0, r],
|
|
36748
|
+
[0, -r],
|
|
36749
|
+
[r * 0.7, r * 0.7],
|
|
36750
|
+
[-r * 0.7, r * 0.7],
|
|
36751
|
+
[r * 0.7, -r * 0.7],
|
|
36752
|
+
[-r * 0.7, -r * 0.7]
|
|
36753
|
+
];
|
|
36754
|
+
for (const [dx, dy] of dirs) {
|
|
36755
|
+
writer.drawText(pageIndex, {
|
|
36756
|
+
...baseTextOptions,
|
|
36757
|
+
x: pxToPt$1(leftPx) + dx,
|
|
36758
|
+
y: pxToPt$1(baselineY) + dy,
|
|
36759
|
+
text: text2,
|
|
36760
|
+
color: glowColor
|
|
36761
|
+
});
|
|
36762
|
+
}
|
|
36763
|
+
}
|
|
36718
36764
|
if (styles.textShadow) {
|
|
36719
36765
|
const ts = styles.textShadow;
|
|
36720
36766
|
const dirRad = ts.dirDeg * Math.PI / 180;
|
|
@@ -36735,6 +36781,18 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
|
|
|
36735
36781
|
color: "#808080"
|
|
36736
36782
|
});
|
|
36737
36783
|
}
|
|
36784
|
+
if (styles.reflection) {
|
|
36785
|
+
const ref = styles.reflection;
|
|
36786
|
+
const avgAlpha = (ref.startAlpha + ref.endAlpha) / 2;
|
|
36787
|
+
const refColor = blendColorWithWhite(mainColor, avgAlpha * 0.6);
|
|
36788
|
+
writer.drawText(pageIndex, {
|
|
36789
|
+
...baseTextOptions,
|
|
36790
|
+
x: pxToPt$1(leftPx),
|
|
36791
|
+
y: pxToPt$1(baselineY) + pxToPt$1(ref.distPt * PX_PER_POINT) + fontSizePt,
|
|
36792
|
+
text: text2,
|
|
36793
|
+
color: refColor
|
|
36794
|
+
});
|
|
36795
|
+
}
|
|
36738
36796
|
const textOutline = styles.textOutline;
|
|
36739
36797
|
writer.drawText(pageIndex, {
|
|
36740
36798
|
...baseTextOptions,
|
|
@@ -37726,7 +37784,11 @@ class PdfContentStream {
|
|
|
37726
37784
|
}
|
|
37727
37785
|
const fontResourceName = this.fonts.resolveAndMarkFontName(options);
|
|
37728
37786
|
const unicodeState = this.fonts.getUnicodeState(fontResourceName);
|
|
37729
|
-
const showCommand = unicodeState ? this.fonts.buildUnicodeShowCommand(
|
|
37787
|
+
const showCommand = unicodeState ? this.fonts.buildUnicodeShowCommand(
|
|
37788
|
+
unicodeState,
|
|
37789
|
+
options.text,
|
|
37790
|
+
options.fontFeatures
|
|
37791
|
+
) : `<${encodePdfHexString(options.text)}> Tj`;
|
|
37730
37792
|
if (showCommand === null) {
|
|
37731
37793
|
return;
|
|
37732
37794
|
}
|
|
@@ -37813,6 +37875,636 @@ class PdfContentStream {
|
|
|
37813
37875
|
);
|
|
37814
37876
|
}
|
|
37815
37877
|
}
|
|
37878
|
+
function readTag(reader) {
|
|
37879
|
+
return reader.tag();
|
|
37880
|
+
}
|
|
37881
|
+
function parseCoverage(reader, offset) {
|
|
37882
|
+
reader.seek(offset);
|
|
37883
|
+
const format = reader.u16();
|
|
37884
|
+
if (format === 1) {
|
|
37885
|
+
const count = reader.u16();
|
|
37886
|
+
const map = /* @__PURE__ */ new Map();
|
|
37887
|
+
for (let i = 0; i < count; i += 1) {
|
|
37888
|
+
map.set(reader.u16(), i);
|
|
37889
|
+
}
|
|
37890
|
+
return (glyphId) => map.get(glyphId) ?? -1;
|
|
37891
|
+
}
|
|
37892
|
+
if (format === 2) {
|
|
37893
|
+
const rangeCount = reader.u16();
|
|
37894
|
+
const ranges = [];
|
|
37895
|
+
for (let i = 0; i < rangeCount; i += 1) {
|
|
37896
|
+
const start = reader.u16();
|
|
37897
|
+
const end = reader.u16();
|
|
37898
|
+
const startIndex = reader.u16();
|
|
37899
|
+
ranges.push({ start, end, startIndex });
|
|
37900
|
+
}
|
|
37901
|
+
return (glyphId) => {
|
|
37902
|
+
for (const range of ranges) {
|
|
37903
|
+
if (glyphId >= range.start && glyphId <= range.end) {
|
|
37904
|
+
return range.startIndex + (glyphId - range.start);
|
|
37905
|
+
}
|
|
37906
|
+
}
|
|
37907
|
+
return -1;
|
|
37908
|
+
};
|
|
37909
|
+
}
|
|
37910
|
+
return () => -1;
|
|
37911
|
+
}
|
|
37912
|
+
function parseClassDef(reader, offset) {
|
|
37913
|
+
reader.seek(offset);
|
|
37914
|
+
const format = reader.u16();
|
|
37915
|
+
if (format === 1) {
|
|
37916
|
+
const startGlyph = reader.u16();
|
|
37917
|
+
const glyphCount = reader.u16();
|
|
37918
|
+
const classes = new Array(glyphCount);
|
|
37919
|
+
for (let i = 0; i < glyphCount; i += 1) {
|
|
37920
|
+
classes[i] = reader.u16();
|
|
37921
|
+
}
|
|
37922
|
+
return (glyphId) => {
|
|
37923
|
+
const index = glyphId - startGlyph;
|
|
37924
|
+
return index >= 0 && index < glyphCount ? classes[index] : 0;
|
|
37925
|
+
};
|
|
37926
|
+
}
|
|
37927
|
+
if (format === 2) {
|
|
37928
|
+
const rangeCount = reader.u16();
|
|
37929
|
+
const ranges = [];
|
|
37930
|
+
for (let i = 0; i < rangeCount; i += 1) {
|
|
37931
|
+
const start = reader.u16();
|
|
37932
|
+
const end = reader.u16();
|
|
37933
|
+
const classValue = reader.u16();
|
|
37934
|
+
ranges.push({ start, end, classValue });
|
|
37935
|
+
}
|
|
37936
|
+
return (glyphId) => {
|
|
37937
|
+
for (const range of ranges) {
|
|
37938
|
+
if (glyphId >= range.start && glyphId <= range.end) {
|
|
37939
|
+
return range.classValue;
|
|
37940
|
+
}
|
|
37941
|
+
}
|
|
37942
|
+
return 0;
|
|
37943
|
+
};
|
|
37944
|
+
}
|
|
37945
|
+
return () => 0;
|
|
37946
|
+
}
|
|
37947
|
+
function parseSingleSubst(reader, offset) {
|
|
37948
|
+
reader.seek(offset);
|
|
37949
|
+
const format = reader.u16();
|
|
37950
|
+
const coverageOffset = offset + reader.u16();
|
|
37951
|
+
if (format === 1) {
|
|
37952
|
+
const delta = reader.i16();
|
|
37953
|
+
const coverage = parseCoverage(reader, coverageOffset);
|
|
37954
|
+
return {
|
|
37955
|
+
apply(glyphs, pos) {
|
|
37956
|
+
const glyph = glyphs[pos];
|
|
37957
|
+
if (!glyph || coverage(glyph.id) < 0) return null;
|
|
37958
|
+
glyph.id = glyph.id + delta & 65535;
|
|
37959
|
+
return 1;
|
|
37960
|
+
}
|
|
37961
|
+
};
|
|
37962
|
+
}
|
|
37963
|
+
if (format === 2) {
|
|
37964
|
+
const glyphCount = reader.u16();
|
|
37965
|
+
const substitutes = new Array(glyphCount);
|
|
37966
|
+
for (let i = 0; i < glyphCount; i += 1) {
|
|
37967
|
+
substitutes[i] = reader.u16();
|
|
37968
|
+
}
|
|
37969
|
+
const coverage = parseCoverage(reader, coverageOffset);
|
|
37970
|
+
return {
|
|
37971
|
+
apply(glyphs, pos) {
|
|
37972
|
+
const glyph = glyphs[pos];
|
|
37973
|
+
if (!glyph) return null;
|
|
37974
|
+
const index = coverage(glyph.id);
|
|
37975
|
+
if (index < 0 || index >= glyphCount) return null;
|
|
37976
|
+
glyph.id = substitutes[index];
|
|
37977
|
+
return 1;
|
|
37978
|
+
}
|
|
37979
|
+
};
|
|
37980
|
+
}
|
|
37981
|
+
return null;
|
|
37982
|
+
}
|
|
37983
|
+
function parseMultipleSubst(reader, offset) {
|
|
37984
|
+
reader.seek(offset);
|
|
37985
|
+
const format = reader.u16();
|
|
37986
|
+
if (format !== 1) return null;
|
|
37987
|
+
const coverageOffset = offset + reader.u16();
|
|
37988
|
+
const sequenceCount = reader.u16();
|
|
37989
|
+
const sequenceOffsets = new Array(sequenceCount);
|
|
37990
|
+
for (let i = 0; i < sequenceCount; i += 1) {
|
|
37991
|
+
sequenceOffsets[i] = offset + reader.u16();
|
|
37992
|
+
}
|
|
37993
|
+
const sequences = sequenceOffsets.map((seqOffset) => {
|
|
37994
|
+
reader.seek(seqOffset);
|
|
37995
|
+
const glyphCount = reader.u16();
|
|
37996
|
+
const ids = new Array(glyphCount);
|
|
37997
|
+
for (let i = 0; i < glyphCount; i += 1) ids[i] = reader.u16();
|
|
37998
|
+
return ids;
|
|
37999
|
+
});
|
|
38000
|
+
const coverage = parseCoverage(reader, coverageOffset);
|
|
38001
|
+
return {
|
|
38002
|
+
apply(glyphs, pos) {
|
|
38003
|
+
const glyph = glyphs[pos];
|
|
38004
|
+
if (!glyph) return null;
|
|
38005
|
+
const index = coverage(glyph.id);
|
|
38006
|
+
if (index < 0 || index >= sequences.length) return null;
|
|
38007
|
+
const ids = sequences[index];
|
|
38008
|
+
if (ids.length === 0) return null;
|
|
38009
|
+
const codePoints = glyph.codePoints;
|
|
38010
|
+
const replacement = ids.map((id, i) => ({
|
|
38011
|
+
id,
|
|
38012
|
+
codePoints: i === 0 ? codePoints : []
|
|
38013
|
+
}));
|
|
38014
|
+
glyphs.splice(pos, 1, ...replacement);
|
|
38015
|
+
return ids.length;
|
|
38016
|
+
}
|
|
38017
|
+
};
|
|
38018
|
+
}
|
|
38019
|
+
function parseAlternateSubst(reader, offset) {
|
|
38020
|
+
reader.seek(offset);
|
|
38021
|
+
const format = reader.u16();
|
|
38022
|
+
if (format !== 1) return null;
|
|
38023
|
+
const coverageOffset = offset + reader.u16();
|
|
38024
|
+
const altSetCount = reader.u16();
|
|
38025
|
+
const altSetOffsets = new Array(altSetCount);
|
|
38026
|
+
for (let i = 0; i < altSetCount; i += 1) {
|
|
38027
|
+
altSetOffsets[i] = offset + reader.u16();
|
|
38028
|
+
}
|
|
38029
|
+
const altSets = altSetOffsets.map((setOffset) => {
|
|
38030
|
+
reader.seek(setOffset);
|
|
38031
|
+
const glyphCount = reader.u16();
|
|
38032
|
+
const ids = new Array(glyphCount);
|
|
38033
|
+
for (let i = 0; i < glyphCount; i += 1) ids[i] = reader.u16();
|
|
38034
|
+
return ids;
|
|
38035
|
+
});
|
|
38036
|
+
const coverage = parseCoverage(reader, coverageOffset);
|
|
38037
|
+
return {
|
|
38038
|
+
apply(glyphs, pos) {
|
|
38039
|
+
const glyph = glyphs[pos];
|
|
38040
|
+
if (!glyph) return null;
|
|
38041
|
+
const index = coverage(glyph.id);
|
|
38042
|
+
if (index < 0 || index >= altSets.length) return null;
|
|
38043
|
+
const alts = altSets[index];
|
|
38044
|
+
if (alts.length === 0) return null;
|
|
38045
|
+
glyph.id = alts[0];
|
|
38046
|
+
return 1;
|
|
38047
|
+
}
|
|
38048
|
+
};
|
|
38049
|
+
}
|
|
38050
|
+
function parseLigatureSubst(reader, offset) {
|
|
38051
|
+
reader.seek(offset);
|
|
38052
|
+
const format = reader.u16();
|
|
38053
|
+
if (format !== 1) return null;
|
|
38054
|
+
const coverageOffset = offset + reader.u16();
|
|
38055
|
+
const ligSetCount = reader.u16();
|
|
38056
|
+
const ligSetOffsets = new Array(ligSetCount);
|
|
38057
|
+
for (let i = 0; i < ligSetCount; i += 1) {
|
|
38058
|
+
ligSetOffsets[i] = offset + reader.u16();
|
|
38059
|
+
}
|
|
38060
|
+
const ligatureSets = ligSetOffsets.map((setOffset) => {
|
|
38061
|
+
reader.seek(setOffset);
|
|
38062
|
+
const ligCount = reader.u16();
|
|
38063
|
+
const ligOffsets = new Array(ligCount);
|
|
38064
|
+
for (let i = 0; i < ligCount; i += 1) {
|
|
38065
|
+
ligOffsets[i] = setOffset + reader.u16();
|
|
38066
|
+
}
|
|
38067
|
+
return ligOffsets.map((ligOffset) => {
|
|
38068
|
+
reader.seek(ligOffset);
|
|
38069
|
+
const ligatureGlyph = reader.u16();
|
|
38070
|
+
const componentCount = reader.u16();
|
|
38071
|
+
const components = new Array(Math.max(0, componentCount - 1));
|
|
38072
|
+
for (let i = 0; i < components.length; i += 1) {
|
|
38073
|
+
components[i] = reader.u16();
|
|
38074
|
+
}
|
|
38075
|
+
return { ligatureGlyph, components };
|
|
38076
|
+
});
|
|
38077
|
+
});
|
|
38078
|
+
const coverage = parseCoverage(reader, coverageOffset);
|
|
38079
|
+
return {
|
|
38080
|
+
apply(glyphs, pos) {
|
|
38081
|
+
const first = glyphs[pos];
|
|
38082
|
+
if (!first) return null;
|
|
38083
|
+
const setIndex = coverage(first.id);
|
|
38084
|
+
if (setIndex < 0 || setIndex >= ligatureSets.length) return null;
|
|
38085
|
+
for (const ligature of ligatureSets[setIndex]) {
|
|
38086
|
+
const { components } = ligature;
|
|
38087
|
+
if (pos + components.length > glyphs.length - 1) continue;
|
|
38088
|
+
let matched = true;
|
|
38089
|
+
for (let i = 0; i < components.length; i += 1) {
|
|
38090
|
+
if (glyphs[pos + 1 + i].id !== components[i]) {
|
|
38091
|
+
matched = false;
|
|
38092
|
+
break;
|
|
38093
|
+
}
|
|
38094
|
+
}
|
|
38095
|
+
if (!matched) continue;
|
|
38096
|
+
const total = components.length + 1;
|
|
38097
|
+
const mergedCodePoints = [];
|
|
38098
|
+
for (let i = 0; i < total; i += 1) {
|
|
38099
|
+
mergedCodePoints.push(...glyphs[pos + i].codePoints);
|
|
38100
|
+
}
|
|
38101
|
+
glyphs.splice(pos, total, {
|
|
38102
|
+
id: ligature.ligatureGlyph,
|
|
38103
|
+
codePoints: mergedCodePoints
|
|
38104
|
+
});
|
|
38105
|
+
return 1;
|
|
38106
|
+
}
|
|
38107
|
+
return null;
|
|
38108
|
+
}
|
|
38109
|
+
};
|
|
38110
|
+
}
|
|
38111
|
+
function readSubstLookupRecords(reader, count) {
|
|
38112
|
+
const records = new Array(count);
|
|
38113
|
+
for (let i = 0; i < count; i += 1) {
|
|
38114
|
+
const sequenceIndex = reader.u16();
|
|
38115
|
+
const lookupListIndex = reader.u16();
|
|
38116
|
+
records[i] = { sequenceIndex, lookupListIndex };
|
|
38117
|
+
}
|
|
38118
|
+
return records;
|
|
38119
|
+
}
|
|
38120
|
+
function applySubstRecords(glyphs, pos, records, engine) {
|
|
38121
|
+
for (const record of records) {
|
|
38122
|
+
engine.applyLookupIndexAt(
|
|
38123
|
+
record.lookupListIndex,
|
|
38124
|
+
glyphs,
|
|
38125
|
+
pos + record.sequenceIndex
|
|
38126
|
+
);
|
|
38127
|
+
}
|
|
38128
|
+
}
|
|
38129
|
+
function parseChainContextSubst(reader, offset) {
|
|
38130
|
+
reader.seek(offset);
|
|
38131
|
+
const format = reader.u16();
|
|
38132
|
+
if (format === 3) {
|
|
38133
|
+
const backtrackCount = reader.u16();
|
|
38134
|
+
const backtrackCoverageOffsets = new Array(backtrackCount);
|
|
38135
|
+
for (let i = 0; i < backtrackCount; i += 1) {
|
|
38136
|
+
backtrackCoverageOffsets[i] = offset + reader.u16();
|
|
38137
|
+
}
|
|
38138
|
+
const inputCount = reader.u16();
|
|
38139
|
+
const inputCoverageOffsets = new Array(inputCount);
|
|
38140
|
+
for (let i = 0; i < inputCount; i += 1) {
|
|
38141
|
+
inputCoverageOffsets[i] = offset + reader.u16();
|
|
38142
|
+
}
|
|
38143
|
+
const lookaheadCount = reader.u16();
|
|
38144
|
+
const lookaheadCoverageOffsets = new Array(lookaheadCount);
|
|
38145
|
+
for (let i = 0; i < lookaheadCount; i += 1) {
|
|
38146
|
+
lookaheadCoverageOffsets[i] = offset + reader.u16();
|
|
38147
|
+
}
|
|
38148
|
+
const substCount = reader.u16();
|
|
38149
|
+
const records = readSubstLookupRecords(reader, substCount);
|
|
38150
|
+
const backtrack = backtrackCoverageOffsets.map(
|
|
38151
|
+
(o) => parseCoverage(reader, o)
|
|
38152
|
+
);
|
|
38153
|
+
const input = inputCoverageOffsets.map((o) => parseCoverage(reader, o));
|
|
38154
|
+
const lookahead = lookaheadCoverageOffsets.map(
|
|
38155
|
+
(o) => parseCoverage(reader, o)
|
|
38156
|
+
);
|
|
38157
|
+
return {
|
|
38158
|
+
apply(glyphs, pos, engine) {
|
|
38159
|
+
if (input.length === 0) return null;
|
|
38160
|
+
if (pos + input.length > glyphs.length) return null;
|
|
38161
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
38162
|
+
if (input[i](glyphs[pos + i].id) < 0) return null;
|
|
38163
|
+
}
|
|
38164
|
+
for (let i = 0; i < backtrack.length; i += 1) {
|
|
38165
|
+
const g = glyphs[pos - 1 - i];
|
|
38166
|
+
if (!g || backtrack[i](g.id) < 0) return null;
|
|
38167
|
+
}
|
|
38168
|
+
for (let i = 0; i < lookahead.length; i += 1) {
|
|
38169
|
+
const g = glyphs[pos + input.length + i];
|
|
38170
|
+
if (!g || lookahead[i](g.id) < 0) return null;
|
|
38171
|
+
}
|
|
38172
|
+
applySubstRecords(glyphs, pos, records, engine);
|
|
38173
|
+
return input.length;
|
|
38174
|
+
}
|
|
38175
|
+
};
|
|
38176
|
+
}
|
|
38177
|
+
if (format === 1) {
|
|
38178
|
+
const coverageOffset = offset + reader.u16();
|
|
38179
|
+
const ruleSetCount = reader.u16();
|
|
38180
|
+
const ruleSetOffsets = new Array(ruleSetCount);
|
|
38181
|
+
for (let i = 0; i < ruleSetCount; i += 1) {
|
|
38182
|
+
ruleSetOffsets[i] = offset + reader.u16();
|
|
38183
|
+
}
|
|
38184
|
+
const coverage = parseCoverage(reader, coverageOffset);
|
|
38185
|
+
const ruleSets = ruleSetOffsets.map(
|
|
38186
|
+
(setOffset) => parseChainRuleSet(reader, setOffset)
|
|
38187
|
+
);
|
|
38188
|
+
return {
|
|
38189
|
+
apply(glyphs, pos, engine) {
|
|
38190
|
+
const glyph = glyphs[pos];
|
|
38191
|
+
if (!glyph) return null;
|
|
38192
|
+
const index = coverage(glyph.id);
|
|
38193
|
+
if (index < 0 || index >= ruleSets.length) return null;
|
|
38194
|
+
return applyChainRules(ruleSets[index], glyphs, pos, engine, null);
|
|
38195
|
+
}
|
|
38196
|
+
};
|
|
38197
|
+
}
|
|
38198
|
+
if (format === 2) {
|
|
38199
|
+
const coverageOffset = offset + reader.u16();
|
|
38200
|
+
const backtrackClassOffset = offset + reader.u16();
|
|
38201
|
+
const inputClassOffset = offset + reader.u16();
|
|
38202
|
+
const lookaheadClassOffset = offset + reader.u16();
|
|
38203
|
+
const classSetCount = reader.u16();
|
|
38204
|
+
const classSetOffsets = new Array(classSetCount);
|
|
38205
|
+
for (let i = 0; i < classSetCount; i += 1) {
|
|
38206
|
+
const raw = reader.u16();
|
|
38207
|
+
classSetOffsets[i] = raw === 0 ? 0 : offset + raw;
|
|
38208
|
+
}
|
|
38209
|
+
const coverage = parseCoverage(reader, coverageOffset);
|
|
38210
|
+
const backtrackClass = parseClassDef(reader, backtrackClassOffset);
|
|
38211
|
+
const inputClass = parseClassDef(reader, inputClassOffset);
|
|
38212
|
+
const lookaheadClass = parseClassDef(reader, lookaheadClassOffset);
|
|
38213
|
+
const classSets = classSetOffsets.map(
|
|
38214
|
+
(setOffset) => setOffset === 0 ? [] : parseChainRuleSet(reader, setOffset)
|
|
38215
|
+
);
|
|
38216
|
+
return {
|
|
38217
|
+
apply(glyphs, pos, engine) {
|
|
38218
|
+
const glyph = glyphs[pos];
|
|
38219
|
+
if (!glyph) return null;
|
|
38220
|
+
if (coverage(glyph.id) < 0) return null;
|
|
38221
|
+
const classIndex = inputClass(glyph.id);
|
|
38222
|
+
const ruleSet = classSets[classIndex];
|
|
38223
|
+
if (!ruleSet) return null;
|
|
38224
|
+
return applyChainRules(ruleSet, glyphs, pos, engine, {
|
|
38225
|
+
backtrack: backtrackClass,
|
|
38226
|
+
input: inputClass,
|
|
38227
|
+
lookahead: lookaheadClass
|
|
38228
|
+
});
|
|
38229
|
+
}
|
|
38230
|
+
};
|
|
38231
|
+
}
|
|
38232
|
+
return null;
|
|
38233
|
+
}
|
|
38234
|
+
function parseChainRuleSet(reader, setOffset) {
|
|
38235
|
+
reader.seek(setOffset);
|
|
38236
|
+
const ruleCount = reader.u16();
|
|
38237
|
+
const ruleOffsets = new Array(ruleCount);
|
|
38238
|
+
for (let i = 0; i < ruleCount; i += 1) {
|
|
38239
|
+
ruleOffsets[i] = setOffset + reader.u16();
|
|
38240
|
+
}
|
|
38241
|
+
return ruleOffsets.map((ruleOffset) => {
|
|
38242
|
+
reader.seek(ruleOffset);
|
|
38243
|
+
const backtrackCount = reader.u16();
|
|
38244
|
+
const backtrack = new Array(backtrackCount);
|
|
38245
|
+
for (let i = 0; i < backtrackCount; i += 1) backtrack[i] = reader.u16();
|
|
38246
|
+
const inputCount = reader.u16();
|
|
38247
|
+
const input = new Array(Math.max(0, inputCount - 1));
|
|
38248
|
+
for (let i = 0; i < input.length; i += 1) input[i] = reader.u16();
|
|
38249
|
+
const lookaheadCount = reader.u16();
|
|
38250
|
+
const lookahead = new Array(lookaheadCount);
|
|
38251
|
+
for (let i = 0; i < lookaheadCount; i += 1) lookahead[i] = reader.u16();
|
|
38252
|
+
const substCount = reader.u16();
|
|
38253
|
+
const records = readSubstLookupRecords(reader, substCount);
|
|
38254
|
+
return { backtrack, input, lookahead, records };
|
|
38255
|
+
});
|
|
38256
|
+
}
|
|
38257
|
+
function applyChainRules(rules, glyphs, pos, engine, resolver) {
|
|
38258
|
+
const valueAt = (index, kind) => {
|
|
38259
|
+
const g = glyphs[index];
|
|
38260
|
+
if (!g) return null;
|
|
38261
|
+
if (!resolver) return g.id;
|
|
38262
|
+
if (kind === "b") return resolver.backtrack(g.id);
|
|
38263
|
+
if (kind === "l") return resolver.lookahead(g.id);
|
|
38264
|
+
return resolver.input(g.id);
|
|
38265
|
+
};
|
|
38266
|
+
for (const rule of rules) {
|
|
38267
|
+
const inputLen = rule.input.length + 1;
|
|
38268
|
+
if (pos + inputLen > glyphs.length) continue;
|
|
38269
|
+
let matched = true;
|
|
38270
|
+
for (let i = 0; i < rule.input.length; i += 1) {
|
|
38271
|
+
if (valueAt(pos + 1 + i, "i") !== rule.input[i]) {
|
|
38272
|
+
matched = false;
|
|
38273
|
+
break;
|
|
38274
|
+
}
|
|
38275
|
+
}
|
|
38276
|
+
if (!matched) continue;
|
|
38277
|
+
for (let i = 0; i < rule.backtrack.length; i += 1) {
|
|
38278
|
+
if (valueAt(pos - 1 - i, "b") !== rule.backtrack[i]) {
|
|
38279
|
+
matched = false;
|
|
38280
|
+
break;
|
|
38281
|
+
}
|
|
38282
|
+
}
|
|
38283
|
+
if (!matched) continue;
|
|
38284
|
+
for (let i = 0; i < rule.lookahead.length; i += 1) {
|
|
38285
|
+
if (valueAt(pos + inputLen + i, "l") !== rule.lookahead[i]) {
|
|
38286
|
+
matched = false;
|
|
38287
|
+
break;
|
|
38288
|
+
}
|
|
38289
|
+
}
|
|
38290
|
+
if (!matched) continue;
|
|
38291
|
+
applySubstRecords(glyphs, pos, rule.records, engine);
|
|
38292
|
+
return inputLen;
|
|
38293
|
+
}
|
|
38294
|
+
return null;
|
|
38295
|
+
}
|
|
38296
|
+
function parseSubtable(reader, offset, lookupType) {
|
|
38297
|
+
switch (lookupType) {
|
|
38298
|
+
case 1:
|
|
38299
|
+
return parseSingleSubst(reader, offset);
|
|
38300
|
+
case 2:
|
|
38301
|
+
return parseMultipleSubst(reader, offset);
|
|
38302
|
+
case 3:
|
|
38303
|
+
return parseAlternateSubst(reader, offset);
|
|
38304
|
+
case 4:
|
|
38305
|
+
return parseLigatureSubst(reader, offset);
|
|
38306
|
+
case 6:
|
|
38307
|
+
return parseChainContextSubst(reader, offset);
|
|
38308
|
+
case 7: {
|
|
38309
|
+
reader.seek(offset);
|
|
38310
|
+
const format = reader.u16();
|
|
38311
|
+
if (format !== 1) return null;
|
|
38312
|
+
const extensionType = reader.u16();
|
|
38313
|
+
const extensionOffset = offset + reader.u32();
|
|
38314
|
+
return parseSubtable(reader, extensionOffset, extensionType);
|
|
38315
|
+
}
|
|
38316
|
+
default:
|
|
38317
|
+
return null;
|
|
38318
|
+
}
|
|
38319
|
+
}
|
|
38320
|
+
class GsubTable {
|
|
38321
|
+
constructor(lookups, featureToLookups) {
|
|
38322
|
+
this.lookups = lookups;
|
|
38323
|
+
this.featureToLookups = featureToLookups;
|
|
38324
|
+
}
|
|
38325
|
+
/** Parses GSUB bytes (table-relative). Returns null on any structural failure. */
|
|
38326
|
+
static parse(bytes) {
|
|
38327
|
+
try {
|
|
38328
|
+
const reader = new BinaryReader(bytes);
|
|
38329
|
+
reader.seek(0);
|
|
38330
|
+
reader.skip(2);
|
|
38331
|
+
reader.skip(2);
|
|
38332
|
+
const scriptListOffset = reader.u16();
|
|
38333
|
+
const featureListOffset = reader.u16();
|
|
38334
|
+
const lookupListOffset = reader.u16();
|
|
38335
|
+
const featureRecords = parseFeatureList(reader, featureListOffset);
|
|
38336
|
+
const activeFeatureIndices = parseScriptList(reader, scriptListOffset);
|
|
38337
|
+
const lookups = parseLookupList(reader, lookupListOffset);
|
|
38338
|
+
const featureToLookups = /* @__PURE__ */ new Map();
|
|
38339
|
+
for (const featureIndex of activeFeatureIndices) {
|
|
38340
|
+
const record = featureRecords[featureIndex];
|
|
38341
|
+
if (!record) continue;
|
|
38342
|
+
const existing = featureToLookups.get(record.tag) ?? [];
|
|
38343
|
+
for (const lookupIndex of record.lookupIndices) {
|
|
38344
|
+
if (!existing.includes(lookupIndex)) existing.push(lookupIndex);
|
|
38345
|
+
}
|
|
38346
|
+
featureToLookups.set(record.tag, existing);
|
|
38347
|
+
}
|
|
38348
|
+
return new GsubTable(lookups, featureToLookups);
|
|
38349
|
+
} catch {
|
|
38350
|
+
return null;
|
|
38351
|
+
}
|
|
38352
|
+
}
|
|
38353
|
+
/** True when at least one of the requested feature tags exists in this font. */
|
|
38354
|
+
hasAnyFeature(tags) {
|
|
38355
|
+
return tags.some((tag) => this.featureToLookups.has(tag));
|
|
38356
|
+
}
|
|
38357
|
+
/** Ordered (ascending) unique lookup indices referenced by the given tags. */
|
|
38358
|
+
collectLookupIndices(tags) {
|
|
38359
|
+
const indices = /* @__PURE__ */ new Set();
|
|
38360
|
+
for (const tag of tags) {
|
|
38361
|
+
for (const lookupIndex of this.featureToLookups.get(tag) ?? []) {
|
|
38362
|
+
indices.add(lookupIndex);
|
|
38363
|
+
}
|
|
38364
|
+
}
|
|
38365
|
+
return Array.from(indices).sort((a, b) => a - b);
|
|
38366
|
+
}
|
|
38367
|
+
/**
|
|
38368
|
+
* Applies one lookup (by index) at a single position. Tries each subtable in
|
|
38369
|
+
* order; the first that matches wins. Returns the advance (output glyph count)
|
|
38370
|
+
* or null when nothing matched. Used both by the shaping walk and by nested
|
|
38371
|
+
* chaining-contextual substitution records.
|
|
38372
|
+
*/
|
|
38373
|
+
applyLookupIndexAt(lookupIndex, glyphs, pos) {
|
|
38374
|
+
const lookup = this.lookups[lookupIndex];
|
|
38375
|
+
if (!lookup || pos < 0 || pos >= glyphs.length) return null;
|
|
38376
|
+
for (const subtable of lookup.subtables) {
|
|
38377
|
+
const advance = subtable.apply(glyphs, pos, this);
|
|
38378
|
+
if (advance !== null) return advance;
|
|
38379
|
+
}
|
|
38380
|
+
return null;
|
|
38381
|
+
}
|
|
38382
|
+
/** Applies the lookups for the given features across the whole buffer. */
|
|
38383
|
+
shape(glyphs, tags) {
|
|
38384
|
+
const lookupIndices = this.collectLookupIndices(tags);
|
|
38385
|
+
for (const lookupIndex of lookupIndices) {
|
|
38386
|
+
let pos = 0;
|
|
38387
|
+
while (pos < glyphs.length) {
|
|
38388
|
+
const advance = this.applyLookupIndexAt(lookupIndex, glyphs, pos);
|
|
38389
|
+
pos += advance && advance > 0 ? advance : 1;
|
|
38390
|
+
}
|
|
38391
|
+
}
|
|
38392
|
+
}
|
|
38393
|
+
}
|
|
38394
|
+
function parseFeatureList(reader, featureListOffset) {
|
|
38395
|
+
reader.seek(featureListOffset);
|
|
38396
|
+
const featureCount = reader.u16();
|
|
38397
|
+
const records = [];
|
|
38398
|
+
for (let i = 0; i < featureCount; i += 1) {
|
|
38399
|
+
const tag = readTag(reader);
|
|
38400
|
+
const offset = featureListOffset + reader.u16();
|
|
38401
|
+
records.push({ tag, offset });
|
|
38402
|
+
}
|
|
38403
|
+
return records.map(({ tag, offset }) => {
|
|
38404
|
+
reader.seek(offset);
|
|
38405
|
+
reader.skip(2);
|
|
38406
|
+
const lookupIndexCount = reader.u16();
|
|
38407
|
+
const lookupIndices = new Array(lookupIndexCount);
|
|
38408
|
+
for (let i = 0; i < lookupIndexCount; i += 1) {
|
|
38409
|
+
lookupIndices[i] = reader.u16();
|
|
38410
|
+
}
|
|
38411
|
+
return { tag, lookupIndices };
|
|
38412
|
+
});
|
|
38413
|
+
}
|
|
38414
|
+
function parseScriptList(reader, scriptListOffset) {
|
|
38415
|
+
reader.seek(scriptListOffset);
|
|
38416
|
+
const scriptCount = reader.u16();
|
|
38417
|
+
const scripts = [];
|
|
38418
|
+
for (let i = 0; i < scriptCount; i += 1) {
|
|
38419
|
+
const tag = readTag(reader);
|
|
38420
|
+
const offset = scriptListOffset + reader.u16();
|
|
38421
|
+
scripts.push({ tag, offset });
|
|
38422
|
+
}
|
|
38423
|
+
if (scripts.length === 0) return [];
|
|
38424
|
+
const chosen = scripts.find((s) => s.tag === "latn") ?? scripts.find((s) => s.tag === "DFLT") ?? scripts[0];
|
|
38425
|
+
reader.seek(chosen.offset);
|
|
38426
|
+
const defaultLangSysOffset = reader.u16();
|
|
38427
|
+
if (defaultLangSysOffset === 0) return [];
|
|
38428
|
+
reader.seek(chosen.offset + defaultLangSysOffset);
|
|
38429
|
+
reader.skip(2);
|
|
38430
|
+
const requiredFeatureIndex = reader.u16();
|
|
38431
|
+
const featureIndexCount = reader.u16();
|
|
38432
|
+
const featureIndices = [];
|
|
38433
|
+
if (requiredFeatureIndex !== 65535) {
|
|
38434
|
+
featureIndices.push(requiredFeatureIndex);
|
|
38435
|
+
}
|
|
38436
|
+
for (let i = 0; i < featureIndexCount; i += 1) {
|
|
38437
|
+
featureIndices.push(reader.u16());
|
|
38438
|
+
}
|
|
38439
|
+
return featureIndices;
|
|
38440
|
+
}
|
|
38441
|
+
function parseLookupList(reader, lookupListOffset) {
|
|
38442
|
+
reader.seek(lookupListOffset);
|
|
38443
|
+
const lookupCount = reader.u16();
|
|
38444
|
+
const lookupOffsets = new Array(lookupCount);
|
|
38445
|
+
for (let i = 0; i < lookupCount; i += 1) {
|
|
38446
|
+
lookupOffsets[i] = lookupListOffset + reader.u16();
|
|
38447
|
+
}
|
|
38448
|
+
return lookupOffsets.map((lookupOffset) => {
|
|
38449
|
+
reader.seek(lookupOffset);
|
|
38450
|
+
const lookupType = reader.u16();
|
|
38451
|
+
reader.skip(2);
|
|
38452
|
+
const subTableCount = reader.u16();
|
|
38453
|
+
const subtableOffsets = new Array(subTableCount);
|
|
38454
|
+
for (let i = 0; i < subTableCount; i += 1) {
|
|
38455
|
+
subtableOffsets[i] = lookupOffset + reader.u16();
|
|
38456
|
+
}
|
|
38457
|
+
const subtables = [];
|
|
38458
|
+
for (const subtableOffset of subtableOffsets) {
|
|
38459
|
+
try {
|
|
38460
|
+
const subtable = parseSubtable(reader, subtableOffset, lookupType);
|
|
38461
|
+
if (subtable) subtables.push(subtable);
|
|
38462
|
+
} catch {
|
|
38463
|
+
}
|
|
38464
|
+
}
|
|
38465
|
+
return { lookupType, subtables };
|
|
38466
|
+
});
|
|
38467
|
+
}
|
|
38468
|
+
class OpenTypeLayouter {
|
|
38469
|
+
constructor(font) {
|
|
38470
|
+
__publicField(this, "gsub");
|
|
38471
|
+
this.font = font;
|
|
38472
|
+
const raw = font.getRawTableData("GSUB");
|
|
38473
|
+
this.gsub = raw ? GsubTable.parse(raw) : null;
|
|
38474
|
+
}
|
|
38475
|
+
/** True when the font exposes a GSUB table the shaper could use. */
|
|
38476
|
+
get hasGsub() {
|
|
38477
|
+
return this.gsub !== null;
|
|
38478
|
+
}
|
|
38479
|
+
layout(text, features) {
|
|
38480
|
+
const buffer = [];
|
|
38481
|
+
for (const char of text) {
|
|
38482
|
+
const codePoint = char.codePointAt(0) ?? 65533;
|
|
38483
|
+
const glyphId = this.font.glyphForCodePoint(codePoint);
|
|
38484
|
+
buffer.push({
|
|
38485
|
+
id: glyphId,
|
|
38486
|
+
codePoints: glyphId === 0 ? [65533] : [codePoint]
|
|
38487
|
+
});
|
|
38488
|
+
}
|
|
38489
|
+
if (this.gsub && features && features.length > 0) {
|
|
38490
|
+
this.gsub.shape(buffer, features);
|
|
38491
|
+
}
|
|
38492
|
+
const glyphs = [];
|
|
38493
|
+
const positions = [];
|
|
38494
|
+
let advanceWidth = 0;
|
|
38495
|
+
for (const glyph of buffer) {
|
|
38496
|
+
const glyphAdvance = this.font.advanceWidthForGlyph(glyph.id);
|
|
38497
|
+
glyphs.push({
|
|
38498
|
+
id: glyph.id,
|
|
38499
|
+
codePoints: glyph.codePoints,
|
|
38500
|
+
advanceWidth: glyphAdvance
|
|
38501
|
+
});
|
|
38502
|
+
positions.push({ xAdvance: glyphAdvance });
|
|
38503
|
+
advanceWidth += glyphAdvance;
|
|
38504
|
+
}
|
|
38505
|
+
return { glyphs, positions, advanceWidth };
|
|
38506
|
+
}
|
|
38507
|
+
}
|
|
37816
38508
|
class SimpleTextLayouter {
|
|
37817
38509
|
constructor(font) {
|
|
37818
38510
|
this.font = font;
|
|
@@ -38028,9 +38720,11 @@ function parseEmbeddedFontSync(sourceBytes) {
|
|
|
38028
38720
|
return SfntFontProgram.parse(sfntBytes);
|
|
38029
38721
|
}
|
|
38030
38722
|
function createPdfEmbeddableFont(program) {
|
|
38723
|
+
const shaper = new OpenTypeLayouter(program);
|
|
38724
|
+
const layouter = shaper.hasGsub ? shaper : new SimpleTextLayouter(program);
|
|
38031
38725
|
return {
|
|
38032
38726
|
program,
|
|
38033
|
-
layouter
|
|
38727
|
+
layouter,
|
|
38034
38728
|
subsetter: new TrueTypePdfFontSubsetter()
|
|
38035
38729
|
};
|
|
38036
38730
|
}
|
|
@@ -38078,8 +38772,8 @@ class PdfFontTable {
|
|
|
38078
38772
|
* Unicode run, recording the glyphs used for subsetting. Returns `null` when
|
|
38079
38773
|
* the run produced no glyphs so the caller can skip emission entirely.
|
|
38080
38774
|
*/
|
|
38081
|
-
buildUnicodeShowCommand(state, text) {
|
|
38082
|
-
const run = this.layoutUnicodeText(state, text);
|
|
38775
|
+
buildUnicodeShowCommand(state, text, features) {
|
|
38776
|
+
const run = this.layoutUnicodeText(state, text, features);
|
|
38083
38777
|
const encoded = this.encodeUnicodeGlyphRun(state, run);
|
|
38084
38778
|
if (encoded.length === 0) {
|
|
38085
38779
|
return null;
|
|
@@ -38113,13 +38807,14 @@ class PdfFontTable {
|
|
|
38113
38807
|
const resourceXml = fontResourceEntries.map((font, index) => `/${font.resourceName} ${fontObjectIds[index]} 0 R`).join(" ");
|
|
38114
38808
|
return { resourceXml };
|
|
38115
38809
|
}
|
|
38116
|
-
layoutUnicodeText(state, text) {
|
|
38117
|
-
const
|
|
38810
|
+
layoutUnicodeText(state, text, features) {
|
|
38811
|
+
const cacheKey = `${(features == null ? void 0 : features.join(",")) ?? ""}\0${text}`;
|
|
38812
|
+
const cached = state.layoutCache.get(cacheKey);
|
|
38118
38813
|
if (cached) {
|
|
38119
38814
|
return cached;
|
|
38120
38815
|
}
|
|
38121
|
-
const run = state.font.layouter.layout(text);
|
|
38122
|
-
state.layoutCache.set(
|
|
38816
|
+
const run = state.font.layouter.layout(text, features);
|
|
38817
|
+
state.layoutCache.set(cacheKey, run);
|
|
38123
38818
|
return run;
|
|
38124
38819
|
}
|
|
38125
38820
|
encodeUnicodeGlyphRun(state, run) {
|