@json-to-office/core-docx 0.37.0 → 0.38.1

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.
@@ -2104,8 +2104,7 @@ import {
2104
2104
  PageNumber as PageNumber2,
2105
2105
  ColumnBreak,
2106
2106
  TableLayoutType,
2107
- VerticalAlign,
2108
- Bookmark
2107
+ VerticalAlign
2109
2108
  } from "docx";
2110
2109
 
2111
2110
  // src/utils/imageUtils.ts
@@ -2158,6 +2157,7 @@ import { ImageRun } from "docx";
2158
2157
  var SVG_RASTER_SCALE = 3;
2159
2158
  var SVG_MAX_EDGE_PX = 4096;
2160
2159
  var SVG_MIN_EDGE_PX = 16;
2160
+ var SVG_MAX_TOTAL_PX = 1e6;
2161
2161
  var resvgModule;
2162
2162
  function loadResvg() {
2163
2163
  resvgModule ??= import("@resvg/resvg-js");
@@ -2169,6 +2169,13 @@ function toRasterPx(px) {
2169
2169
  Math.max(SVG_MIN_EDGE_PX, Math.round(px * SVG_RASTER_SCALE))
2170
2170
  );
2171
2171
  }
2172
+ function capToPixelBudget(edge, aspect) {
2173
+ if (!Number.isFinite(aspect) || aspect <= 0) return void 0;
2174
+ const total = edge * edge * aspect;
2175
+ if (total <= SVG_MAX_TOTAL_PX) return edge;
2176
+ const scaled = Math.floor(edge * Math.sqrt(SVG_MAX_TOTAL_PX / total));
2177
+ return scaled >= SVG_MIN_EDGE_PX ? scaled : void 0;
2178
+ }
2172
2179
  async function rasterizeSvgFallback(svg, transformation) {
2173
2180
  let Resvg;
2174
2181
  try {
@@ -2185,8 +2192,22 @@ async function rasterizeSvgFallback(svg, transformation) {
2185
2192
  const markup = svg.toString("utf-8");
2186
2193
  const probeImage = new Resvg(markup);
2187
2194
  const wide = probeImage.width / probeImage.height > transformation.width / transformation.height;
2188
- const fitTo = wide ? { mode: "height", value: toRasterPx(transformation.height) } : { mode: "width", value: toRasterPx(transformation.width) };
2189
- return Buffer.from(new Resvg(markup, { fitTo }).render().asPng());
2195
+ const mode = wide ? "height" : "width";
2196
+ const value = capToPixelBudget(
2197
+ toRasterPx(wide ? transformation.height : transformation.width),
2198
+ wide ? probeImage.width / probeImage.height : probeImage.height / probeImage.width
2199
+ );
2200
+ if (value === void 0) {
2201
+ reportWarning(
2202
+ "image",
2203
+ "IMAGE_SVG_RASTER_SKIPPED",
2204
+ "Inline SVG is too far from square to rasterize within the fallback pixel budget, so its fallback only renders in Word 2016+."
2205
+ );
2206
+ return void 0;
2207
+ }
2208
+ return Buffer.from(
2209
+ new Resvg(markup, { fitTo: { mode, value } }).render().asPng()
2210
+ );
2190
2211
  } catch (error) {
2191
2212
  reportWarning(
2192
2213
  "image",
@@ -3700,6 +3721,11 @@ function getStyleIdForLevel(level) {
3700
3721
 
3701
3722
  // src/utils/bookmarkRegistry.ts
3702
3723
  import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
3724
+ import { BookmarkStart, BookmarkEnd } from "docx";
3725
+ var CONTENT_LINK_ID_BASE = 2e6;
3726
+ function createState() {
3727
+ return { bookmarks: /* @__PURE__ */ new Map(), nextLinkId: CONTENT_LINK_ID_BASE + 1 };
3728
+ }
3703
3729
  function slugifyBookmarkText(text) {
3704
3730
  return text.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").substring(0, 40);
3705
3731
  }
@@ -3712,14 +3738,14 @@ function dedupeBookmarkId(base, taken) {
3712
3738
  return id;
3713
3739
  }
3714
3740
  var BookmarkRegistry = class {
3715
- fallback = { bookmarks: /* @__PURE__ */ new Map() };
3741
+ fallback = createState();
3716
3742
  scopes = new AsyncLocalStorage3();
3717
3743
  get state() {
3718
3744
  return this.scopes.getStore() ?? this.fallback;
3719
3745
  }
3720
3746
  /** Run work with an isolated registry that follows its async call chain. */
3721
3747
  runScoped(callback) {
3722
- return this.scopes.run({ bookmarks: /* @__PURE__ */ new Map() }, callback);
3748
+ return this.scopes.run(createState(), callback);
3723
3749
  }
3724
3750
  /**
3725
3751
  * Register a bookmark
@@ -3732,6 +3758,19 @@ var BookmarkRegistry = class {
3732
3758
  }
3733
3759
  this.state.bookmarks.set(id, { id, title, type });
3734
3760
  }
3761
+ /**
3762
+ * A `w:id` no other bookmark in this document will use.
3763
+ *
3764
+ * docx's own `Bookmark` cannot supply one: its constructor builds a fresh
3765
+ * id generator per instance (`bookmarkUniqueNumericIdGen()`), so every
3766
+ * bookmark it emits carries `w:id="1"`. Reported upstream as dolanmiu/docx
3767
+ * #3478, still unreleased as of 9.7.1. Duplicated ids leave the start/end
3768
+ * pairing ambiguous, which is why a `REF` field could not read a target's
3769
+ * text even though navigating to it by name worked.
3770
+ */
3771
+ allocateLinkId() {
3772
+ return this.state.nextLinkId++;
3773
+ }
3735
3774
  /**
3736
3775
  * Generate a unique bookmark ID from text
3737
3776
  * Converts text to a URL-friendly format
@@ -3781,6 +3820,14 @@ var BookmarkRegistry = class {
3781
3820
  }
3782
3821
  };
3783
3822
  var globalBookmarkRegistry = new BookmarkRegistry();
3823
+ function createBookmarkedContent(name, children) {
3824
+ const linkId = globalBookmarkRegistry.allocateLinkId();
3825
+ return [
3826
+ new BookmarkStart(name, linkId),
3827
+ ...children,
3828
+ new BookmarkEnd(linkId)
3829
+ ];
3830
+ }
3784
3831
 
3785
3832
  // src/utils/revisionUtils.ts
3786
3833
  import { TextRun as TextRun3, InsertedTextRun, DeletedTextRun } from "docx";
@@ -3949,7 +3996,7 @@ import { Paragraph, TextRun as TextRun4 } from "docx";
3949
3996
  import { AsyncLocalStorage as AsyncLocalStorage5 } from "async_hooks";
3950
3997
  var DEFAULT_COMMENT_AUTHOR = "json-to-office";
3951
3998
  var DEFAULT_COMMENT_DATE = "1970-01-01T00:00:00Z";
3952
- function createState() {
3999
+ function createState2() {
3953
4000
  return { counter: 0, comments: [], hasResolved: false };
3954
4001
  }
3955
4002
  function deriveInitials(author) {
@@ -3960,14 +4007,14 @@ function bodyParagraphs(text) {
3960
4007
  return normalizeUnicodeText(text).split("\n").map((line) => new Paragraph({ children: [new TextRun4({ text: line })] }));
3961
4008
  }
3962
4009
  var CommentRegistry = class {
3963
- fallback = createState();
4010
+ fallback = createState2();
3964
4011
  scopes = new AsyncLocalStorage5();
3965
4012
  get state() {
3966
4013
  return this.scopes.getStore() ?? this.fallback;
3967
4014
  }
3968
4015
  /** Run work with an isolated registry that follows its async call chain. */
3969
4016
  runScoped(callback) {
3970
- return this.scopes.run(createState(), callback);
4017
+ return this.scopes.run(createState2(), callback);
3971
4018
  }
3972
4019
  /**
3973
4020
  * Register a comment thread and return every id its anchors must carry — the
@@ -4035,7 +4082,7 @@ var CommentRegistry = class {
4035
4082
  state.comments.length = 0;
4036
4083
  state.hasResolved = false;
4037
4084
  } else {
4038
- this.fallback = createState();
4085
+ this.fallback = createState2();
4039
4086
  }
4040
4087
  }
4041
4088
  };
@@ -4057,7 +4104,7 @@ function closeCommentRange(ids) {
4057
4104
  // src/utils/noteRegistry.ts
4058
4105
  import { Paragraph as Paragraph2, TextRun as TextRun6 } from "docx";
4059
4106
  import { AsyncLocalStorage as AsyncLocalStorage6 } from "async_hooks";
4060
- function createState2() {
4107
+ function createState3() {
4061
4108
  return {
4062
4109
  footnoteCounter: 0,
4063
4110
  endnoteCounter: 0,
@@ -4074,14 +4121,14 @@ function bodyParagraphs2(text, style) {
4074
4121
  );
4075
4122
  }
4076
4123
  var NoteRegistry = class {
4077
- fallback = createState2();
4124
+ fallback = createState3();
4078
4125
  scopes = new AsyncLocalStorage6();
4079
4126
  get state() {
4080
4127
  return this.scopes.getStore() ?? this.fallback;
4081
4128
  }
4082
4129
  /** Run work with an isolated registry that follows its async call chain. */
4083
4130
  runScoped(callback) {
4084
- return this.scopes.run(createState2(), callback);
4131
+ return this.scopes.run(createState3(), callback);
4085
4132
  }
4086
4133
  /**
4087
4134
  * Register a footnote body and return the id its reference must use. Ids are
@@ -4125,7 +4172,7 @@ var NoteRegistry = class {
4125
4172
  delete state.endnotes[key];
4126
4173
  }
4127
4174
  } else {
4128
- this.fallback = createState2();
4175
+ this.fallback = createState3();
4129
4176
  }
4130
4177
  }
4131
4178
  };
@@ -4291,10 +4338,7 @@ function createText(content, theme, themeName, options = {}) {
4291
4338
  "paragraph"
4292
4339
  );
4293
4340
  children.push(
4294
- new Bookmark({
4295
- id: options.bookmarkId,
4296
- children: revisionRuns
4297
- })
4341
+ ...createBookmarkedContent(options.bookmarkId, revisionRuns)
4298
4342
  );
4299
4343
  } else {
4300
4344
  children.push(...revisionRuns);
@@ -4317,12 +4361,7 @@ function createText(content, theme, themeName, options = {}) {
4317
4361
  normalizedContent,
4318
4362
  "paragraph"
4319
4363
  );
4320
- children.push(
4321
- new Bookmark({
4322
- id: options.bookmarkId,
4323
- children: textRuns
4324
- })
4325
- );
4364
+ children.push(...createBookmarkedContent(options.bookmarkId, textRuns));
4326
4365
  } else {
4327
4366
  children.push(...textRuns);
4328
4367
  }
@@ -4483,10 +4522,7 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4483
4522
  "heading"
4484
4523
  );
4485
4524
  children.push(
4486
- new Bookmark({
4487
- id: options.bookmarkId,
4488
- children: revisionRuns
4489
- })
4525
+ ...createBookmarkedContent(options.bookmarkId, revisionRuns)
4490
4526
  );
4491
4527
  } else {
4492
4528
  children.push(...revisionRuns);
@@ -4509,10 +4545,7 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4509
4545
  headingTextChildren.push(...makeHeadingRuns(normalizedText));
4510
4546
  }
4511
4547
  children.push(
4512
- new Bookmark({
4513
- id: options.bookmarkId,
4514
- children: headingTextChildren
4515
- })
4548
+ ...createBookmarkedContent(options.bookmarkId, headingTextChildren)
4516
4549
  );
4517
4550
  } else {
4518
4551
  if (hasInlineSyntax) {
@@ -4704,9 +4737,7 @@ function createList(items, _theme, _themeName, options = {}) {
4704
4737
  let itemContent = textRuns;
4705
4738
  if (itemId) {
4706
4739
  globalBookmarkRegistry.register(itemId, itemText, "list-item");
4707
- itemContent = [
4708
- new Bookmark({ id: itemId, children: textRuns })
4709
- ];
4740
+ itemContent = createBookmarkedContent(itemId, textRuns);
4710
4741
  }
4711
4742
  const paragraphChildren = [
4712
4743
  ...commentAnchor && index === firstRendered ? commentAnchor.start : [],
@@ -6701,8 +6732,8 @@ import {
6701
6732
  Paragraph as Paragraph9,
6702
6733
  TextRun as TextRun9,
6703
6734
  AlignmentType as AlignmentType5,
6704
- BookmarkStart as BookmarkStart2,
6705
- BookmarkEnd as BookmarkEnd2
6735
+ BookmarkStart as BookmarkStart3,
6736
+ BookmarkEnd as BookmarkEnd3
6706
6737
  } from "docx";
6707
6738
  init_defaults();
6708
6739
  init_styleHelpers();
@@ -7911,23 +7942,23 @@ async function renderTableComponent(component, theme, themeName) {
7911
7942
  }
7912
7943
 
7913
7944
  // src/components/section.ts
7914
- import { Paragraph as Paragraph6, BookmarkStart, BookmarkEnd } from "docx";
7945
+ import { Paragraph as Paragraph6, BookmarkStart as BookmarkStart2, BookmarkEnd as BookmarkEnd2 } from "docx";
7915
7946
 
7916
7947
  // src/core/sectionBookmarks.ts
7917
7948
  import { AsyncLocalStorage as AsyncLocalStorage8 } from "async_hooks";
7918
7949
  var NESTED_LINK_ID_BASE = 1e6;
7919
- function createState3() {
7950
+ function createState4() {
7920
7951
  return { nextNested: 1, resolved: /* @__PURE__ */ new WeakMap() };
7921
7952
  }
7922
7953
  var SectionBookmarkRegistry = class {
7923
- fallback = createState3();
7954
+ fallback = createState4();
7924
7955
  scopes = new AsyncLocalStorage8();
7925
7956
  get state() {
7926
7957
  return this.scopes.getStore() ?? this.fallback;
7927
7958
  }
7928
7959
  /** Run work with an isolated registry that follows its async call chain. */
7929
7960
  runScoped(callback) {
7930
- return this.scopes.run(createState3(), callback);
7961
+ return this.scopes.run(createState4(), callback);
7931
7962
  }
7932
7963
  /**
7933
7964
  * The bookmark for a layout section, derived from its ordinal.
@@ -7958,7 +7989,7 @@ var SectionBookmarkRegistry = class {
7958
7989
  /** Test-only: reset the unscoped fallback counters. */
7959
7990
  clear() {
7960
7991
  if (!this.scopes.getStore()) {
7961
- this.fallback = createState3();
7992
+ this.fallback = createState4();
7962
7993
  }
7963
7994
  }
7964
7995
  };
@@ -7971,7 +8002,7 @@ async function renderSectionComponent(component, theme, themeName, context) {
7971
8002
  const { id: sectionBookmarkId, linkId: bookmarkLinkId } = globalSectionBookmarkRegistry.forSectionComponent(component);
7972
8003
  elements.push(
7973
8004
  new Paragraph6({
7974
- children: [new BookmarkStart(sectionBookmarkId, bookmarkLinkId)],
8005
+ children: [new BookmarkStart2(sectionBookmarkId, bookmarkLinkId)],
7975
8006
  spacing: {
7976
8007
  before: 0,
7977
8008
  after: 0,
@@ -7999,7 +8030,7 @@ async function renderSectionComponent(component, theme, themeName, context) {
7999
8030
  }
8000
8031
  elements.push(
8001
8032
  new Paragraph6({
8002
- children: [new BookmarkEnd(bookmarkLinkId)],
8033
+ children: [new BookmarkEnd2(bookmarkLinkId)],
8003
8034
  spacing: {
8004
8035
  before: 0,
8005
8036
  after: 0,
@@ -9036,7 +9067,7 @@ async function renderSection(section, theme, themeName, context, sectionOrdinal,
9036
9067
  if (sectionBookmarkId && isFirstLayoutOfUserSection && sharedLinkId !== void 0) {
9037
9068
  elements.push(
9038
9069
  new Paragraph9({
9039
- children: [new BookmarkStart2(sectionBookmarkId, sharedLinkId)],
9070
+ children: [new BookmarkStart3(sectionBookmarkId, sharedLinkId)],
9040
9071
  spacing: {
9041
9072
  before: 0,
9042
9073
  after: 0,
@@ -9061,7 +9092,7 @@ async function renderSection(section, theme, themeName, context, sectionOrdinal,
9061
9092
  if (closeBookmark && sharedLinkId !== void 0) {
9062
9093
  elements.push(
9063
9094
  new Paragraph9({
9064
- children: [new BookmarkEnd2(sharedLinkId)]
9095
+ children: [new BookmarkEnd3(sharedLinkId)]
9065
9096
  })
9066
9097
  );
9067
9098
  }