oasis-editor 0.0.99 → 0.0.101

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 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";
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-BPpHyg6q.js";
5
5
  function cloneStyle(style2) {
6
6
  return style2 ? { ...style2 } : void 0;
7
7
  }
@@ -34711,28 +34711,54 @@ function serializeRunsWithBoundaries(runs, tokens, context, paragraphStyleId, st
34711
34711
  flushUpTo(Number.POSITIVE_INFINITY);
34712
34712
  return out;
34713
34713
  }
34714
+ function serializeSingleBlockXml(block, context, styles) {
34715
+ var _a;
34716
+ switch (block.type) {
34717
+ case "paragraph":
34718
+ return serializeParagraphXml(block, context, styles);
34719
+ case "table": {
34720
+ const pageBreakXml = ((_a = block.style) == null ? void 0 : _a.pageBreakBefore) ? '<w:p><w:r><w:br w:type="page"/></w:r></w:p>' : "";
34721
+ return pageBreakXml + serializeTableXml(
34722
+ block,
34723
+ (paragraph, cell) => {
34724
+ var _a2;
34725
+ return serializeParagraphXml(paragraph, context, styles, {
34726
+ align: (_a2 = cell.style) == null ? void 0 : _a2.horizontalAlign
34727
+ });
34728
+ }
34729
+ );
34730
+ }
34731
+ default:
34732
+ return assertNever(block, "block");
34733
+ }
34734
+ }
34714
34735
  function serializeBlocksXml(blocks, context, styles) {
34715
- return blocks.map((block) => {
34716
- var _a;
34717
- switch (block.type) {
34718
- case "paragraph":
34719
- return serializeParagraphXml(block, context, styles);
34720
- case "table": {
34721
- const pageBreakXml = ((_a = block.style) == null ? void 0 : _a.pageBreakBefore) ? '<w:p><w:r><w:br w:type="page"/></w:r></w:p>' : "";
34722
- return pageBreakXml + serializeTableXml(
34723
- block,
34724
- (paragraph, cell) => {
34725
- var _a2;
34726
- return serializeParagraphXml(paragraph, context, styles, {
34727
- align: (_a2 = cell.style) == null ? void 0 : _a2.horizontalAlign
34728
- });
34729
- }
34730
- );
34731
- }
34732
- default:
34733
- return assertNever(block, "block");
34736
+ var _a, _b, _c;
34737
+ let out = "";
34738
+ let i = 0;
34739
+ while (i < blocks.length) {
34740
+ const block = blocks[i];
34741
+ const wrapper = (_a = block.sdtWrappers) == null ? void 0 : _a[0];
34742
+ if (!wrapper) {
34743
+ out += serializeSingleBlockXml(block, context, styles);
34744
+ i += 1;
34745
+ continue;
34734
34746
  }
34735
- }).join("");
34747
+ const group = [];
34748
+ let j = i;
34749
+ while (j < blocks.length && ((_c = (_b = blocks[j].sdtWrappers) == null ? void 0 : _b[0]) == null ? void 0 : _c.groupId) === wrapper.groupId) {
34750
+ const rest = blocks[j].sdtWrappers.slice(1);
34751
+ group.push({
34752
+ ...blocks[j],
34753
+ sdtWrappers: rest.length > 0 ? rest : void 0
34754
+ });
34755
+ j += 1;
34756
+ }
34757
+ const inner = serializeBlocksXml(group, context, styles);
34758
+ out += `<w:sdt>${wrapper.sdtPrXml}${wrapper.sdtEndPrXml ?? ""}<w:sdtContent>${inner}</w:sdtContent></w:sdt>`;
34759
+ i = j;
34760
+ }
34761
+ return out;
34736
34762
  }
34737
34763
  function serializeParagraphXml(paragraph, context, styles, overrides) {
34738
34764
  var _a, _b, _c;
@@ -36356,6 +36382,39 @@ function drawFragmentShading(writer, pageIndex, line, fragment, originX, originY
36356
36382
  fill: styles.shading
36357
36383
  });
36358
36384
  }
36385
+ function resolveGradientShadingName(writer, pageIndex, line, fragment, originX, originY, styles) {
36386
+ const fill = styles.textFill;
36387
+ if (!fill || fill.type !== "gradient" || fill.stops.length < 2) {
36388
+ return void 0;
36389
+ }
36390
+ const bounds = resolveFragmentBounds(
36391
+ line,
36392
+ fragment,
36393
+ styles.fontSize ?? DEFAULT_FONT_SIZE_PX
36394
+ );
36395
+ if (!bounds) {
36396
+ return void 0;
36397
+ }
36398
+ const x0px = originX + bounds.left;
36399
+ const x1px = originX + bounds.right;
36400
+ const y0px = originY + line.top;
36401
+ const y1px = originY + line.top + line.height;
36402
+ const cx = (x0px + x1px) / 2;
36403
+ const cy = (y0px + y1px) / 2;
36404
+ const rad = (fill.angle ?? 0) * Math.PI / 180;
36405
+ const dx = Math.cos(rad) * (x1px - x0px) / 2;
36406
+ const dy = Math.sin(rad) * (y1px - y0px) / 2;
36407
+ return writer.registerAxialGradient(pageIndex, {
36408
+ x0: pxToPt$1(cx - dx),
36409
+ y0: pxToPt$1(cy - dy),
36410
+ x1: pxToPt$1(cx + dx),
36411
+ y1: pxToPt$1(cy + dy),
36412
+ stops: fill.stops.map((stop) => ({
36413
+ offset: stop.position,
36414
+ color: stop.color
36415
+ }))
36416
+ }) ?? void 0;
36417
+ }
36359
36418
  function drawFragmentBorder(writer, pageIndex, line, fragment, originX, originY, styles) {
36360
36419
  const border = styles.textBorder;
36361
36420
  if (!border || border.type === "none" || border.width <= 0) {
@@ -36714,7 +36773,10 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
36714
36773
  styles.color ?? "#000000"
36715
36774
  );
36716
36775
  const paragraphAlign = resolveEffectiveParagraphStyle(paragraph.style, document2.styles).align ?? "left";
36717
- const fontFeatures = resolveOpenTypeFeatureTags(styles);
36776
+ const fontFeatures = resolveOpenTypeFeatureTags(
36777
+ styles,
36778
+ textStyleToFontSizePt(styles)
36779
+ );
36718
36780
  const baseTextOptions = {
36719
36781
  fontSize: fontSizePt,
36720
36782
  bold: styles.bold,
@@ -36725,6 +36787,15 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
36725
36787
  fontFeatures
36726
36788
  };
36727
36789
  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";
36790
+ const gradientShadingName = resolveGradientShadingName(
36791
+ writer,
36792
+ pageIndex,
36793
+ line,
36794
+ fragment,
36795
+ originX,
36796
+ originY,
36797
+ styles
36798
+ );
36728
36799
  const offsetPt = pxToPt$1(1);
36729
36800
  const emitChunk = (leftPx, text2) => {
36730
36801
  if (styles.emboss || styles.imprint) {
@@ -36800,7 +36871,9 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
36800
36871
  y: pxToPt$1(baselineY),
36801
36872
  text: text2,
36802
36873
  color: mainColor,
36803
- ...textOutline ? {
36874
+ // A gradient fill paints the glyphs via the shading clip; it supersedes
36875
+ // both the solid color and any outline/stroke render mode.
36876
+ ...gradientShadingName ? { gradientShadingName } : textOutline ? {
36804
36877
  renderMode: 2,
36805
36878
  strokeColor: textOutline.color ?? mainColor,
36806
36879
  strokeWidth: textOutline.widthPt
@@ -37631,10 +37704,27 @@ function textMarkerComment(value) {
37631
37704
  return `% OasisText ${encodePdfUtf16Hex(codePoints)}`;
37632
37705
  }
37633
37706
  class PdfContentStream {
37634
- constructor(page, fonts, images) {
37707
+ constructor(page, fonts, images, shadings) {
37635
37708
  this.page = page;
37636
37709
  this.fonts = fonts;
37637
37710
  this.images = images;
37711
+ this.shadings = shadings;
37712
+ }
37713
+ /**
37714
+ * Registers an axial gradient for use as a glyph fill on this page. Coordinates
37715
+ * come in the writer's top-left point space and are flipped to PDF bottom-left
37716
+ * space here; the returned name is referenced via `OasisPdfTextOptions`.
37717
+ */
37718
+ registerAxialGradient(gradient) {
37719
+ const name = this.shadings.register({
37720
+ x0: gradient.x0,
37721
+ y0: this.page.height - gradient.y0,
37722
+ x1: gradient.x1,
37723
+ y1: this.page.height - gradient.y1,
37724
+ stops: gradient.stops
37725
+ });
37726
+ this.page.shadingResourceNames.add(name);
37727
+ return name;
37638
37728
  }
37639
37729
  drawRect(options) {
37640
37730
  const page = this.page;
@@ -37792,8 +37882,24 @@ class PdfContentStream {
37792
37882
  if (showCommand === null) {
37793
37883
  return;
37794
37884
  }
37885
+ if (options.gradientShadingName) {
37886
+ this.emitGradientTextBlock(
37887
+ fontResourceName,
37888
+ showCommand,
37889
+ options,
37890
+ options.gradientShadingName
37891
+ );
37892
+ return;
37893
+ }
37795
37894
  this.emitTextBlock(fontResourceName, showCommand, options);
37796
37895
  }
37896
+ textSetupCommands(fontResourceName, options) {
37897
+ return [
37898
+ `/${fontResourceName} ${formatNumber(options.fontSize ?? 12)} Tf`,
37899
+ ...options.horizontalScale && options.horizontalScale > 0 && options.horizontalScale !== 100 ? [`${formatNumber(options.horizontalScale)} Tz`] : [],
37900
+ ...options.characterSpacing && options.characterSpacing !== 0 ? [`${formatNumber(options.characterSpacing)} Tc`] : []
37901
+ ];
37902
+ }
37797
37903
  emitTextBlock(fontResourceName, showCommand, options) {
37798
37904
  const page = this.page;
37799
37905
  page.commands.push(
@@ -37801,7 +37907,6 @@ class PdfContentStream {
37801
37907
  textMarkerComment(options.text),
37802
37908
  "BT",
37803
37909
  colorCommand(options.color, "rg", [0, 0, 0]),
37804
- `/${fontResourceName} ${formatNumber(options.fontSize ?? 12)} Tf`,
37805
37910
  ...options.renderMode && options.renderMode !== 0 ? [
37806
37911
  colorCommand(
37807
37912
  options.strokeColor ?? options.color,
@@ -37813,14 +37918,37 @@ class PdfContentStream {
37813
37918
  )} w`,
37814
37919
  `${options.renderMode} Tr`
37815
37920
  ] : [],
37816
- ...options.horizontalScale && options.horizontalScale > 0 && options.horizontalScale !== 100 ? [`${formatNumber(options.horizontalScale)} Tz`] : [],
37817
- ...options.characterSpacing && options.characterSpacing !== 0 ? [`${formatNumber(options.characterSpacing)} Tc`] : [],
37921
+ ...this.textSetupCommands(fontResourceName, options),
37818
37922
  `${formatNumber(options.x)} ${formatNumber(page.height - options.y)} Td`,
37819
37923
  showCommand,
37820
37924
  "ET"
37821
37925
  ].join("\n")
37822
37926
  );
37823
37927
  }
37928
+ /**
37929
+ * Fills glyphs with an axial gradient: the text is drawn in clip render mode
37930
+ * (`7 Tr`) so the glyph outlines become the clip path, then the shading is
37931
+ * painted through them with `sh`. The whole sequence is isolated in a
37932
+ * `q … Q` pair so the clip does not leak. Outline/stroke is not combined with
37933
+ * gradient fill (the gradient supersedes it).
37934
+ */
37935
+ emitGradientTextBlock(fontResourceName, showCommand, options, shadingName) {
37936
+ const page = this.page;
37937
+ page.commands.push(
37938
+ [
37939
+ textMarkerComment(options.text),
37940
+ "q",
37941
+ "BT",
37942
+ ...this.textSetupCommands(fontResourceName, options),
37943
+ "7 Tr",
37944
+ `${formatNumber(options.x)} ${formatNumber(page.height - options.y)} Td`,
37945
+ showCommand,
37946
+ "ET",
37947
+ `/${shadingName} sh`,
37948
+ "Q"
37949
+ ].join("\n")
37950
+ );
37951
+ }
37824
37952
  drawImage(options) {
37825
37953
  const page = this.page;
37826
37954
  if (options.width <= 0 || options.height <= 0 || !this.images.has(options.resourceName)) {
@@ -37875,9 +38003,6 @@ class PdfContentStream {
37875
38003
  );
37876
38004
  }
37877
38005
  }
37878
- function readTag(reader) {
37879
- return reader.tag();
37880
- }
37881
38006
  function parseCoverage(reader, offset) {
37882
38007
  reader.seek(offset);
37883
38008
  const format = reader.u16();
@@ -37944,6 +38069,83 @@ function parseClassDef(reader, offset) {
37944
38069
  }
37945
38070
  return () => 0;
37946
38071
  }
38072
+ function parseFeatureList(reader, featureListOffset) {
38073
+ reader.seek(featureListOffset);
38074
+ const featureCount = reader.u16();
38075
+ const records = [];
38076
+ for (let i = 0; i < featureCount; i += 1) {
38077
+ const tag = reader.tag();
38078
+ const offset = featureListOffset + reader.u16();
38079
+ records.push({ tag, offset });
38080
+ }
38081
+ return records.map(({ tag, offset }) => {
38082
+ reader.seek(offset);
38083
+ reader.skip(2);
38084
+ const lookupIndexCount = reader.u16();
38085
+ const lookupIndices = new Array(lookupIndexCount);
38086
+ for (let i = 0; i < lookupIndexCount; i += 1) {
38087
+ lookupIndices[i] = reader.u16();
38088
+ }
38089
+ return { tag, lookupIndices };
38090
+ });
38091
+ }
38092
+ function parseScriptList(reader, scriptListOffset) {
38093
+ reader.seek(scriptListOffset);
38094
+ const scriptCount = reader.u16();
38095
+ const scripts = [];
38096
+ for (let i = 0; i < scriptCount; i += 1) {
38097
+ const tag = reader.tag();
38098
+ const offset = scriptListOffset + reader.u16();
38099
+ scripts.push({ tag, offset });
38100
+ }
38101
+ if (scripts.length === 0) return [];
38102
+ const chosen = scripts.find((s) => s.tag === "latn") ?? scripts.find((s) => s.tag === "DFLT") ?? scripts[0];
38103
+ reader.seek(chosen.offset);
38104
+ const defaultLangSysOffset = reader.u16();
38105
+ if (defaultLangSysOffset === 0) return [];
38106
+ reader.seek(chosen.offset + defaultLangSysOffset);
38107
+ reader.skip(2);
38108
+ const requiredFeatureIndex = reader.u16();
38109
+ const featureIndexCount = reader.u16();
38110
+ const featureIndices = [];
38111
+ if (requiredFeatureIndex !== 65535) {
38112
+ featureIndices.push(requiredFeatureIndex);
38113
+ }
38114
+ for (let i = 0; i < featureIndexCount; i += 1) {
38115
+ featureIndices.push(reader.u16());
38116
+ }
38117
+ return featureIndices;
38118
+ }
38119
+ function parseLayoutTableHeader(reader) {
38120
+ reader.seek(0);
38121
+ reader.skip(2);
38122
+ reader.skip(2);
38123
+ const scriptListOffset = reader.u16();
38124
+ const featureListOffset = reader.u16();
38125
+ const lookupListOffset = reader.u16();
38126
+ const featureRecords = parseFeatureList(reader, featureListOffset);
38127
+ const activeFeatureIndices = parseScriptList(reader, scriptListOffset);
38128
+ const featureToLookups = /* @__PURE__ */ new Map();
38129
+ for (const featureIndex of activeFeatureIndices) {
38130
+ const record = featureRecords[featureIndex];
38131
+ if (!record) continue;
38132
+ const existing = featureToLookups.get(record.tag) ?? [];
38133
+ for (const lookupIndex of record.lookupIndices) {
38134
+ if (!existing.includes(lookupIndex)) existing.push(lookupIndex);
38135
+ }
38136
+ featureToLookups.set(record.tag, existing);
38137
+ }
38138
+ return { featureToLookups, lookupListOffset };
38139
+ }
38140
+ function collectLookupIndices(featureToLookups, tags) {
38141
+ const indices = /* @__PURE__ */ new Set();
38142
+ for (const tag of tags) {
38143
+ for (const lookupIndex of featureToLookups.get(tag) ?? []) {
38144
+ indices.add(lookupIndex);
38145
+ }
38146
+ }
38147
+ return Array.from(indices).sort((a, b) => a - b);
38148
+ }
37947
38149
  function parseSingleSubst(reader, offset) {
37948
38150
  reader.seek(offset);
37949
38151
  const format = reader.u16();
@@ -38293,7 +38495,7 @@ function applyChainRules(rules, glyphs, pos, engine, resolver) {
38293
38495
  }
38294
38496
  return null;
38295
38497
  }
38296
- function parseSubtable(reader, offset, lookupType) {
38498
+ function parseSubtable$1(reader, offset, lookupType) {
38297
38499
  switch (lookupType) {
38298
38500
  case 1:
38299
38501
  return parseSingleSubst(reader, offset);
@@ -38311,7 +38513,7 @@ function parseSubtable(reader, offset, lookupType) {
38311
38513
  if (format !== 1) return null;
38312
38514
  const extensionType = reader.u16();
38313
38515
  const extensionOffset = offset + reader.u32();
38314
- return parseSubtable(reader, extensionOffset, extensionType);
38516
+ return parseSubtable$1(reader, extensionOffset, extensionType);
38315
38517
  }
38316
38518
  default:
38317
38519
  return null;
@@ -38326,25 +38528,8 @@ class GsubTable {
38326
38528
  static parse(bytes) {
38327
38529
  try {
38328
38530
  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
- }
38531
+ const { featureToLookups, lookupListOffset } = parseLayoutTableHeader(reader);
38532
+ const lookups = parseLookupList$1(reader, lookupListOffset);
38348
38533
  return new GsubTable(lookups, featureToLookups);
38349
38534
  } catch {
38350
38535
  return null;
@@ -38356,13 +38541,7 @@ class GsubTable {
38356
38541
  }
38357
38542
  /** Ordered (ascending) unique lookup indices referenced by the given tags. */
38358
38543
  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);
38544
+ return collectLookupIndices(this.featureToLookups, tags);
38366
38545
  }
38367
38546
  /**
38368
38547
  * Applies one lookup (by index) at a single position. Tries each subtable in
@@ -38391,52 +38570,214 @@ class GsubTable {
38391
38570
  }
38392
38571
  }
38393
38572
  }
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 });
38573
+ function parseLookupList$1(reader, lookupListOffset) {
38574
+ reader.seek(lookupListOffset);
38575
+ const lookupCount = reader.u16();
38576
+ const lookupOffsets = new Array(lookupCount);
38577
+ for (let i = 0; i < lookupCount; i += 1) {
38578
+ lookupOffsets[i] = lookupListOffset + reader.u16();
38402
38579
  }
38403
- return records.map(({ tag, offset }) => {
38404
- reader.seek(offset);
38580
+ return lookupOffsets.map((lookupOffset) => {
38581
+ reader.seek(lookupOffset);
38582
+ const lookupType = reader.u16();
38405
38583
  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();
38584
+ const subTableCount = reader.u16();
38585
+ const subtableOffsets = new Array(subTableCount);
38586
+ for (let i = 0; i < subTableCount; i += 1) {
38587
+ subtableOffsets[i] = lookupOffset + reader.u16();
38410
38588
  }
38411
- return { tag, lookupIndices };
38589
+ const subtables = [];
38590
+ for (const subtableOffset of subtableOffsets) {
38591
+ try {
38592
+ const subtable = parseSubtable$1(reader, subtableOffset, lookupType);
38593
+ if (subtable) subtables.push(subtable);
38594
+ } catch {
38595
+ }
38596
+ }
38597
+ return { lookupType, subtables };
38412
38598
  });
38413
38599
  }
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 });
38600
+ const X_PLACEMENT = 1;
38601
+ const Y_PLACEMENT = 2;
38602
+ const X_ADVANCE = 4;
38603
+ const Y_ADVANCE = 8;
38604
+ const X_PLACEMENT_DEVICE = 16;
38605
+ const Y_PLACEMENT_DEVICE = 32;
38606
+ const X_ADVANCE_DEVICE = 64;
38607
+ const Y_ADVANCE_DEVICE = 128;
38608
+ function readValueRecordXAdvance(reader, valueFormat) {
38609
+ let xAdvance = 0;
38610
+ if (valueFormat & X_PLACEMENT) reader.i16();
38611
+ if (valueFormat & Y_PLACEMENT) reader.i16();
38612
+ if (valueFormat & X_ADVANCE) xAdvance = reader.i16();
38613
+ if (valueFormat & Y_ADVANCE) reader.i16();
38614
+ if (valueFormat & X_PLACEMENT_DEVICE) reader.u16();
38615
+ if (valueFormat & Y_PLACEMENT_DEVICE) reader.u16();
38616
+ if (valueFormat & X_ADVANCE_DEVICE) reader.u16();
38617
+ if (valueFormat & Y_ADVANCE_DEVICE) reader.u16();
38618
+ return xAdvance;
38619
+ }
38620
+ function valueRecordSize(valueFormat) {
38621
+ let count = 0;
38622
+ for (let bit = 0; bit < 8; bit += 1) {
38623
+ if (valueFormat & 1 << bit) count += 1;
38624
+ }
38625
+ return count * 2;
38626
+ }
38627
+ function parseSinglePos(reader, offset) {
38628
+ reader.seek(offset);
38629
+ const format = reader.u16();
38630
+ const coverageOffset = offset + reader.u16();
38631
+ if (format === 1) {
38632
+ const valueFormat = reader.u16();
38633
+ const xAdvance = readValueRecordXAdvance(reader, valueFormat);
38634
+ const coverage = parseCoverage(reader, coverageOffset);
38635
+ if (xAdvance === 0) return null;
38636
+ return {
38637
+ apply(glyphIds, advances, pos) {
38638
+ if (coverage(glyphIds[pos]) < 0) return null;
38639
+ advances[pos] += xAdvance;
38640
+ return 1;
38641
+ }
38642
+ };
38422
38643
  }
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);
38644
+ if (format === 2) {
38645
+ const valueFormat = reader.u16();
38646
+ const valueCount = reader.u16();
38647
+ const recordSize = valueRecordSize(valueFormat);
38648
+ const recordsStart = reader.tell();
38649
+ const xAdvances = new Array(valueCount);
38650
+ for (let i = 0; i < valueCount; i += 1) {
38651
+ reader.seek(recordsStart + i * recordSize);
38652
+ xAdvances[i] = readValueRecordXAdvance(reader, valueFormat);
38653
+ }
38654
+ const coverage = parseCoverage(reader, coverageOffset);
38655
+ return {
38656
+ apply(glyphIds, advances, pos) {
38657
+ const index = coverage(glyphIds[pos]);
38658
+ if (index < 0 || index >= valueCount) return null;
38659
+ const delta = xAdvances[index];
38660
+ if (delta === 0) return null;
38661
+ advances[pos] += delta;
38662
+ return 1;
38663
+ }
38664
+ };
38435
38665
  }
38436
- for (let i = 0; i < featureIndexCount; i += 1) {
38437
- featureIndices.push(reader.u16());
38666
+ return null;
38667
+ }
38668
+ function parsePairPos(reader, offset) {
38669
+ reader.seek(offset);
38670
+ const format = reader.u16();
38671
+ const coverageOffset = offset + reader.u16();
38672
+ const valueFormat1 = reader.u16();
38673
+ const valueFormat2 = reader.u16();
38674
+ const hasSecondAdjustment = (valueFormat2 & X_ADVANCE) !== 0;
38675
+ if (format === 1) {
38676
+ const pairSetCount = reader.u16();
38677
+ const pairSetOffsets = new Array(pairSetCount);
38678
+ for (let i = 0; i < pairSetCount; i += 1) {
38679
+ pairSetOffsets[i] = offset + reader.u16();
38680
+ }
38681
+ const pairSets = pairSetOffsets.map(
38682
+ (setOffset) => {
38683
+ reader.seek(setOffset);
38684
+ const pairValueCount = reader.u16();
38685
+ const pairs = /* @__PURE__ */ new Map();
38686
+ for (let i = 0; i < pairValueCount; i += 1) {
38687
+ const secondGlyph = reader.u16();
38688
+ const value1XAdvance = readValueRecordXAdvance(reader, valueFormat1);
38689
+ const value2XAdvance = readValueRecordXAdvance(reader, valueFormat2);
38690
+ pairs.set(secondGlyph, {
38691
+ secondGlyph,
38692
+ value1XAdvance,
38693
+ value2XAdvance
38694
+ });
38695
+ }
38696
+ return pairs;
38697
+ }
38698
+ );
38699
+ const coverage = parseCoverage(reader, coverageOffset);
38700
+ return {
38701
+ apply(glyphIds, advances, pos) {
38702
+ const second = glyphIds[pos + 1];
38703
+ if (second === void 0) return null;
38704
+ const setIndex = coverage(glyphIds[pos]);
38705
+ if (setIndex < 0 || setIndex >= pairSets.length) return null;
38706
+ const pair = pairSets[setIndex].get(second);
38707
+ if (!pair) return null;
38708
+ advances[pos] += pair.value1XAdvance;
38709
+ advances[pos + 1] += pair.value2XAdvance;
38710
+ return hasSecondAdjustment ? 2 : 1;
38711
+ }
38712
+ };
38713
+ }
38714
+ if (format === 2) {
38715
+ const classDef1Offset = offset + reader.u16();
38716
+ const classDef2Offset = offset + reader.u16();
38717
+ const class1Count = reader.u16();
38718
+ const class2Count = reader.u16();
38719
+ const value1Size = valueRecordSize(valueFormat1);
38720
+ const value2Size = valueRecordSize(valueFormat2);
38721
+ const cellSize = value1Size + value2Size;
38722
+ const matrixStart = reader.tell();
38723
+ const value1 = new Array(class1Count * class2Count);
38724
+ const value2 = new Array(class1Count * class2Count);
38725
+ for (let c1 = 0; c1 < class1Count; c1 += 1) {
38726
+ for (let c2 = 0; c2 < class2Count; c2 += 1) {
38727
+ const cell = (c1 * class2Count + c2) * cellSize;
38728
+ reader.seek(matrixStart + cell);
38729
+ value1[c1 * class2Count + c2] = readValueRecordXAdvance(
38730
+ reader,
38731
+ valueFormat1
38732
+ );
38733
+ reader.seek(matrixStart + cell + value1Size);
38734
+ value2[c1 * class2Count + c2] = readValueRecordXAdvance(
38735
+ reader,
38736
+ valueFormat2
38737
+ );
38738
+ }
38739
+ }
38740
+ const coverage = parseCoverage(reader, coverageOffset);
38741
+ const classDef1 = parseClassDef(reader, classDef1Offset);
38742
+ const classDef2 = parseClassDef(reader, classDef2Offset);
38743
+ return {
38744
+ apply(glyphIds, advances, pos) {
38745
+ const first = glyphIds[pos];
38746
+ const second = glyphIds[pos + 1];
38747
+ if (second === void 0) return null;
38748
+ if (coverage(first) < 0) return null;
38749
+ const c1 = classDef1(first);
38750
+ const c2 = classDef2(second);
38751
+ if (c1 >= class1Count || c2 >= class2Count) return null;
38752
+ const cellIndex = c1 * class2Count + c2;
38753
+ const v1 = value1[cellIndex];
38754
+ const v2 = value2[cellIndex];
38755
+ if (v1 === 0 && v2 === 0) return null;
38756
+ advances[pos] += v1;
38757
+ advances[pos + 1] += v2;
38758
+ return hasSecondAdjustment ? 2 : 1;
38759
+ }
38760
+ };
38761
+ }
38762
+ return null;
38763
+ }
38764
+ function parseSubtable(reader, offset, lookupType) {
38765
+ switch (lookupType) {
38766
+ case 1:
38767
+ return parseSinglePos(reader, offset);
38768
+ case 2:
38769
+ return parsePairPos(reader, offset);
38770
+ case 9: {
38771
+ reader.seek(offset);
38772
+ const format = reader.u16();
38773
+ if (format !== 1) return null;
38774
+ const extensionType = reader.u16();
38775
+ const extensionOffset = offset + reader.u32();
38776
+ return parseSubtable(reader, extensionOffset, extensionType);
38777
+ }
38778
+ default:
38779
+ return null;
38438
38780
  }
38439
- return featureIndices;
38440
38781
  }
38441
38782
  function parseLookupList(reader, lookupListOffset) {
38442
38783
  reader.seek(lookupListOffset);
@@ -38465,17 +38806,71 @@ function parseLookupList(reader, lookupListOffset) {
38465
38806
  return { lookupType, subtables };
38466
38807
  });
38467
38808
  }
38809
+ class GposTable {
38810
+ constructor(lookups, featureToLookups) {
38811
+ this.lookups = lookups;
38812
+ this.featureToLookups = featureToLookups;
38813
+ }
38814
+ /** Parses GPOS bytes (table-relative). Returns null on any structural failure. */
38815
+ static parse(bytes) {
38816
+ try {
38817
+ const reader = new BinaryReader(bytes);
38818
+ const { featureToLookups, lookupListOffset } = parseLayoutTableHeader(reader);
38819
+ const lookups = parseLookupList(reader, lookupListOffset);
38820
+ return new GposTable(lookups, featureToLookups);
38821
+ } catch {
38822
+ return null;
38823
+ }
38824
+ }
38825
+ /** True when at least one of the requested feature tags exists in this font. */
38826
+ hasAnyFeature(tags) {
38827
+ return tags.some((tag) => this.featureToLookups.has(tag));
38828
+ }
38829
+ /**
38830
+ * Applies the positioning lookups for the given features across `advances`
38831
+ * (font design units), mutating it in place. `glyphIds[i]` is the glyph at
38832
+ * advance slot `i`; the arrays are parallel and equal length.
38833
+ */
38834
+ position(glyphIds, advances, tags) {
38835
+ const lookupIndices = collectLookupIndices(this.featureToLookups, tags);
38836
+ for (const lookupIndex of lookupIndices) {
38837
+ const lookup = this.lookups[lookupIndex];
38838
+ if (!lookup) continue;
38839
+ let pos = 0;
38840
+ while (pos < glyphIds.length) {
38841
+ let advance = null;
38842
+ for (const subtable of lookup.subtables) {
38843
+ advance = subtable.apply(glyphIds, advances, pos);
38844
+ if (advance !== null) break;
38845
+ }
38846
+ pos += advance && advance > 0 ? advance : 1;
38847
+ }
38848
+ }
38849
+ }
38850
+ }
38851
+ const GPOS_FEATURE_TAGS = /* @__PURE__ */ new Set(["kern"]);
38468
38852
  class OpenTypeLayouter {
38469
38853
  constructor(font) {
38470
38854
  __publicField(this, "gsub");
38855
+ __publicField(this, "gpos");
38471
38856
  this.font = font;
38472
- const raw = font.getRawTableData("GSUB");
38473
- this.gsub = raw ? GsubTable.parse(raw) : null;
38857
+ const gsubRaw = font.getRawTableData("GSUB");
38858
+ this.gsub = gsubRaw ? GsubTable.parse(gsubRaw) : null;
38859
+ const gposRaw = font.getRawTableData("GPOS");
38860
+ this.gpos = gposRaw ? GposTable.parse(gposRaw) : null;
38474
38861
  }
38475
- /** True when the font exposes a GSUB table the shaper could use. */
38862
+ /** True when the font exposes a GSUB table the substitution shaper could use. */
38476
38863
  get hasGsub() {
38477
38864
  return this.gsub !== null;
38478
38865
  }
38866
+ /** True when the font exposes a GPOS table the kerning shaper could use. */
38867
+ get hasGpos() {
38868
+ return this.gpos !== null;
38869
+ }
38870
+ /** True when either shaping table is present (so this layouter adds value). */
38871
+ get hasShaping() {
38872
+ return this.gsub !== null || this.gpos !== null;
38873
+ }
38479
38874
  layout(text, features) {
38480
38875
  const buffer = [];
38481
38876
  for (const char of text) {
@@ -38486,21 +38881,35 @@ class OpenTypeLayouter {
38486
38881
  codePoints: glyphId === 0 ? [65533] : [codePoint]
38487
38882
  });
38488
38883
  }
38489
- if (this.gsub && features && features.length > 0) {
38490
- this.gsub.shape(buffer, features);
38884
+ const gsubTags = (features == null ? void 0 : features.filter((tag) => !GPOS_FEATURE_TAGS.has(tag))) ?? [];
38885
+ const gposTags = (features == null ? void 0 : features.filter((tag) => GPOS_FEATURE_TAGS.has(tag))) ?? [];
38886
+ if (this.gsub && gsubTags.length > 0) {
38887
+ this.gsub.shape(buffer, gsubTags);
38888
+ }
38889
+ const advances = buffer.map(
38890
+ (glyph) => this.font.advanceWidthForGlyph(glyph.id)
38891
+ );
38892
+ if (this.gpos && gposTags.length > 0) {
38893
+ this.gpos.position(
38894
+ buffer.map((glyph) => glyph.id),
38895
+ advances,
38896
+ gposTags
38897
+ );
38491
38898
  }
38492
38899
  const glyphs = [];
38493
38900
  const positions = [];
38494
38901
  let advanceWidth = 0;
38495
- for (const glyph of buffer) {
38496
- const glyphAdvance = this.font.advanceWidthForGlyph(glyph.id);
38902
+ for (let i = 0; i < buffer.length; i += 1) {
38903
+ const glyph = buffer[i];
38904
+ const nominalAdvance = this.font.advanceWidthForGlyph(glyph.id);
38905
+ const positionedAdvance = advances[i];
38497
38906
  glyphs.push({
38498
38907
  id: glyph.id,
38499
38908
  codePoints: glyph.codePoints,
38500
- advanceWidth: glyphAdvance
38909
+ advanceWidth: nominalAdvance
38501
38910
  });
38502
- positions.push({ xAdvance: glyphAdvance });
38503
- advanceWidth += glyphAdvance;
38911
+ positions.push({ xAdvance: positionedAdvance });
38912
+ advanceWidth += positionedAdvance;
38504
38913
  }
38505
38914
  return { glyphs, positions, advanceWidth };
38506
38915
  }
@@ -38721,7 +39130,7 @@ function parseEmbeddedFontSync(sourceBytes) {
38721
39130
  }
38722
39131
  function createPdfEmbeddableFont(program) {
38723
39132
  const shaper = new OpenTypeLayouter(program);
38724
- const layouter = shaper.hasGsub ? shaper : new SimpleTextLayouter(program);
39133
+ const layouter = shaper.hasShaping ? shaper : new SimpleTextLayouter(program);
38725
39134
  return {
38726
39135
  program,
38727
39136
  layouter,
@@ -38948,7 +39357,97 @@ class PdfImageTable {
38948
39357
  );
38949
39358
  }
38950
39359
  }
38951
- function serializePdfDocument(pages, fonts, images) {
39360
+ class PdfShadingTable {
39361
+ constructor() {
39362
+ __publicField(this, "shadings", []);
39363
+ }
39364
+ /** Registers a gradient (PDF space) and returns its `/Shading` resource name. */
39365
+ register(spec) {
39366
+ const name = `Sh${this.shadings.length + 1}`;
39367
+ this.shadings.push({ name, spec });
39368
+ return name;
39369
+ }
39370
+ /** Emits each gradient's Function + Shading objects, keyed by resource name. */
39371
+ buildShadingObjects(addObject) {
39372
+ const ids = /* @__PURE__ */ new Map();
39373
+ for (const { name, spec } of this.shadings) {
39374
+ ids.set(name, this.addShadingObject(spec, addObject));
39375
+ }
39376
+ return ids;
39377
+ }
39378
+ addShadingObject(spec, addObject) {
39379
+ const stops = normalizeStops(spec.stops);
39380
+ const functionObjectId = addObject(buildFunctionObject(stops));
39381
+ return addObject(
39382
+ [
39383
+ "<< /ShadingType 2",
39384
+ "/ColorSpace /DeviceRGB",
39385
+ `/Coords [${[spec.x0, spec.y0, spec.x1, spec.y1].map(formatNumber).join(" ")}]`,
39386
+ "/Domain [0 1]",
39387
+ `/Function ${functionObjectId} 0 R`,
39388
+ "/Extend [true true]",
39389
+ ">>"
39390
+ ].join("\n")
39391
+ );
39392
+ }
39393
+ }
39394
+ function normalizeStops(stops) {
39395
+ const resolved = stops.map((stop) => ({
39396
+ offset: Math.min(1, Math.max(0, stop.offset)),
39397
+ rgb: colorToRgb(stop.color, [0, 0, 0])
39398
+ })).sort((a, b) => a.offset - b.offset);
39399
+ if (resolved.length === 0) {
39400
+ return [
39401
+ { offset: 0, rgb: [0, 0, 0] },
39402
+ { offset: 1, rgb: [0, 0, 0] }
39403
+ ];
39404
+ }
39405
+ if (resolved.length === 1) {
39406
+ return [
39407
+ { offset: 0, rgb: resolved[0].rgb },
39408
+ { offset: 1, rgb: resolved[0].rgb }
39409
+ ];
39410
+ }
39411
+ resolved[0].offset = 0;
39412
+ resolved[resolved.length - 1].offset = 1;
39413
+ for (let i = 1; i < resolved.length - 1; i += 1) {
39414
+ const min = resolved[i - 1].offset + 1e-4;
39415
+ if (resolved[i].offset <= min) {
39416
+ resolved[i].offset = Math.min(1 - 1e-4, min);
39417
+ }
39418
+ }
39419
+ return resolved;
39420
+ }
39421
+ function exponentialFunction(c0, c1) {
39422
+ return [
39423
+ "<< /FunctionType 2",
39424
+ "/Domain [0 1]",
39425
+ `/C0 [${c0.map(formatNumber).join(" ")}]`,
39426
+ `/C1 [${c1.map(formatNumber).join(" ")}]`,
39427
+ "/N 1",
39428
+ ">>"
39429
+ ].join("\n");
39430
+ }
39431
+ function buildFunctionObject(stops) {
39432
+ if (stops.length === 2) {
39433
+ return exponentialFunction(stops[0].rgb, stops[1].rgb);
39434
+ }
39435
+ const subFunctions = [];
39436
+ for (let i = 0; i < stops.length - 1; i += 1) {
39437
+ subFunctions.push(exponentialFunction(stops[i].rgb, stops[i + 1].rgb));
39438
+ }
39439
+ const bounds = stops.slice(1, -1).map((stop) => formatNumber(stop.offset));
39440
+ const encode = stops.slice(0, -1).map(() => "0 1").join(" ");
39441
+ return [
39442
+ "<< /FunctionType 3",
39443
+ "/Domain [0 1]",
39444
+ `/Functions [${subFunctions.join(" ")}]`,
39445
+ `/Bounds [${bounds.join(" ")}]`,
39446
+ `/Encode [${encode}]`,
39447
+ ">>"
39448
+ ].join("\n");
39449
+ }
39450
+ function serializePdfDocument(pages, fonts, images, shadings) {
38952
39451
  const objects = [];
38953
39452
  const addObject = (body2) => {
38954
39453
  const id = objects.length + 1;
@@ -38959,6 +39458,7 @@ function serializePdfDocument(pages, fonts, images) {
38959
39458
  const pagesObjectId = addObject("");
38960
39459
  const { resourceXml: fontResourceXml } = fonts.buildFontObjects(addObject);
38961
39460
  const imageObjectIds = images.buildImageObjects(addObject);
39461
+ const shadingObjectIds = shadings.buildShadingObjects(addObject);
38962
39462
  const pageObjectIds = [];
38963
39463
  for (const page of pages) {
38964
39464
  const stream = `${page.commands.join("\n")}
@@ -38973,12 +39473,17 @@ ${stream}endstream`
38973
39473
  return objectId ? `/${resourceName} ${objectId} 0 R` : "";
38974
39474
  }).filter(Boolean).join(" ");
38975
39475
  const xObjectResourceXml = imageResourceXml ? ` /XObject << ${imageResourceXml} >>` : "";
39476
+ const shadingResourceXml = Array.from(page.shadingResourceNames).map((resourceName) => {
39477
+ const objectId = shadingObjectIds.get(resourceName);
39478
+ return objectId ? `/${resourceName} ${objectId} 0 R` : "";
39479
+ }).filter(Boolean).join(" ");
39480
+ const shadingResourceDictXml = shadingResourceXml ? ` /Shading << ${shadingResourceXml} >>` : "";
38976
39481
  const pageObjectId = addObject(
38977
39482
  [
38978
39483
  "<< /Type /Page",
38979
39484
  `/Parent ${pagesObjectId} 0 R`,
38980
39485
  `/MediaBox [0 0 ${formatNumber(page.width)} ${formatNumber(page.height)}]`,
38981
- `/Resources << /Font << ${fontResourceXml} >>${xObjectResourceXml} >>`,
39486
+ `/Resources << /Font << ${fontResourceXml} >>${xObjectResourceXml}${shadingResourceDictXml} >>`,
38982
39487
  `/Contents ${contentObjectId} 0 R`,
38983
39488
  ">>"
38984
39489
  ].join("\n")
@@ -39026,6 +39531,7 @@ class OasisPdfWriter {
39026
39531
  __publicField(this, "streams", []);
39027
39532
  __publicField(this, "fonts");
39028
39533
  __publicField(this, "images", new PdfImageTable());
39534
+ __publicField(this, "shadings", new PdfShadingTable());
39029
39535
  this.fonts = new PdfFontTable(fontResources);
39030
39536
  }
39031
39537
  registerFontResource(resource) {
@@ -39036,10 +39542,13 @@ class OasisPdfWriter {
39036
39542
  width: Math.max(1, size.width),
39037
39543
  height: Math.max(1, size.height),
39038
39544
  commands: [],
39039
- imageResourceNames: /* @__PURE__ */ new Set()
39545
+ imageResourceNames: /* @__PURE__ */ new Set(),
39546
+ shadingResourceNames: /* @__PURE__ */ new Set()
39040
39547
  };
39041
39548
  this.pages.push(page);
39042
- this.streams.push(new PdfContentStream(page, this.fonts, this.images));
39549
+ this.streams.push(
39550
+ new PdfContentStream(page, this.fonts, this.images, this.shadings)
39551
+ );
39043
39552
  return this.pages.length - 1;
39044
39553
  }
39045
39554
  getPageCount() {
@@ -39077,6 +39586,15 @@ class OasisPdfWriter {
39077
39586
  var _a;
39078
39587
  (_a = this.streams[pageIndex]) == null ? void 0 : _a.drawText(options);
39079
39588
  }
39589
+ /**
39590
+ * Registers an axial (linear) gradient on a page for use as a glyph fill, and
39591
+ * returns its shading resource name to pass as `gradientShadingName` on a
39592
+ * subsequent `drawText`. Coordinates are in the writer's top-left point space.
39593
+ */
39594
+ registerAxialGradient(pageIndex, gradient) {
39595
+ var _a;
39596
+ return ((_a = this.streams[pageIndex]) == null ? void 0 : _a.registerAxialGradient(gradient)) ?? null;
39597
+ }
39080
39598
  registerImageResource(resource) {
39081
39599
  return this.images.registerImageResource(resource);
39082
39600
  }
@@ -39095,7 +39613,12 @@ class OasisPdfWriter {
39095
39613
  if (this.pages.length === 0) {
39096
39614
  this.addPage({ width: 612, height: 792 });
39097
39615
  }
39098
- return serializePdfDocument(this.pages, this.fonts, this.images);
39616
+ return serializePdfDocument(
39617
+ this.pages,
39618
+ this.fonts,
39619
+ this.images,
39620
+ this.shadings
39621
+ );
39099
39622
  }
39100
39623
  }
39101
39624
  const FOOTNOTE_BLOCK_GAP_PX = 2;