ooxml-core 0.6.0 → 0.7.0

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.
@@ -148,10 +148,10 @@ function inspectZip(bytes, limits, check) {
148
148
  const name = utf8.decode(bytes.subarray(offset + 46, offset + 46 + nameLength));
149
149
  const directory = name.endsWith("/");
150
150
  safePath(name, directory);
151
- const key = decodePath(name).replace(/\/$/, "");
152
- if (entries.has(name) || canonical.has(key))
151
+ const key2 = decodePath(name).replace(/\/$/, "");
152
+ if (entries.has(name) || canonical.has(key2))
153
153
  fail("DUPLICATE_ENTRY", `Duplicate ZIP path: ${name}`);
154
- canonical.add(key);
154
+ canonical.add(key2);
155
155
  extras(offset + 46 + nameLength, extraLength, name);
156
156
  if (size > limits.maxEntryBytes) fail("LIMIT_ENTRY", `ZIP entry is too large: ${name}`);
157
157
  total += size;
@@ -296,9 +296,9 @@ function inspectXml(xml, limits, check, encoding = "utf-8") {
296
296
  const colon = attr2.indexOf(":");
297
297
  const uri = colon < 0 ? "" : namespaces[attr2.slice(0, colon)];
298
298
  if (uri === void 0) fail("INVALID_XML", "Unbound XML attribute prefix");
299
- const key = `${uri}\0${attr2.slice(colon + 1)}`;
300
- if (expanded.has(key)) fail("INVALID_XML", "Duplicate expanded XML attribute name");
301
- expanded.add(key);
299
+ const key2 = `${uri}\0${attr2.slice(colon + 1)}`;
300
+ if (expanded.has(key2)) fail("INVALID_XML", "Duplicate expanded XML attribute name");
301
+ expanded.add(key2);
302
302
  }
303
303
  if (!stack.length && ++roots > 1) fail("INVALID_XML", "Multiple XML roots");
304
304
  if (stack.length + 1 > limits.maxXmlDepth) fail("LIMIT_XML", "XML depth limit exceeded");
@@ -352,9 +352,9 @@ function inspectNamespaces(root, check) {
352
352
  if (!element) break;
353
353
  const seen = /* @__PURE__ */ new Set();
354
354
  for (const attr2 of Array.from(element.attributes)) {
355
- const key = `${attr2.namespaceURI ?? ""}\0${attr2.localName}`;
356
- if (seen.has(key)) fail("INVALID_XML", "Duplicate expanded XML attribute name");
357
- seen.add(key);
355
+ const key2 = `${attr2.namespaceURI ?? ""}\0${attr2.localName}`;
356
+ if (seen.has(key2)) fail("INVALID_XML", "Duplicate expanded XML attribute name");
357
+ seen.add(key2);
358
358
  if (attr2.namespaceURI === xmlns && (attr2.name === "xmlns:xmlns" || attr2.value === xmlns || (attr2.name === "xmlns:xml" ? attr2.value !== xml : attr2.value === xml) || attr2.name !== "xmlns" && !attr2.value))
359
359
  fail("INVALID_XML", "Invalid reserved XML namespace binding");
360
360
  }
@@ -694,21 +694,21 @@ function readShapes(node) {
694
694
  }
695
695
  function mergeCells(base, local) {
696
696
  const result = new Map(base);
697
- for (const [key, value] of local) {
697
+ for (const [key2, value] of local) {
698
698
  if (value.formula === "Inh" && value.value === void 0 && value.error === void 0) continue;
699
- const unit = value.unit ?? base.get(key)?.unit;
700
- result.set(key, { ...value, ...unit === void 0 ? {} : { unit } });
699
+ const unit = value.unit ?? base.get(key2)?.unit;
700
+ result.set(key2, { ...value, ...unit === void 0 ? {} : { unit } });
701
701
  }
702
702
  return result;
703
703
  }
704
704
  function mergeSheets(base, local) {
705
705
  const sections = new Map(base.sections);
706
- for (const [key, section] of local.sections) {
706
+ for (const [key2, section] of local.sections) {
707
707
  if (section.deleted) {
708
- sections.delete(key);
708
+ sections.delete(key2);
709
709
  continue;
710
710
  }
711
- const previous = sections.get(key);
711
+ const previous = sections.get(key2);
712
712
  const rows = new Map(previous?.rows);
713
713
  for (const [index, row] of section.rows) {
714
714
  if (row.deleted) {
@@ -723,7 +723,7 @@ function mergeSheets(base, local) {
723
723
  cells: mergeCells(old?.cells ?? /* @__PURE__ */ new Map(), row.cells)
724
724
  });
725
725
  }
726
- sections.set(key, {
726
+ sections.set(key2, {
727
727
  ...section,
728
728
  cells: mergeCells(previous?.cells ?? /* @__PURE__ */ new Map(), section.cells),
729
729
  rows
@@ -731,8 +731,8 @@ function mergeSheets(base, local) {
731
731
  }
732
732
  return { cells: mergeCells(base.cells, local.cells), sections };
733
733
  }
734
- function number(cells, name, fallback, report) {
735
- const cell = cells.get(name);
734
+ function number(cells3, name, fallback, report) {
735
+ const cell = cells3.get(name);
736
736
  if (!cell) return fallback;
737
737
  const raw = cell.value?.trim();
738
738
  if (raw && raw.length <= 128 && /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(raw)) {
@@ -748,8 +748,8 @@ function number(cells, name, fallback, report) {
748
748
  var sectionRows = (sheet, name) => [...sheet.sections.values()].filter((s) => s.name === name && !s.deleted).flatMap((s) => [...s.rows.values()].filter((r) => !r.deleted));
749
749
  var emptySheet = () => ({ cells: /* @__PURE__ */ new Map(), sections: /* @__PURE__ */ new Map() });
750
750
  function reportCachedErrors(sheet, report) {
751
- const visit = (cells) => {
752
- for (const [name, cell] of cells) {
751
+ const visit = (cells3) => {
752
+ for (const [name, cell] of cells3) {
753
753
  if (cell.error !== void 0)
754
754
  report(
755
755
  "cached-cell-error",
@@ -889,16 +889,16 @@ function emfInputOptions(options) {
889
889
  const prototype = Object.getPrototypeOf(options);
890
890
  if (prototype !== null && prototype !== Object.prototype) return null;
891
891
  const snapshot = {};
892
- for (const key of Object.keys(
892
+ for (const key2 of Object.keys(
893
893
  VISIO_EMF_ADMISSION_LIMITS
894
894
  )) {
895
- const descriptor = Object.getOwnPropertyDescriptor(options, key);
895
+ const descriptor = Object.getOwnPropertyDescriptor(options, key2);
896
896
  if (!descriptor) continue;
897
897
  if (!("value" in descriptor)) return null;
898
898
  const value = descriptor.value;
899
899
  if (value === void 0) continue;
900
900
  if (typeof value !== "number") return null;
901
- snapshot[key] = value;
901
+ snapshot[key2] = value;
902
902
  }
903
903
  return snapshot;
904
904
  } catch {
@@ -918,12 +918,12 @@ var EmfAdmissionContext = class {
918
918
  const cap = options.maxDiagnostics;
919
919
  if (cap !== void 0 && Number.isInteger(cap) && cap > 0 && cap <= this.limits.maxDiagnostics)
920
920
  this.limits.maxDiagnostics = cap;
921
- for (const key of Object.keys(VISIO_EMF_ADMISSION_LIMITS)) {
922
- const value = options[key];
921
+ for (const key2 of Object.keys(VISIO_EMF_ADMISSION_LIMITS)) {
922
+ const value = options[key2];
923
923
  if (value === void 0) continue;
924
- if (!Number.isSafeInteger(value) || value < 1 || value > this.limits[key])
924
+ if (!Number.isSafeInteger(value) || value < 1 || value > this.limits[key2])
925
925
  this.emit("invalid", "limits", "Limits must be positive integers below the hard ceiling.");
926
- else this.limits[key] = value;
926
+ else this.limits[key2] = value;
927
927
  }
928
928
  }
929
929
  view;
@@ -999,8 +999,8 @@ var EmfAdmissionContext = class {
999
999
  });
1000
1000
  else this.omittedDiagnostics++;
1001
1001
  }
1002
- charge(units) {
1003
- this.metrics.allocationUnits += units;
1002
+ charge(units2) {
1003
+ this.metrics.allocationUnits += units2;
1004
1004
  return this.bound(
1005
1005
  this.metrics.allocationUnits,
1006
1006
  this.limits.maxAllocationUnits,
@@ -1678,23 +1678,23 @@ function record(value, allowed) {
1678
1678
  const result = /* @__PURE__ */ Object.create(null);
1679
1679
  const keys = Reflect.ownKeys(value);
1680
1680
  if (keys.length > allowed.length) unsafe("Unexpected object fields.");
1681
- for (const key of keys) {
1682
- if (typeof key !== "string" || !allowed.includes(key)) unsafe("Unexpected object field.");
1683
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
1681
+ for (const key2 of keys) {
1682
+ if (typeof key2 !== "string" || !allowed.includes(key2)) unsafe("Unexpected object field.");
1683
+ const descriptor = Object.getOwnPropertyDescriptor(value, key2);
1684
1684
  if (!descriptor || !("value" in descriptor) || !descriptor.enumerable)
1685
1685
  unsafe("Invalid data property.");
1686
- result[key] = descriptor.value;
1686
+ result[key2] = descriptor.value;
1687
1687
  }
1688
1688
  return result;
1689
1689
  }
1690
1690
  function limitsFor(options) {
1691
1691
  const result = { ...VISIO_FOREIGN_VECTOR_LIMITS };
1692
1692
  const values = record(options, Object.keys(result));
1693
- for (const key of Object.keys(values)) {
1694
- const value = values[key];
1695
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > result[key])
1693
+ for (const key2 of Object.keys(values)) {
1694
+ const value = values[key2];
1695
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > result[key2])
1696
1696
  unsafe("Limits must be positive integers no greater than the defaults.");
1697
- result[key] = value;
1697
+ result[key2] = value;
1698
1698
  }
1699
1699
  return result;
1700
1700
  }
@@ -2239,10 +2239,10 @@ function sanitize(root, limits) {
2239
2239
  charge(data2.tag);
2240
2240
  const allowed = clipping && data2.tag === "path" ? ["d", "clip-rule", ...BASE] : ATTRS[data2.tag];
2241
2241
  const attrs = record(data2.attrs, allowed);
2242
- for (const key of Object.keys(attrs)) {
2243
- charge(key);
2244
- if (typeof attrs[key] === "string") charge(attrs[key]);
2245
- else if (typeof attrs[key] !== "number") unsafe("Invalid attribute value type.");
2242
+ for (const key2 of Object.keys(attrs)) {
2243
+ charge(key2);
2244
+ if (typeof attrs[key2] === "string") charge(attrs[key2]);
2245
+ else if (typeof attrs[key2] !== "number") unsafe("Invalid attribute value type.");
2246
2246
  }
2247
2247
  return { tag: data2.tag, attrs, children: data2.children };
2248
2248
  }
@@ -2728,10 +2728,10 @@ function inspectVisioRasterImage(bytes, options = {}) {
2728
2728
  if (!options || typeof options !== "object" || Array.isArray(options))
2729
2729
  throw new VisioImageError("invalid-limit", "Image limits must be an options object.");
2730
2730
  const limits = { ...VISIO_RASTER_IMAGE_LIMITS, ...options };
2731
- for (const key of Object.keys(VISIO_RASTER_IMAGE_LIMITS)) {
2732
- const value = limits[key];
2733
- if (!Number.isSafeInteger(value) || value <= 0 || value > VISIO_RASTER_IMAGE_LIMITS[key])
2734
- throw new VisioImageError("invalid-limit", `Invalid raster image limit: ${key}.`);
2731
+ for (const key2 of Object.keys(VISIO_RASTER_IMAGE_LIMITS)) {
2732
+ const value = limits[key2];
2733
+ if (!Number.isSafeInteger(value) || value <= 0 || value > VISIO_RASTER_IMAGE_LIMITS[key2])
2734
+ throw new VisioImageError("invalid-limit", `Invalid raster image limit: ${key2}.`);
2735
2735
  }
2736
2736
  if (!(bytes instanceof Uint8Array)) invalid("Raster image bytes must be a Uint8Array.");
2737
2737
  if (bytes.byteLength > limits.maxImageBytes)
@@ -2773,8 +2773,8 @@ async function readVisioImage(pkg, sourcePart, foreignData, report, options = {}
2773
2773
  cache = /* @__PURE__ */ new Map();
2774
2774
  validationCache.set(pkg, cache);
2775
2775
  }
2776
- const key = `${rel.target}\0${limits.maxImageBytes}/${limits.maxPixels}/${limits.maxDimension}`;
2777
- let result = cache.get(key);
2776
+ const key2 = `${rel.target}\0${limits.maxImageBytes}/${limits.maxPixels}/${limits.maxDimension}`;
2777
+ let result = cache.get(key2);
2778
2778
  if (!result) {
2779
2779
  try {
2780
2780
  result = { ...inspect(bytes, limits), bytes };
@@ -2782,7 +2782,7 @@ async function readVisioImage(pkg, sourcePart, foreignData, report, options = {}
2782
2782
  if (!(error instanceof VisioImageError)) throw error;
2783
2783
  result = error;
2784
2784
  }
2785
- cache.set(key, result);
2785
+ cache.set(key2, result);
2786
2786
  }
2787
2787
  return result instanceof VisioImageError ? omit(result.code, result.message) : result;
2788
2788
  }
@@ -2909,21 +2909,21 @@ function drawingColor(node, colors, placeholder) {
2909
2909
  }
2910
2910
 
2911
2911
  // src/visio/theme-resolve.ts
2912
- var cellNumber = (cells, name) => integer(cells?.get(name)?.value);
2913
- function rootThemeSelected(cells, resources) {
2914
- const local = cellNumber(cells, "ColorSchemeIndex");
2912
+ var cellNumber = (cells3, name) => integer(cells3?.get(name)?.value);
2913
+ function rootThemeSelected(cells3, resources) {
2914
+ const local = cellNumber(cells3, "ColorSchemeIndex");
2915
2915
  return local === 0 || local === 65534 && cellNumber(resources.pageCells, "ColorSchemeIndex") === 0;
2916
2916
  }
2917
- function inheritedIndex(cells, page, name, fallback) {
2918
- const local = cellNumber(cells, name);
2919
- if (cells.has(name) && local === void 0) return void 0;
2917
+ function inheritedIndex(cells3, page, name, fallback) {
2918
+ const local = cellNumber(cells3, name);
2919
+ if (cells3.has(name) && local === void 0) return void 0;
2920
2920
  if (local !== void 0 && local !== 65534) return local;
2921
2921
  const value = cellNumber(page, name);
2922
2922
  return page?.has(name) && value === void 0 ? void 0 : value ?? fallback;
2923
2923
  }
2924
- function themeFor(themes, id, key) {
2924
+ function themeFor(themes, id, key2) {
2925
2925
  if (id === void 0 || id === 0 || id >= 65534) return void 0;
2926
- const matching = themes.filter((theme) => theme[key] === id);
2926
+ const matching = themes.filter((theme) => theme[key2] === id);
2927
2927
  return matching.length === 1 ? matching[0] : void 0;
2928
2928
  }
2929
2929
  function quickColor(theme, index, variant, report) {
@@ -2950,22 +2950,22 @@ function quickColor(theme, index, variant, report) {
2950
2950
  if (index < 100 || index > 106 || variant === void 0 || variant > 3) return void 0;
2951
2951
  return drawingColor(theme.variants[variant]?.get(String(index - 99)), theme.colors);
2952
2952
  }
2953
- function themeFormat(cells, category, resources) {
2954
- if (rootThemeSelected(cells, resources) || !resources.themes?.length) return void 0;
2955
- const type = cells.has("QuickStyleType") ? cellNumber(cells, "QuickStyleType") : 0;
2953
+ function themeFormat(cells3, category, resources) {
2954
+ if (rootThemeSelected(cells3, resources) || !resources.themes?.length) return void 0;
2955
+ const type = cells3.has("QuickStyleType") ? cellNumber(cells3, "QuickStyleType") : 0;
2956
2956
  if (type === void 0 || type > 3) return void 0;
2957
- const oneD = cellNumber(cells, "OneD") === 1 || cells.has("BeginX") && cells.has("EndX");
2957
+ const oneD = cellNumber(cells3, "OneD") === 1 || cells3.has("BeginX") && cells3.has("EndX");
2958
2958
  const connector = type === 3 || type === 0 && oneD;
2959
2959
  const scheme = connector ? "ConnectorSchemeIndex" : "EffectSchemeIndex";
2960
2960
  const effect = themeFor(
2961
2961
  resources.themes,
2962
- inheritedIndex(cells, resources.pageCells, scheme, 0),
2962
+ inheritedIndex(cells3, resources.pageCells, scheme, 0),
2963
2963
  connector ? "connectorId" : "effectId"
2964
2964
  );
2965
2965
  if (!effect) return void 0;
2966
- let matrix2 = cellNumber(cells, `QuickStyle${category}Matrix`);
2966
+ let matrix2 = cellNumber(cells3, `QuickStyle${category}Matrix`);
2967
2967
  if (matrix2 !== void 0 && matrix2 >= 100 && matrix2 <= 103) {
2968
- const variant = inheritedIndex(cells, resources.pageCells, "VariationStyleIndex", 0);
2968
+ const variant = inheritedIndex(cells3, resources.pageCells, "VariationStyleIndex", 0);
2969
2969
  const style = variant === void 0 ? void 0 : effect.variationStyles[variant]?.[matrix2 - 100];
2970
2970
  matrix2 = integer(
2971
2971
  style?.getAttribute(`${category === "Effects" ? "effect" : category.toLowerCase()}Idx`)
@@ -2975,26 +2975,26 @@ function themeFormat(cells, category, resources) {
2975
2975
  const list = category === "Effects" ? connector ? effect.connectorEffects : effect.effects : category === "Fill" ? connector ? effect.connectorFills : effect.fills : category === "Line" ? connector ? effect.connectorLines : effect.lines : connector ? effect.connectorFonts : effect.fonts;
2976
2976
  return list[matrix2 - 1];
2977
2977
  }
2978
- function themePaintContext(cells, category, resources, report) {
2978
+ function themePaintContext(cells3, category, resources, report) {
2979
2979
  if (!resources.themes?.length) return void 0;
2980
- const variation = cells.has("QuickStyleVariation") ? cellNumber(cells, "QuickStyleVariation") : 0;
2980
+ const variation = cells3.has("QuickStyleVariation") ? cellNumber(cells3, "QuickStyleVariation") : 0;
2981
2981
  const visibilityBit = category === "Fill" ? 8 : category === "Line" ? 4 : 2;
2982
2982
  if (variation === void 0 || variation > 15 || variation & visibilityBit) return void 0;
2983
- const colorId = inheritedIndex(cells, resources.pageCells, "ColorSchemeIndex", 0);
2983
+ const colorId = inheritedIndex(cells3, resources.pageCells, "ColorSchemeIndex", 0);
2984
2984
  const colorTheme = themeFor(resources.themes, colorId, "colorId");
2985
- const selected = themeFormat(cells, category, resources);
2985
+ const selected = themeFormat(cells3, category, resources);
2986
2986
  if (!colorTheme || !selected) return void 0;
2987
- const variant = inheritedIndex(cells, resources.pageCells, "VariationColorIndex", 0);
2987
+ const variant = inheritedIndex(cells3, resources.pageCells, "VariationColorIndex", 0);
2988
2988
  return {
2989
2989
  selected,
2990
2990
  colors: colorTheme.colors,
2991
- base: quickColor(colorTheme, cellNumber(cells, `QuickStyle${category}Color`), variant, report)
2991
+ base: quickColor(colorTheme, cellNumber(cells3, `QuickStyle${category}Color`), variant, report)
2992
2992
  };
2993
2993
  }
2994
- function themeColor(cells, name, resources, report, gradientRendered = false) {
2994
+ function themeColor(cells3, name, resources, report, gradientRendered = false) {
2995
2995
  const category = name === "FillForegnd" ? "Fill" : name === "LineColor" ? "Line" : name === "Color" ? "Font" : void 0;
2996
2996
  if (!category) return void 0;
2997
- const context = themePaintContext(cells, category, resources, report);
2997
+ const context = themePaintContext(cells3, category, resources, report);
2998
2998
  if (!context) return void 0;
2999
2999
  const { selected, colors, base } = context;
3000
3000
  if (category === "Font")
@@ -3024,12 +3024,12 @@ function linearGradientEndpoints(width, height, angle) {
3024
3024
  };
3025
3025
  }
3026
3026
  function themeLinearGradient(sheet, resources, width, height, report) {
3027
- const cells = sheet.cells;
3028
- if (cells.get("FillForegnd")?.value !== "Themed") return void 0;
3029
- const enabled = cells.get("FillGradientEnabled");
3030
- if (enabled && enabled.value !== "Themed" && number(cells, "FillGradientEnabled", 0) !== 1)
3027
+ const cells3 = sheet.cells;
3028
+ if (cells3.get("FillForegnd")?.value !== "Themed") return void 0;
3029
+ const enabled = cells3.get("FillGradientEnabled");
3030
+ if (enabled && enabled.value !== "Themed" && number(cells3, "FillGradientEnabled", 0) !== 1)
3031
3031
  return void 0;
3032
- const context = themePaintContext(cells, "Fill", resources, report);
3032
+ const context = themePaintContext(cells3, "Fill", resources, report);
3033
3033
  if (!context || context.selected.localName !== "gradFill") return void 0;
3034
3034
  const reject = () => {
3035
3035
  report(
@@ -3041,7 +3041,7 @@ function themeLinearGradient(sheet, resources, width, height, report) {
3041
3041
  if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0)
3042
3042
  return reject();
3043
3043
  if (["FillGradientDir", "FillGradientAngle", "RotateGradientWithShape", "UseGroupGradient"].some(
3044
- (key) => cells.has(key) && cells.get(key)?.value !== "Themed"
3044
+ (key2) => cells3.has(key2) && cells3.get(key2)?.value !== "Themed"
3045
3045
  ))
3046
3046
  return reject();
3047
3047
  if (sectionRows(sheet, "FillGradient").some(
@@ -3072,8 +3072,8 @@ function themeLinearGradient(sheet, resources, width, height, report) {
3072
3072
 
3073
3073
  // src/visio/saved-fill-gradient.ts
3074
3074
  function savedFillGradient(sheet, width, height, resolveColor, report) {
3075
- const cells = sheet.cells;
3076
- if (number(cells, "FillGradientEnabled", NaN) !== 1) return void 0;
3075
+ const cells3 = sheet.cells;
3076
+ if (number(cells3, "FillGradientEnabled", NaN) !== 1) return void 0;
3077
3077
  const rows = sectionRows(sheet, "FillGradient").slice(0, 10);
3078
3078
  if (!rows.some((row) => [...row.cells.values()].some((cell) => cell.value !== "Themed")))
3079
3079
  return void 0;
@@ -3084,10 +3084,10 @@ function savedFillGradient(sheet, width, height, resolveColor, report) {
3084
3084
  );
3085
3085
  return void 0;
3086
3086
  };
3087
- const angle = number(cells, "FillGradientAngle", NaN);
3087
+ const angle = number(cells3, "FillGradientAngle", NaN);
3088
3088
  const wrapped = (angle % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
3089
3089
  const horizontal = Math.min(wrapped, 2 * Math.PI - wrapped) < 1e-12 ? 0 : Math.abs(wrapped - Math.PI) < 1e-12 ? Math.PI : void 0;
3090
- if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0 || horizontal === void 0 || number(cells, "FillGradientDir", NaN) !== 0 || number(cells, "RotateGradientWithShape", NaN) !== 1 || number(cells, "UseGroupGradient", NaN) !== 0)
3090
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0 || horizontal === void 0 || number(cells3, "FillGradientDir", NaN) !== 0 || number(cells3, "RotateGradientWithShape", NaN) !== 1 || number(cells3, "UseGroupGradient", NaN) !== 0)
3091
3091
  return reject();
3092
3092
  if (rows.length < 2) return reject();
3093
3093
  const stops = [];
@@ -3109,9 +3109,9 @@ function savedFillGradient(sheet, width, height, resolveColor, report) {
3109
3109
  }
3110
3110
 
3111
3111
  // src/visio/theme-line.ts
3112
- function themeLineWeight(cells, resources, report) {
3113
- if (cells.get("LineWeight")?.value !== "Themed") return void 0;
3114
- const line = themeFormat(cells, "Line", resources);
3112
+ function themeLineWeight(cells3, resources, report) {
3113
+ if (cells3.get("LineWeight")?.value !== "Themed") return void 0;
3114
+ const line = themeFormat(cells3, "Line", resources);
3115
3115
  const raw = line?.getAttribute("w");
3116
3116
  if (!line || !raw || !/^\d{1,8}$/.test(raw)) return void 0;
3117
3117
  const emu = Number(raw);
@@ -3124,12 +3124,12 @@ function themeLineWeight(cells, resources, report) {
3124
3124
  );
3125
3125
  return emu / 914400;
3126
3126
  }
3127
- function reportThemeEffects(cells, resources, report) {
3127
+ function reportThemeEffects(cells3, resources, report) {
3128
3128
  if (!["ShdwPattern", "ShapeShdwShow", "ReflectionTrans", "GlowSize", "SoftEdgesSize"].some(
3129
- (name) => cells.get(name)?.value === "Themed"
3129
+ (name) => cells3.get(name)?.value === "Themed"
3130
3130
  ))
3131
3131
  return;
3132
- const style = themeFormat(cells, "Effects", resources);
3132
+ const style = themeFormat(cells3, "Effects", resources);
3133
3133
  if (style && elements(style).some(
3134
3134
  (node) => node.localName === "effectLst" && elements(node).some(
3135
3135
  (effect) => !["blur", "fillOverlay", "prstShdw"].includes(effect.localName)
@@ -3140,9 +3140,9 @@ function reportThemeEffects(cells, resources, report) {
3140
3140
  "The selected theme effect style contains effects that are not rendered."
3141
3141
  );
3142
3142
  }
3143
- function themeSolidLinePattern(cells, resources) {
3144
- if (cells.get("LinePattern")?.value !== "Themed") return void 0;
3145
- const line = themeFormat(cells, "Line", resources);
3143
+ function themeSolidLinePattern(cells3, resources) {
3144
+ if (cells3.get("LinePattern")?.value !== "Themed") return void 0;
3145
+ const line = themeFormat(cells3, "Line", resources);
3146
3146
  const dash = themeChildren(line, "prstDash");
3147
3147
  if (dash.length !== 1 || themeChild(line, "custDash") || dash[0]?.getAttribute("val") !== "solid")
3148
3148
  return void 0;
@@ -3150,11 +3150,11 @@ function themeSolidLinePattern(cells, resources) {
3150
3150
  }
3151
3151
 
3152
3152
  // src/visio/line-style.ts
3153
- function lineCap(cells, resources, report) {
3154
- const cell = cells.get("LineCap");
3153
+ function lineCap(cells3, resources, report) {
3154
+ const cell = cells3.get("LineCap");
3155
3155
  if (!cell) return void 0;
3156
3156
  if (cell.value === "Themed") {
3157
- const line = themeFormat(cells, "Line", resources);
3157
+ const line = themeFormat(cells3, "Line", resources);
3158
3158
  if (!line) {
3159
3159
  report("unresolved-line-cap", "The requested theme line cap could not be resolved.");
3160
3160
  return void 0;
@@ -3170,7 +3170,7 @@ function lineCap(cells, resources, report) {
3170
3170
  report("missing-cached-value", "Cell LineCap exceeds the bounded numeric cache length.");
3171
3171
  return void 0;
3172
3172
  }
3173
- const value = number(cells, "LineCap", NaN, report);
3173
+ const value = number(cells3, "LineCap", NaN, report);
3174
3174
  if (value === 0) return "round";
3175
3175
  if (value === 1 || value === 2) {
3176
3176
  report(
@@ -3204,8 +3204,8 @@ function builtinDash(pattern) {
3204
3204
  ][motif];
3205
3205
  return lengths.flatMap((length) => [length, motif >= 5 ? longGap : gap]);
3206
3206
  }
3207
- function linePattern(cells, report) {
3208
- const raw = cells.get("LinePattern")?.value;
3207
+ function linePattern(cells3, report) {
3208
+ const raw = cells3.get("LinePattern")?.value;
3209
3209
  if (raw === "Themed") {
3210
3210
  report(
3211
3211
  "unresolved-line-pattern",
@@ -3217,7 +3217,7 @@ function linePattern(cells, report) {
3217
3217
  report("missing-cached-value", "Cell LinePattern exceeds the bounded numeric cache length.");
3218
3218
  return { linePattern: 1 };
3219
3219
  }
3220
- const pattern = number(cells, "LinePattern", 1, report);
3220
+ const pattern = number(cells3, "LinePattern", 1, report);
3221
3221
  if (Number.isInteger(pattern) && pattern >= 2 && pattern <= 23) {
3222
3222
  report(
3223
3223
  "inferred-line-pattern",
@@ -3241,44 +3241,44 @@ function linePattern(cells, report) {
3241
3241
  }
3242
3242
 
3243
3243
  // src/visio/text-background.ts
3244
- function textBackground(cells, resources, report) {
3245
- const raw = cells.get("TextBkgnd")?.value;
3244
+ function textBackground(cells3, resources, report) {
3245
+ const raw = cells3.get("TextBkgnd")?.value;
3246
3246
  if (raw === void 0 || raw === "0" || raw === "255") return {};
3247
3247
  const value = /^\d+$/.test(raw) ? String(Number(raw) - 1) : raw;
3248
3248
  const resolved = color(/* @__PURE__ */ new Map([["TextBkgnd", { value }]]), "TextBkgnd", "", resources, report);
3249
3249
  if (!resolved) return {};
3250
3250
  return {
3251
3251
  backgroundColor: resolved,
3252
- backgroundOpacity: Math.max(0, Math.min(1, 1 - number(cells, "TextBkgndTrans", 0, report)))
3252
+ backgroundOpacity: Math.max(0, Math.min(1, 1 - number(cells3, "TextBkgndTrans", 0, report)))
3253
3253
  };
3254
3254
  }
3255
3255
 
3256
3256
  // src/visio/paragraphs.ts
3257
3257
  var BULLETS = { 1: "\u2022", 2: "\u25C6", 3: "\u25A0", 4: "\u2610", 5: "\u2756", 6: "\u27A4", 7: "\u2713" };
3258
- function paragraphStyle(cells, fontFamily, fontSize, resources, report) {
3259
- const align = number(cells, "HorzAlign", 1, report), spacing = number(cells, "SpLine", -1.2, report);
3260
- const bulletType = number(cells, "Bullet", 0, report), customBullet = cells.get("BulletStr")?.value;
3258
+ function paragraphStyle(cells3, fontFamily, fontSize, resources, report) {
3259
+ const align = number(cells3, "HorzAlign", 1, report), spacing = number(cells3, "SpLine", -1.2, report);
3260
+ const bulletType = number(cells3, "Bullet", 0, report), customBullet = cells3.get("BulletStr")?.value;
3261
3261
  const bulletText = bulletType ? customBullet || BULLETS[bulletType] : void 0;
3262
3262
  if (bulletType && !bulletText)
3263
3263
  report("unsupported-bullet", `Bullet style ${bulletType} is unsupported and was omitted.`);
3264
- const fontId = cells.get("BulletFont")?.value;
3264
+ const fontId = cells3.get("BulletFont")?.value;
3265
3265
  const bulletFont = fontId && fontId !== "0" ? resources.fonts.get(fontId) ?? (!/^\d+$/.test(fontId) ? fontId : fontFamily) : fontFamily;
3266
- const bulletSize = number(cells, "BulletFontSize", -1, report);
3266
+ const bulletSize = number(cells3, "BulletFontSize", -1, report);
3267
3267
  return {
3268
3268
  horizontalAlign: align === 1 ? "center" : align === 2 ? "right" : align === 3 ? "justify" : align === 4 ? "distributed" : "left",
3269
- indentLeft: number(cells, "IndLeft", 0, report),
3270
- indentRight: number(cells, "IndRight", 0, report),
3271
- indentFirst: number(cells, "IndFirst", 0, report),
3272
- spaceBefore: number(cells, "SpBefore", 0, report),
3273
- spaceAfter: number(cells, "SpAfter", 0, report),
3269
+ indentLeft: number(cells3, "IndLeft", 0, report),
3270
+ indentRight: number(cells3, "IndRight", 0, report),
3271
+ indentFirst: number(cells3, "IndFirst", 0, report),
3272
+ spaceBefore: number(cells3, "SpBefore", 0, report),
3273
+ spaceAfter: number(cells3, "SpAfter", 0, report),
3274
3274
  lineSpacing: { kind: spacing < 0 ? "multiple" : "exact", value: Math.abs(spacing) },
3275
- direction: number(cells, "Flags", 0, report) === 1 ? "rtl" : "ltr",
3275
+ direction: number(cells3, "Flags", 0, report) === 1 ? "rtl" : "ltr",
3276
3276
  ...bulletText ? {
3277
3277
  bullet: {
3278
3278
  text: bulletText,
3279
3279
  fontFamily: bulletFont,
3280
3280
  fontSize: bulletSize > 0 ? bulletSize : fontSize,
3281
- offset: number(cells, "TextPosAfterBullet", 0, report)
3281
+ offset: number(cells3, "TextPosAfterBullet", 0, report)
3282
3282
  }
3283
3283
  } : {}
3284
3284
  };
@@ -3338,9 +3338,9 @@ function distance(p, a, b) {
3338
3338
  }
3339
3339
  function flattenNurbs(controls2, sourceKnots, degree, consume, consumeWork) {
3340
3340
  let work = 0;
3341
- const charge = (units) => {
3342
- consumeWork(units);
3343
- if ((work += units) > MAX_WORK) throw failure;
3341
+ const charge = (units2) => {
3342
+ consumeWork(units2);
3343
+ if ((work += units2) > MAX_WORK) throw failure;
3344
3344
  };
3345
3345
  try {
3346
3346
  charge(controls2.length + sourceKnots.length);
@@ -3708,15 +3708,15 @@ function roundedOrthogonalPath(points, radius, point2) {
3708
3708
  // src/visio/geometry.ts
3709
3709
  var clean = (value) => Math.abs(value) < 1e-12 ? 0 : Number(value.toFixed(9));
3710
3710
  var point = (x, y) => `${clean(x)} ${clean(y)}`;
3711
- function shapeTransform(cells, width, height, report) {
3711
+ function shapeTransform(cells3, width, height, report) {
3712
3712
  return transform(
3713
- number(cells, "PinX", width / 2, report),
3714
- number(cells, "PinY", height / 2, report),
3715
- number(cells, "LocPinX", width / 2, report),
3716
- number(cells, "LocPinY", height / 2, report),
3717
- number(cells, "Angle", 0, report),
3718
- number(cells, "FlipX", 0, report) !== 0,
3719
- number(cells, "FlipY", 0, report) !== 0
3713
+ number(cells3, "PinX", width / 2, report),
3714
+ number(cells3, "PinY", height / 2, report),
3715
+ number(cells3, "LocPinX", width / 2, report),
3716
+ number(cells3, "LocPinY", height / 2, report),
3717
+ number(cells3, "Angle", 0, report),
3718
+ number(cells3, "FlipX", 0, report) !== 0,
3719
+ number(cells3, "FlipY", 0, report) !== 0
3720
3720
  );
3721
3721
  }
3722
3722
  function transform(x, y, localX, localY, angle, flipX = false, flipY = false) {
@@ -3758,7 +3758,7 @@ function geometryPaths(sheet, width, height, report, consume, consumeWork = () =
3758
3758
  let linePoints = rows.length >= 3 && rows.every(
3759
3759
  (row, i) => i === 0 ? ["MoveTo", "RelMoveTo"].includes(row.type) : ["LineTo", "RelLineTo"].includes(row.type)
3760
3760
  ) ? [] : void 0;
3761
- let currentX = 0, currentY = 0, currentValid = false, supported = true;
3761
+ let currentX = 0, currentY = 0, currentValid = false, supported2 = true;
3762
3762
  for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
3763
3763
  const row = rows[rowIndex];
3764
3764
  consume();
@@ -3773,13 +3773,13 @@ function geometryPaths(sheet, width, height, report, consume, consumeWork = () =
3773
3773
  "geometry-limit",
3774
3774
  "A spline exceeds 256 control points; its geometry section was omitted."
3775
3775
  );
3776
- supported = false;
3776
+ supported2 = false;
3777
3777
  } else if (!currentValid || rowIndex === 0 || rows[rowIndex - 1].type === "Ellipse") {
3778
3778
  report(
3779
3779
  "invalid-spline",
3780
3780
  "A spline requires a preceding endpoint row; its geometry section was omitted."
3781
3781
  );
3782
- supported = false;
3782
+ supported2 = false;
3783
3783
  } else {
3784
3784
  const spline = splineGeometry(
3785
3785
  row,
@@ -3796,7 +3796,7 @@ function geometryPaths(sheet, width, height, report, consume, consumeWork = () =
3796
3796
  currentY = spline.endY;
3797
3797
  currentValid = true;
3798
3798
  } else {
3799
- supported = false;
3799
+ supported2 = false;
3800
3800
  currentValid = false;
3801
3801
  }
3802
3802
  }
@@ -3854,7 +3854,7 @@ function geometryPaths(sheet, width, height, report, consume, consumeWork = () =
3854
3854
  );
3855
3855
  if (command2) commands.push(command2);
3856
3856
  else {
3857
- supported = false;
3857
+ supported2 = false;
3858
3858
  report(
3859
3859
  "invalid-geometry",
3860
3860
  "Invalid elliptical arc parameters; its geometry section was omitted."
@@ -3867,7 +3867,7 @@ function geometryPaths(sheet, width, height, report, consume, consumeWork = () =
3867
3867
  const c = cell("C") - x, d = cell("D") - y;
3868
3868
  const rx = Math.hypot(a, b), ry = Math.hypot(c, d);
3869
3869
  if (Math.abs(a * c + b * d) > 1e-7 * Math.max(1, rx * ry)) {
3870
- supported = false;
3870
+ supported2 = false;
3871
3871
  report(
3872
3872
  "unsupported-ellipse",
3873
3873
  "Non-orthogonal ellipse control axes are unsupported; its geometry section was omitted."
@@ -3888,7 +3888,7 @@ function geometryPaths(sheet, width, height, report, consume, consumeWork = () =
3888
3888
  "invalid-geometry",
3889
3889
  "NURBSTo requires a usable cached preceding endpoint; its geometry section was omitted."
3890
3890
  );
3891
- supported = false;
3891
+ supported2 = false;
3892
3892
  break;
3893
3893
  }
3894
3894
  const suffix = geometryRow(
@@ -3902,11 +3902,11 @@ function geometryPaths(sheet, width, height, report, consume, consumeWork = () =
3902
3902
  consumeWork
3903
3903
  );
3904
3904
  if (suffix) commands.push(suffix);
3905
- else supported = false;
3905
+ else supported2 = false;
3906
3906
  break;
3907
3907
  }
3908
3908
  default:
3909
- supported = false;
3909
+ supported2 = false;
3910
3910
  report(
3911
3911
  "unsupported-geometry",
3912
3912
  `Geometry row ${row.type || "(missing type)"} is unsupported; its geometry section was omitted.`
@@ -3916,7 +3916,7 @@ function geometryPaths(sheet, width, height, report, consume, consumeWork = () =
3916
3916
  currentX = x;
3917
3917
  currentY = y;
3918
3918
  }
3919
- if (!supported) continue;
3919
+ if (!supported2) continue;
3920
3920
  const rectangle = rounding > 0 ? roundedRectanglePath(linePoints, rounding, point) : void 0;
3921
3921
  const open = rounding > 0 && !rectangle ? roundedOrthogonalPath(linePoints, rounding, point) : void 0;
3922
3922
  const rounded = rectangle ?? open?.path;
@@ -3964,8 +3964,8 @@ var PALETTE = [
3964
3964
  "#333333",
3965
3965
  "#1a1a1a"
3966
3966
  ];
3967
- function color(cells, name, fallback, resources, report, themeCells = cells, gradientRendered = false) {
3968
- const value = cells.get(name)?.value;
3967
+ function color(cells3, name, fallback, resources, report, themeCells = cells3, gradientRendered = false) {
3968
+ const value = cells3.get(name)?.value;
3969
3969
  if (value === void 0) return fallback;
3970
3970
  if (/^#[0-9a-f]{6}$/i.test(value)) return value;
3971
3971
  if (/^\d+$/.test(value)) {
@@ -3981,7 +3981,7 @@ function color(cells, name, fallback, resources, report, themeCells = cells, gra
3981
3981
  }
3982
3982
  var opacity = (value) => Math.max(0, Math.min(1, 1 - value));
3983
3983
  function shapeStyle(sheet, resources, report, width = number(sheet.cells, "Width", 1), height = number(sheet.cells, "Height", 1)) {
3984
- const cells = sheet.cells, pattern = number(cells, "FillPattern", 1, report);
3984
+ const cells3 = sheet.cells, pattern = number(cells3, "FillPattern", 1, report);
3985
3985
  const savedGradient = pattern !== 0 ? savedFillGradient(
3986
3986
  sheet,
3987
3987
  width,
@@ -3995,48 +3995,48 @@ function shapeStyle(sheet, resources, report, width = number(sheet.cells, "Width
3995
3995
  "unsupported-fill-pattern",
3996
3996
  `Fill pattern ${pattern} is approximated by its foreground color.`
3997
3997
  );
3998
- if (!fillGradient && number(cells, "FillGradientEnabled", 0, report) || number(cells, "LineGradientEnabled", 0, report))
3998
+ if (!fillGradient && number(cells3, "FillGradientEnabled", 0, report) || number(cells3, "LineGradientEnabled", 0, report))
3999
3999
  report(
4000
4000
  "unsupported-gradient",
4001
4001
  "Gradient fills and strokes are approximated with their foreground colors."
4002
4002
  );
4003
- reportThemeEffects(cells, resources, report);
4004
- if (number(cells, "ShdwPattern", 0, report))
4003
+ reportThemeEffects(cells3, resources, report);
4004
+ if (number(cells3, "ShdwPattern", 0, report))
4005
4005
  report("unsupported-shadow", "Shape shadows are not rendered.");
4006
- const cap = lineCap(cells, resources, report);
4006
+ const cap = lineCap(cells3, resources, report);
4007
4007
  return {
4008
- fill: pattern === 0 ? "none" : color(cells, "FillForegnd", "#ffffff", resources, report, cells, !!fillGradient),
4008
+ fill: pattern === 0 ? "none" : color(cells3, "FillForegnd", "#ffffff", resources, report, cells3, !!fillGradient),
4009
4009
  ...fillGradient ? { fillGradient } : {},
4010
- lineColor: color(cells, "LineColor", "#000000", resources, report),
4011
- lineWidth: themeLineWeight(cells, resources, report) ?? Math.max(0, number(cells, "LineWeight", 0.01, report)),
4010
+ lineColor: color(cells3, "LineColor", "#000000", resources, report),
4011
+ lineWidth: themeLineWeight(cells3, resources, report) ?? Math.max(0, number(cells3, "LineWeight", 0.01, report)),
4012
4012
  ...cap === void 0 ? {} : { lineCap: cap },
4013
- ...themeSolidLinePattern(cells, resources) ?? linePattern(cells, report),
4013
+ ...themeSolidLinePattern(cells3, resources) ?? linePattern(cells3, report),
4014
4014
  // Saved stop transparencies are the fill opacity, not a second alpha layer.
4015
- fillOpacity: savedGradient ? 1 : opacity(number(cells, "FillForegndTrans", 0, report)),
4016
- lineOpacity: opacity(number(cells, "LineColorTrans", 0, report)),
4017
- startArrow: number(cells, "BeginArrow", 0, report),
4018
- endArrow: number(cells, "EndArrow", 0, report),
4019
- startArrowSize: number(cells, "BeginArrowSize", 2, report),
4020
- endArrowSize: number(cells, "EndArrowSize", 2, report)
4015
+ fillOpacity: savedGradient ? 1 : opacity(number(cells3, "FillForegndTrans", 0, report)),
4016
+ lineOpacity: opacity(number(cells3, "LineColorTrans", 0, report)),
4017
+ startArrow: number(cells3, "BeginArrow", 0, report),
4018
+ endArrow: number(cells3, "EndArrow", 0, report),
4019
+ startArrowSize: number(cells3, "BeginArrowSize", 2, report),
4020
+ endArrowSize: number(cells3, "EndArrowSize", 2, report)
4021
4021
  };
4022
4022
  }
4023
- function font(cells, resources) {
4024
- const value = cells.get("Font")?.value;
4023
+ function font(cells3, resources) {
4024
+ const value = cells3.get("Font")?.value;
4025
4025
  return (value ? resources.fonts.get(value) ?? (!/^\d+$/.test(value) && value !== "Themed" ? value.replace(/^"|"$/g, "") : void 0) : void 0) || "Arial";
4026
4026
  }
4027
- function runStyle(cells, resources, report, themeCells) {
4028
- const bits = number(cells, "Style", 0, report);
4027
+ function runStyle(cells3, resources, report, themeCells) {
4028
+ const bits = number(cells3, "Style", 0, report);
4029
4029
  return {
4030
- fontFamily: font(cells, resources),
4031
- fontSize: Math.max(1e-3, number(cells, "Size", 10 / 72, report)),
4032
- color: color(cells, "Color", "#000000", resources, report, themeCells),
4030
+ fontFamily: font(cells3, resources),
4031
+ fontSize: Math.max(1e-3, number(cells3, "Size", 10 / 72, report)),
4032
+ color: color(cells3, "Color", "#000000", resources, report, themeCells),
4033
4033
  bold: !!(bits & 1),
4034
4034
  italic: !!(bits & 2),
4035
4035
  underline: !!(bits & 4)
4036
4036
  };
4037
4037
  }
4038
4038
  function shapeText(sheet, node, width, height, resources, report, consume, consumeParagraph) {
4039
- const cells = sheet.cells;
4039
+ const cells3 = sheet.cells;
4040
4040
  const characters = new Map(sectionRows(sheet, "Character").map((row) => [row.index, row.cells]));
4041
4041
  const defaultCells = characters.get("0") ?? /* @__PURE__ */ new Map();
4042
4042
  let currentCells = defaultCells;
@@ -4046,11 +4046,11 @@ function shapeText(sheet, node, width, height, resources, report, consume, consu
4046
4046
  const append = (text) => {
4047
4047
  if (text) {
4048
4048
  consume(text.length);
4049
- runs.push({ text, ...runStyle(currentCells, resources, report, cells) });
4049
+ runs.push({ text, ...runStyle(currentCells, resources, report, cells3) });
4050
4050
  textOffset += text.length;
4051
4051
  }
4052
4052
  };
4053
- if (node && !number(cells, "HideText", 0, report)) {
4053
+ if (node && !number(cells3, "HideText", 0, report)) {
4054
4054
  for (const part of Array.from(node.childNodes)) {
4055
4055
  if (part.nodeType === 3 || part.nodeType === 4) append(part.nodeValue ?? "");
4056
4056
  else if (part.nodeType === 1) {
@@ -4072,10 +4072,10 @@ function shapeText(sheet, node, width, height, resources, report, consume, consu
4072
4072
  }
4073
4073
  const paragraph = sectionRows(sheet, "Paragraph")[0]?.cells ?? /* @__PURE__ */ new Map();
4074
4074
  const alignment = number(paragraph, "HorzAlign", 1, report);
4075
- const textWidth = Math.max(0, number(cells, "TxtWidth", width, report)), textHeight = Math.max(0, number(cells, "TxtHeight", height, report));
4076
- const vertical = number(cells, "VerticalAlign", 1, report);
4075
+ const textWidth = Math.max(0, number(cells3, "TxtWidth", width, report)), textHeight = Math.max(0, number(cells3, "TxtHeight", height, report));
4076
+ const vertical = number(cells3, "VerticalAlign", 1, report);
4077
4077
  const plainText = runs.map((r) => r.text).join("");
4078
- const defaultRun = runStyle(defaultCells, resources, report, cells);
4078
+ const defaultRun = runStyle(defaultCells, resources, report, cells3);
4079
4079
  const paragraphs = textParagraphs(
4080
4080
  plainText,
4081
4081
  paragraphMarkers,
@@ -4089,26 +4089,26 @@ function shapeText(sheet, node, width, height, resources, report, consume, consu
4089
4089
  );
4090
4090
  return {
4091
4091
  plainText,
4092
- ...textBackground(cells, resources, report),
4092
+ ...textBackground(cells3, resources, report),
4093
4093
  paragraphs,
4094
4094
  runs,
4095
- ...runStyle(defaultCells, resources, report, cells),
4095
+ ...runStyle(defaultCells, resources, report, cells3),
4096
4096
  horizontalAlign: alignment === 1 ? "center" : alignment === 2 ? "right" : "left",
4097
4097
  verticalAlign: vertical === 0 ? "top" : vertical === 2 ? "bottom" : "middle",
4098
4098
  width: textWidth,
4099
4099
  height: textHeight,
4100
4100
  transform: transform(
4101
- number(cells, "TxtPinX", width / 2, report),
4102
- number(cells, "TxtPinY", height / 2, report),
4103
- number(cells, "TxtLocPinX", textWidth / 2, report),
4104
- number(cells, "TxtLocPinY", textHeight / 2, report),
4105
- number(cells, "TxtAngle", 0, report)
4101
+ number(cells3, "TxtPinX", width / 2, report),
4102
+ number(cells3, "TxtPinY", height / 2, report),
4103
+ number(cells3, "TxtLocPinX", textWidth / 2, report),
4104
+ number(cells3, "TxtLocPinY", textHeight / 2, report),
4105
+ number(cells3, "TxtAngle", 0, report)
4106
4106
  ),
4107
4107
  margins: {
4108
- left: number(cells, "LeftMargin", 0.04, report),
4109
- right: number(cells, "RightMargin", 0.04, report),
4110
- top: number(cells, "TopMargin", 0.04, report),
4111
- bottom: number(cells, "BottomMargin", 0.04, report)
4108
+ left: number(cells3, "LeftMargin", 0.04, report),
4109
+ right: number(cells3, "RightMargin", 0.04, report),
4110
+ top: number(cells3, "TopMargin", 0.04, report),
4111
+ bottom: number(cells3, "BottomMargin", 0.04, report)
4112
4112
  }
4113
4113
  };
4114
4114
  }
@@ -4321,15 +4321,15 @@ function diagnosticCollector(maxCount) {
4321
4321
  ...context.pageId === void 0 ? {} : { pageId: bounded4(context.pageId, 256) },
4322
4322
  ...context.shapeId === void 0 ? {} : { shapeId: bounded4(context.shapeId, 1024) }
4323
4323
  };
4324
- const key = JSON.stringify(item);
4325
- if (keys.has(key)) return;
4324
+ const key2 = JSON.stringify(item);
4325
+ if (keys.has(key2)) return;
4326
4326
  const size = Object.values(item).reduce((sum, value) => sum + value.length, 0);
4327
4327
  if (characters + size > MAX_CHARACTERS - 256) {
4328
4328
  truncated = true;
4329
4329
  return;
4330
4330
  }
4331
4331
  characters += size;
4332
- keys.add(key);
4332
+ keys.add(key2);
4333
4333
  diagnostics.push(item);
4334
4334
  };
4335
4335
  return {
@@ -4387,27 +4387,27 @@ var numeric2 = (text) => {
4387
4387
  const value = Number(text);
4388
4388
  return Number.isFinite(value) ? value : void 0;
4389
4389
  };
4390
- function cached(cells, key, budget, report) {
4391
- const cell = cells.get(key);
4390
+ function cached(cells3, key2, budget, report) {
4391
+ const cell = cells3.get(key2);
4392
4392
  if (!cell) return void 0;
4393
4393
  if (cell.error !== void 0)
4394
4394
  report(
4395
4395
  "metadata-cell-error",
4396
- `Metadata cell ${key} has a saved error; its cached value may be stale.`
4396
+ `Metadata cell ${key2} has a saved error; its cached value may be stale.`
4397
4397
  );
4398
4398
  if (cell.value === void 0)
4399
4399
  report(
4400
4400
  "missing-metadata-cache",
4401
- `Metadata cell ${key} has no cached value; its formula was not evaluated.`
4401
+ `Metadata cell ${key2} has no cached value; its formula was not evaluated.`
4402
4402
  );
4403
4403
  return cell.value === void 0 ? void 0 : budget.retain(cell.value);
4404
4404
  }
4405
- function boolean(cells, key, budget, report) {
4406
- const text = cached(cells, key, budget, report);
4405
+ function boolean(cells3, key2, budget, report) {
4406
+ const text = cached(cells3, key2, budget, report);
4407
4407
  if (text === void 0) return void 0;
4408
4408
  const value = booleanValue(text);
4409
- if (value === void 0 || cells.get(key)?.error !== void 0) {
4410
- report("invalid-metadata-boolean", `Metadata cell ${key} has no usable cached Boolean value.`);
4409
+ if (value === void 0 || cells3.get(key2)?.error !== void 0) {
4410
+ report("invalid-metadata-boolean", `Metadata cell ${key2} has no usable cached Boolean value.`);
4411
4411
  return void 0;
4412
4412
  }
4413
4413
  return value;
@@ -4419,7 +4419,7 @@ function booleanValue(text) {
4419
4419
  return value === void 0 ? void 0 : value !== 0;
4420
4420
  }
4421
4421
  function property(row, budget, report) {
4422
- const get = (key) => cached(row.cells, key, budget, report);
4422
+ const get = (key2) => cached(row.cells, key2, budget, report);
4423
4423
  const rawType = get("Type"), rawValue = get("Value");
4424
4424
  const typeValue = row.cells.has("Type") ? numeric2(rawType) : 0;
4425
4425
  const type = typeValue !== void 0 && Number.isInteger(typeValue) && typeValue >= 0 && typeValue <= 7 && row.cells.get("Type")?.error === void 0 ? typeValue : -1;
@@ -4479,13 +4479,13 @@ function safeAddress(address) {
4479
4479
  }
4480
4480
  }
4481
4481
  function hyperlink(row, budget, report) {
4482
- const get = (key) => cached(row.cells, key, budget, report);
4482
+ const get = (key2) => cached(row.cells, key2, budget, report);
4483
4483
  const description = get("Description"), address = get("Address"), subAddress = get("SubAddress");
4484
4484
  const isDefault = boolean(row.cells, "Default", budget, report), newWindow = boolean(row.cells, "NewWindow", budget, report);
4485
4485
  const invisible = boolean(row.cells, "Invisible", budget, report), sortKey = get("SortKey");
4486
4486
  let target = { kind: "unresolved", reason: "missing-target" };
4487
- if (["Address", "SubAddress"].some((key) => {
4488
- const cell = row.cells.get(key);
4487
+ if (["Address", "SubAddress"].some((key2) => {
4488
+ const cell = row.cells.get(key2);
4489
4489
  return cell && (cell.error !== void 0 || cell.value === void 0);
4490
4490
  })) {
4491
4491
  target = { kind: "unresolved", reason: "missing-or-stale-cache" };
@@ -4497,8 +4497,8 @@ function hyperlink(row, budget, report) {
4497
4497
  const href = safeAddress(address);
4498
4498
  target = href === void 0 ? { kind: "unresolved", reason: "unsafe-or-unsupported-address" } : { kind: "external", href: budget.retain(href) };
4499
4499
  } else if (subAddress) target = { kind: "internal", subAddress: budget.retain(subAddress) };
4500
- if (["ExtraInfo", "Frame"].some((key) => {
4501
- const cell = row.cells.get(key);
4500
+ if (["ExtraInfo", "Frame"].some((key2) => {
4501
+ const cell = row.cells.get(key2);
4502
4502
  return cell && (cell.value !== "" || cell.error !== void 0);
4503
4503
  })) {
4504
4504
  report(
@@ -4574,14 +4574,14 @@ function rootCharacterCells(root) {
4574
4574
  rootCharacters.set(root, void 0);
4575
4575
  return void 0;
4576
4576
  }
4577
- function replaceThemed(cells, root, names, report) {
4578
- let result = cells;
4577
+ function replaceThemed(cells3, root, names, report) {
4578
+ let result = cells3;
4579
4579
  for (const name of names) {
4580
4580
  const saved = root.get(name);
4581
- if (cells.get(name)?.value !== "Themed" || !saved?.value || saved.value === "Themed") continue;
4582
- if (result === cells) result = new Map(cells);
4581
+ if (cells3.get(name)?.value !== "Themed" || !saved?.value || saved.value === "Themed") continue;
4582
+ if (result === cells3) result = new Map(cells3);
4583
4583
  result.set(name, saved);
4584
- if (saved.error !== void 0 && cells.get(name)?.error === void 0)
4584
+ if (saved.error !== void 0 && cells3.get(name)?.error === void 0)
4585
4585
  report?.(
4586
4586
  "cached-cell-error",
4587
4587
  `Cell ${name} records a formula error; only its last valid cached value is available.`
@@ -4595,16 +4595,16 @@ function rootThemeSheet(sheet, resources, report) {
4595
4595
  const colorRoot = rootThemeSelected(sheet.cells, resources);
4596
4596
  const lineRoot = colorRoot || integer(sheet.cells.get("QuickStyleLineMatrix")?.value) === 0;
4597
4597
  const fillRoot = colorRoot || integer(sheet.cells.get("QuickStyleFillMatrix")?.value) === 0;
4598
- let cells = sheet.cells;
4598
+ let cells3 = sheet.cells;
4599
4599
  if (colorRoot)
4600
- cells = replaceThemed(cells, root.cells, ["LineColor", "FillForegnd", "FillBkgnd"], report);
4601
- if (lineRoot) cells = replaceThemed(cells, root.cells, LINE_PROPERTIES, report);
4602
- if (fillRoot) cells = replaceThemed(cells, root.cells, FILL_PROPERTIES, report);
4600
+ cells3 = replaceThemed(cells3, root.cells, ["LineColor", "FillForegnd", "FillBkgnd"], report);
4601
+ if (lineRoot) cells3 = replaceThemed(cells3, root.cells, LINE_PROPERTIES, report);
4602
+ if (fillRoot) cells3 = replaceThemed(cells3, root.cells, FILL_PROPERTIES, report);
4603
4603
  let sections = sheet.sections;
4604
4604
  if (fillRoot) {
4605
- for (const [key, section] of sheet.sections) {
4605
+ for (const [key2, section] of sheet.sections) {
4606
4606
  if (section.name !== "FillGradient" || section.deleted) continue;
4607
- const source = root.sections.get(key);
4607
+ const source = root.sections.get(key2);
4608
4608
  if (!source || source.deleted) continue;
4609
4609
  let rows = section.rows;
4610
4610
  for (const [index, row] of section.rows) {
@@ -4619,12 +4619,12 @@ function rootThemeSheet(sheet, resources, report) {
4619
4619
  }
4620
4620
  if (rows === section.rows) continue;
4621
4621
  if (sections === sheet.sections) sections = new Map(sheet.sections);
4622
- sections.set(key, { ...section, rows });
4622
+ sections.set(key2, { ...section, rows });
4623
4623
  }
4624
4624
  }
4625
4625
  const rootCharacter = colorRoot ? rootCharacterCells(root) : void 0;
4626
4626
  if (colorRoot && rootCharacter) {
4627
- for (const [key, section] of sheet.sections) {
4627
+ for (const [key2, section] of sheet.sections) {
4628
4628
  if (section.name !== "Character" || section.deleted) continue;
4629
4629
  const rows = new Map(section.rows);
4630
4630
  for (const [index, row] of rows) {
@@ -4633,19 +4633,19 @@ function rootThemeSheet(sheet, resources, report) {
4633
4633
  if (resolved !== row.cells) rows.set(index, { ...row, cells: resolved });
4634
4634
  }
4635
4635
  if (sections === sheet.sections) sections = new Map(sheet.sections);
4636
- sections.set(key, { ...section, rows });
4636
+ sections.set(key2, { ...section, rows });
4637
4637
  }
4638
4638
  }
4639
- return cells === sheet.cells && sections === sheet.sections ? sheet : { cells, sections };
4639
+ return cells3 === sheet.cells && sections === sheet.sections ? sheet : { cells: cells3, sections };
4640
4640
  }
4641
4641
 
4642
4642
  // src/visio/style-inheritance.ts
4643
4643
  function styleSheet(id, category, context, visited = /* @__PURE__ */ new Set()) {
4644
4644
  context.checkTime();
4645
- const key = `${category}:${id}`;
4646
- const cached2 = context.styleCache.get(key);
4645
+ const key2 = `${category}:${id}`;
4646
+ const cached2 = context.styleCache.get(key2);
4647
4647
  if (cached2) return cached2;
4648
- if (visited.has(key)) {
4648
+ if (visited.has(key2)) {
4649
4649
  context.report("style-inheritance-cycle", `Style ${id} has cyclic inheritance.`);
4650
4650
  return emptySheet();
4651
4651
  }
@@ -4658,7 +4658,7 @@ function styleSheet(id, category, context, visited = /* @__PURE__ */ new Set())
4658
4658
  if (number(style.sheet.cells, enabledCell, 1, context.report) === 0) return emptySheet();
4659
4659
  if (visited.size > 64)
4660
4660
  throw new VisioPackageError("STYLE_DEPTH_LIMIT", "Style inheritance depth limit exceeded.");
4661
- visited.add(key);
4661
+ visited.add(key2);
4662
4662
  const parent = style.attributes.get(category);
4663
4663
  const inherited = parent !== void 0 && parent !== id ? styleSheet(parent, category, context, visited) : emptySheet();
4664
4664
  const full = mergeSheets(inherited, style.sheet);
@@ -4666,7 +4666,7 @@ function styleSheet(id, category, context, visited = /* @__PURE__ */ new Set())
4666
4666
  const themeSelectors = /^(ThemeIndex|ColorSchemeIndex|EffectSchemeIndex|ConnectorSchemeIndex|FontSchemeIndex|VariationColorIndex|VariationStyleIndex|QuickStyleType|QuickStyleVariation)$/;
4667
4667
  const result = {
4668
4668
  cells: new Map(
4669
- [...full.cells].filter(([key2]) => pattern.test(key2) || themeSelectors.test(key2))
4669
+ [...full.cells].filter(([key3]) => pattern.test(key3) || themeSelectors.test(key3))
4670
4670
  ),
4671
4671
  sections: new Map(
4672
4672
  [...full.sections].filter(
@@ -4674,7 +4674,7 @@ function styleSheet(id, category, context, visited = /* @__PURE__ */ new Set())
4674
4674
  )
4675
4675
  )
4676
4676
  };
4677
- context.styleCache.set(key, result);
4677
+ context.styleCache.set(key2, result);
4678
4678
  return result;
4679
4679
  }
4680
4680
 
@@ -4748,7 +4748,7 @@ function normalizeShapes(raw, context, depth = 0, masterStack = []) {
4748
4748
  );
4749
4749
  shape = mergeShape(base, original, context);
4750
4750
  if (["Width", "Height"].some(
4751
- (key) => original.cells.has(key) && number(original.cells, key, 0) !== number(base.cells, key, 0)
4751
+ (key2) => original.cells.has(key2) && number(original.cells, key2, 0) !== number(base.cells, key2, 0)
4752
4752
  ) && [...base.sections.values()].some(
4753
4753
  (section) => section.name === "Geometry" && [...section.rows.values()].some((row) => !row.type.startsWith("Rel"))
4754
4754
  ))
@@ -5002,9 +5002,9 @@ async function parseVsdx(input, options = {}) {
5002
5002
  if (++expansionCount > maxShapes)
5003
5003
  throw new VisioPackageError("SHAPE_LIMIT", "Inherited shape expansion limit exceeded.");
5004
5004
  },
5005
- consumeCurveWork: (units) => {
5005
+ consumeCurveWork: (units2) => {
5006
5006
  checkTime();
5007
- curveWork += units;
5007
+ curveWork += units2;
5008
5008
  if (curveWork > maxCurveWork)
5009
5009
  throw new VisioPackageError(
5010
5010
  "CURVE_WORK_LIMIT",
@@ -5414,6 +5414,1354 @@ function serializeEditedXml(root, limits, check) {
5414
5414
  return { bytes, nodes };
5415
5415
  }
5416
5416
 
5417
+ // src/visio/edit-commands.ts
5418
+ function snapshotVisioEdits(edits, maxEdits, maxText) {
5419
+ if (!Array.isArray(edits) || edits.length > maxEdits)
5420
+ fail("LIMIT_EDITS", "Edit command count exceeds limit.");
5421
+ let textLength = 0;
5422
+ const text = (value) => {
5423
+ if (typeof value !== "string") fail("INVALID_EDIT", "Edit text must be a string.");
5424
+ textLength += value.length;
5425
+ if (textLength > maxText) fail("LIMIT_EDIT_TEXT", "Replacement text exceeds aggregate limit.");
5426
+ if (/[\u0000-\u0008\u000b\u000c\u000d\u000e-\u001f\ufffe\uffff]|[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/u.test(
5427
+ value
5428
+ ))
5429
+ fail("INVALID_EDIT_TEXT", "Replacement text contains invalid XML characters.");
5430
+ return value;
5431
+ };
5432
+ const numeric4 = (value, dimension = false) => {
5433
+ if (typeof value !== "number" || !Number.isFinite(value) || Math.abs(value) > 1e6 || dimension && value <= 0)
5434
+ fail(
5435
+ "INVALID_EDIT",
5436
+ "Geometry requires finite drawing inches within limits and positive dimensions."
5437
+ );
5438
+ return value;
5439
+ };
5440
+ return Array.from(edits, (edit) => {
5441
+ if (!edit || typeof edit.pageId !== "string" || !edit.pageId || edit.pageId.length > 256 || typeof edit.shapeId !== "string" || !edit.shapeId || edit.shapeId.length > 256)
5442
+ fail("INVALID_EDIT", "Invalid edit target.");
5443
+ const target = { pageId: edit.pageId, shapeId: edit.shapeId };
5444
+ if (edit.type === "replace-plain-text")
5445
+ return { ...target, type: edit.type, text: text(edit.text) };
5446
+ if (!/^[1-9]\d{0,9}$/.test(edit.shapeId) || Number(edit.shapeId) > 4294967295)
5447
+ fail("INVALID_EDIT", "Geometry shape IDs must be canonical positive unsigned integers.");
5448
+ switch (edit.type) {
5449
+ case "create-rectangle":
5450
+ return {
5451
+ ...target,
5452
+ type: edit.type,
5453
+ x: numeric4(edit.x),
5454
+ y: numeric4(edit.y),
5455
+ width: numeric4(edit.width, true),
5456
+ height: numeric4(edit.height, true),
5457
+ ...edit.text === void 0 ? {} : { text: text(edit.text) }
5458
+ };
5459
+ case "move-shape":
5460
+ return { ...target, type: edit.type, x: numeric4(edit.x), y: numeric4(edit.y) };
5461
+ case "resize-shape":
5462
+ return {
5463
+ ...target,
5464
+ type: edit.type,
5465
+ width: numeric4(edit.width, true),
5466
+ height: numeric4(edit.height, true)
5467
+ };
5468
+ case "delete-shape":
5469
+ return { ...target, type: edit.type };
5470
+ default:
5471
+ return fail("INVALID_EDIT", "Unsupported edit command.");
5472
+ }
5473
+ });
5474
+ }
5475
+
5476
+ // src/visio/formula-theme.ts
5477
+ var themeInputs = [
5478
+ "ColorSchemeIndex",
5479
+ "EffectSchemeIndex",
5480
+ "FontSchemeIndex",
5481
+ "ThemeIndex",
5482
+ "QuickStyleLineColor",
5483
+ "QuickStyleFillColor",
5484
+ "QuickStyleShadowColor",
5485
+ "QuickStyleFontColor",
5486
+ "QuickStyleLineMatrix",
5487
+ "QuickStyleFillMatrix",
5488
+ "QuickStyleEffectsMatrix",
5489
+ "QuickStyleFontMatrix",
5490
+ "QuickStyleVariation"
5491
+ ];
5492
+
5493
+ // src/visio/formula-evaluate.ts
5494
+ var supported = /* @__PURE__ */ new Set([
5495
+ "GUARD",
5496
+ "IF",
5497
+ "ABS",
5498
+ "MIN",
5499
+ "MAX",
5500
+ "SQRT",
5501
+ "SIN",
5502
+ "COS",
5503
+ "TAN",
5504
+ "ATAN2",
5505
+ "SIGN",
5506
+ "PI"
5507
+ ]);
5508
+ var staticUnsupported = /* @__PURE__ */ new Set([
5509
+ "ROUND",
5510
+ "INT",
5511
+ "MOD",
5512
+ "POW",
5513
+ "EXP",
5514
+ "LN",
5515
+ "LOG",
5516
+ "LOG10",
5517
+ "ASIN",
5518
+ "ACOS",
5519
+ "ATAN",
5520
+ "CEILING",
5521
+ "FLOOR",
5522
+ "TRUNC",
5523
+ "AND",
5524
+ "OR",
5525
+ "NOT",
5526
+ "SUM",
5527
+ "RGB",
5528
+ "HSL",
5529
+ "THEMEGUARD",
5530
+ "THEMEVAL",
5531
+ "THEME",
5532
+ "ISTHEMED",
5533
+ "BITAND",
5534
+ "BITOR",
5535
+ "BITXOR",
5536
+ "TINT",
5537
+ "MSOTINT",
5538
+ "SHADE"
5539
+ ]);
5540
+ var dynamic = /* @__PURE__ */ new Set([
5541
+ "INDIRECT",
5542
+ "EVALCELL",
5543
+ "EVALTEXT",
5544
+ "DEPENDSON",
5545
+ "SETATREF",
5546
+ "SETATREFEXPR",
5547
+ "SETATREFEVAL",
5548
+ "GETREF",
5549
+ "REF",
5550
+ "SHAPETEXT",
5551
+ "CALLOUTTARGET",
5552
+ "CONTAINERSHEETREF",
5553
+ "MEMBERSHEETREF",
5554
+ "PARENT",
5555
+ "PAR"
5556
+ ]);
5557
+ function traverse(ast, limits, visit) {
5558
+ const maxNodes = formulaLimit(limits.maxNodes, 1024), maxDepth = formulaLimit(limits.maxDepth, 64);
5559
+ const queue = [[ast, 0]];
5560
+ let count = 0;
5561
+ while (queue.length) {
5562
+ const [node, depth] = queue.pop();
5563
+ if (++count > maxNodes || depth > maxDepth)
5564
+ return formulaFailure("limit", "Formula analysis limit");
5565
+ visit(node);
5566
+ if (node.kind === "unary") queue.push([node.operand, depth + 1]);
5567
+ if (node.kind === "binary") queue.push([node.left, depth + 1], [node.right, depth + 1]);
5568
+ if (node.kind === "call") for (const arg of node.args) queue.push([arg, depth + 1]);
5569
+ }
5570
+ }
5571
+ function analyzeVisioFormula(source, limits = {}) {
5572
+ const ast = typeof source === "string" ? parseVisioFormula(source, limits) : source;
5573
+ const references = /* @__PURE__ */ new Map(), unsupportedFunctions = /* @__PURE__ */ new Set();
5574
+ let unsafe2 = false, guarded = false;
5575
+ traverse(ast, limits, (node) => {
5576
+ if (node.kind === "reference")
5577
+ references.set(
5578
+ `${node.reference.shapeId ?? ""}!${node.reference.cell.toLowerCase()}`,
5579
+ node.reference
5580
+ );
5581
+ if (node.kind === "call") {
5582
+ const inputs = node.name === "ISTHEMED" ? ["ColorSchemeIndex"] : ["THEMEVAL", "THEME"].includes(node.name) ? themeInputs : [];
5583
+ for (const cell of inputs) references.set(`!${cell.toLowerCase()}`, { cell });
5584
+ if (!supported.has(node.name)) unsupportedFunctions.add(node.name);
5585
+ if (dynamic.has(node.name) || !supported.has(node.name) && !staticUnsupported.has(node.name))
5586
+ unsafe2 = true;
5587
+ if (node.name === "GUARD") guarded = true;
5588
+ }
5589
+ });
5590
+ return {
5591
+ references: [...references.values()],
5592
+ unsupportedFunctions: [...unsupportedFunctions],
5593
+ dynamic: unsafe2,
5594
+ guarded
5595
+ };
5596
+ }
5597
+ function finite2(value) {
5598
+ if (!Number.isFinite(value.value)) return formulaFailure("value", "Formula result is not finite");
5599
+ if (!["scalar", "length", "angle", "time"].includes(value.unit))
5600
+ return formulaFailure("unit", "Unknown resolved unit");
5601
+ return value;
5602
+ }
5603
+ function compatible(a, b) {
5604
+ if (a.unit === b.unit) return a.unit;
5605
+ if (a.unit === "scalar" && a.value === 0) return b.unit;
5606
+ if (b.unit === "scalar" && b.value === 0) return a.unit;
5607
+ return formulaFailure("unit", `Incompatible units ${a.unit} and ${b.unit}`);
5608
+ }
5609
+ function binary(operator, a, b) {
5610
+ let unit;
5611
+ if (operator === "+" || operator === "-") {
5612
+ unit = compatible(a, b);
5613
+ return finite2({ value: operator === "+" ? a.value + b.value : a.value - b.value, unit });
5614
+ }
5615
+ if (operator === "*") {
5616
+ if (a.unit !== "scalar" && b.unit !== "scalar")
5617
+ return formulaFailure("unit", "Compound dimensions are unsupported");
5618
+ return finite2({ value: a.value * b.value, unit: a.unit === "scalar" ? b.unit : a.unit });
5619
+ }
5620
+ if (operator === "/") {
5621
+ if (b.value === 0) return formulaFailure("value", "Division by zero");
5622
+ if (b.unit !== "scalar" && a.unit !== b.unit)
5623
+ return formulaFailure("unit", "Inverse dimensions are unsupported");
5624
+ return finite2({ value: a.value / b.value, unit: a.unit === b.unit ? "scalar" : a.unit });
5625
+ }
5626
+ if (operator === "^") {
5627
+ if (b.unit !== "scalar" || a.unit !== "scalar" && b.value !== 1 && b.value !== 0)
5628
+ return formulaFailure("unit", "Dimensional powers are unsupported");
5629
+ return finite2({ value: a.value ** b.value, unit: b.value === 0 ? "scalar" : a.unit });
5630
+ }
5631
+ compatible(a, b);
5632
+ const comparisons = {
5633
+ "=": a.value === b.value,
5634
+ "<>": a.value !== b.value,
5635
+ "!=": a.value !== b.value,
5636
+ "<": a.value < b.value,
5637
+ ">": a.value > b.value,
5638
+ "<=": a.value <= b.value,
5639
+ ">=": a.value >= b.value
5640
+ };
5641
+ if (!(operator in comparisons))
5642
+ return formulaFailure("syntax", `Unsupported operator ${operator}`);
5643
+ return { value: comparisons[operator] ? 1 : 0, unit: "scalar" };
5644
+ }
5645
+ function evaluateVisioFormula(source, resolve, limits = {}) {
5646
+ const ast = typeof source === "string" ? parseVisioFormula(source, limits) : source;
5647
+ const maxSteps = formulaLimit(limits.maxSteps, 8192), maxDepth = formulaLimit(limits.maxDepth, 64);
5648
+ traverse(ast, limits, () => {
5649
+ });
5650
+ let steps = 0;
5651
+ const evaluate = (node, depth = 0) => {
5652
+ if (++steps > maxSteps || depth > maxDepth)
5653
+ return formulaFailure("limit", "Formula evaluation limit");
5654
+ limits.onStep?.();
5655
+ if (node.kind === "number") return finite2({ value: node.value, unit: node.unit });
5656
+ if (node.kind === "string")
5657
+ return formulaFailure("value", "String results are outside the numeric formula subset");
5658
+ if (node.kind === "reference") return finite2(resolve(node.reference));
5659
+ if (node.kind === "unary") {
5660
+ const result = evaluate(node.operand, depth + 1);
5661
+ return finite2({
5662
+ value: node.operator === "-" ? -result.value : result.value,
5663
+ unit: result.unit
5664
+ });
5665
+ }
5666
+ if (node.kind === "binary")
5667
+ return binary(node.operator, evaluate(node.left, depth + 1), evaluate(node.right, depth + 1));
5668
+ const name = node.name;
5669
+ if (!supported.has(name)) return formulaFailure("unsupported", `Unsupported function ${name}`);
5670
+ const arity = (min, max = min) => {
5671
+ if (node.args.length < min || node.args.length > max)
5672
+ return formulaFailure("arity", `Invalid ${name} argument count`);
5673
+ };
5674
+ const arg = (index) => evaluate(node.args[index], depth + 1);
5675
+ if (name === "PI") {
5676
+ arity(0);
5677
+ return { value: Math.PI, unit: "scalar" };
5678
+ }
5679
+ if (name === "GUARD") {
5680
+ arity(1);
5681
+ return arg(0);
5682
+ }
5683
+ if (name === "IF") {
5684
+ arity(3);
5685
+ const condition = arg(0);
5686
+ if (condition.unit !== "scalar") return formulaFailure("unit", "IF condition must be scalar");
5687
+ return arg(condition.value === 0 ? 2 : 1);
5688
+ }
5689
+ if (name === "MIN" || name === "MAX") {
5690
+ arity(1, 1024);
5691
+ const args = node.args.map((_, i) => arg(i));
5692
+ let result = args[0];
5693
+ for (const item of args.slice(1)) {
5694
+ const unit = compatible(result, item);
5695
+ result = {
5696
+ value: name === "MIN" ? Math.min(result.value, item.value) : Math.max(result.value, item.value),
5697
+ unit
5698
+ };
5699
+ }
5700
+ return finite2(result);
5701
+ }
5702
+ if (name === "ATAN2") {
5703
+ arity(2);
5704
+ const y = arg(0), x = arg(1);
5705
+ compatible(y, x);
5706
+ return { value: Math.atan2(y.value, x.value), unit: "angle" };
5707
+ }
5708
+ if (name === "SIGN") {
5709
+ arity(1, 2);
5710
+ const value2 = arg(0);
5711
+ const fuzz = node.args.length === 2 ? arg(1) : { value: 1e-9, unit: "scalar" };
5712
+ if (fuzz.value < 0 || fuzz.unit !== "scalar" && fuzz.unit !== value2.unit)
5713
+ return formulaFailure("unit", "Invalid SIGN fuzz");
5714
+ return {
5715
+ value: Math.abs(value2.value) <= fuzz.value ? 0 : Math.sign(value2.value),
5716
+ unit: "scalar"
5717
+ };
5718
+ }
5719
+ arity(1);
5720
+ const value = arg(0);
5721
+ if (name === "ABS") return { value: Math.abs(value.value), unit: value.unit };
5722
+ if (name === "SQRT") {
5723
+ if (value.unit !== "scalar") return formulaFailure("unit", "Dimensional SQRT unsupported");
5724
+ return finite2({ value: Math.sqrt(value.value), unit: "scalar" });
5725
+ }
5726
+ if (value.unit !== "angle" && value.unit !== "scalar")
5727
+ return formulaFailure("unit", `${name} requires radians`);
5728
+ return finite2({
5729
+ value: name === "SIN" ? Math.sin(value.value) : name === "COS" ? Math.cos(value.value) : Math.tan(value.value),
5730
+ unit: "scalar"
5731
+ });
5732
+ };
5733
+ return evaluate(ast);
5734
+ }
5735
+
5736
+ // src/visio/formula.ts
5737
+ var VisioFormulaError = class extends Error {
5738
+ constructor(code, message) {
5739
+ super(message);
5740
+ this.code = code;
5741
+ this.name = "VisioFormulaError";
5742
+ }
5743
+ code;
5744
+ };
5745
+ var formulaFailure = (code, message) => {
5746
+ throw new VisioFormulaError(code, message);
5747
+ };
5748
+ function formulaLimit(value, fallback) {
5749
+ if (value === void 0) return fallback;
5750
+ if (!Number.isSafeInteger(value) || value < 1 || value > fallback)
5751
+ return formulaFailure("limit", "Invalid or excessive formula limit");
5752
+ return value;
5753
+ }
5754
+ var units = {
5755
+ NUM: ["scalar", 1],
5756
+ BOOL: ["scalar", 1],
5757
+ DL: ["length", 1],
5758
+ DP: ["length", 1],
5759
+ DA: ["angle", 1],
5760
+ DT: ["length", 1],
5761
+ IN: ["length", 1],
5762
+ INCH: ["length", 1],
5763
+ MM: ["length", 1 / 25.4],
5764
+ CM: ["length", 1 / 2.54],
5765
+ PT: ["length", 1 / 72],
5766
+ FT: ["length", 12],
5767
+ DEG: ["angle", Math.PI / 180],
5768
+ RAD: ["angle", 1],
5769
+ SEC: ["time", 1],
5770
+ MIN: ["time", 60],
5771
+ HR: ["time", 3600]
5772
+ };
5773
+ function visioFormulaCachedValue(value, unit) {
5774
+ if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(value.trim()))
5775
+ return formulaFailure("value", "Non-numeric cell cache");
5776
+ const numeric4 = Number(value);
5777
+ if (!Number.isFinite(numeric4)) return formulaFailure("value", "Non-finite cell cache");
5778
+ if (!unit || unit === "NUM" || unit === "BOOL") return { value: numeric4, unit: "scalar" };
5779
+ const definition = Object.hasOwn(units, unit.toUpperCase()) ? units[unit.toUpperCase()] : void 0;
5780
+ if (!definition) return formulaFailure("unit", `Unsupported unit ${unit}`);
5781
+ return { value: numeric4, unit: definition[0] };
5782
+ }
5783
+ function parseVisioFormula(source, limits = {}) {
5784
+ if (source.length > formulaLimit(limits.maxLength, 8192))
5785
+ return formulaFailure("limit", "Formula length limit");
5786
+ const maxNodes = formulaLimit(limits.maxNodes, 1024), maxDepth = formulaLimit(limits.maxDepth, 64);
5787
+ let offset = 0, count = 0;
5788
+ const whitespace = () => {
5789
+ while (/\s/.test(source[offset] ?? "") && offset < source.length) offset++;
5790
+ };
5791
+ const node = (ast2) => {
5792
+ if (++count > maxNodes) return formulaFailure("limit", "Formula AST limit");
5793
+ return ast2;
5794
+ };
5795
+ const expression = (precedence = 0, depth = 0) => {
5796
+ if (depth > maxDepth) return formulaFailure("limit", "Formula depth limit");
5797
+ whitespace();
5798
+ let left;
5799
+ const char = source[offset];
5800
+ if (char === "+" || char === "-") {
5801
+ offset++;
5802
+ left = node({ kind: "unary", operator: char, operand: expression(3, depth + 1) });
5803
+ } else if (char === "(") {
5804
+ offset++;
5805
+ left = expression(0, depth + 1);
5806
+ whitespace();
5807
+ if (source[offset++] !== ")") return formulaFailure("syntax", "Expected closing parenthesis");
5808
+ } else if (char === '"') {
5809
+ offset++;
5810
+ let value = "", closed = false;
5811
+ while (offset < source.length) {
5812
+ const item = source[offset++];
5813
+ if (item === '"') {
5814
+ if (source[offset] === '"') {
5815
+ value += '"';
5816
+ offset++;
5817
+ } else {
5818
+ closed = true;
5819
+ break;
5820
+ }
5821
+ } else value += item;
5822
+ }
5823
+ if (!closed) return formulaFailure("syntax", "Unterminated formula string");
5824
+ left = node({ kind: "string", value });
5825
+ } else {
5826
+ const number2 = /^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/i.exec(source.slice(offset));
5827
+ if (number2) {
5828
+ offset += number2[0].length;
5829
+ whitespace();
5830
+ const suffix = /^[A-Za-z]+\b/.exec(source.slice(offset));
5831
+ let unit = "scalar", scale = 1;
5832
+ if (suffix) {
5833
+ const definition = Object.hasOwn(units, suffix[0].toUpperCase()) ? units[suffix[0].toUpperCase()] : void 0;
5834
+ if (!definition) return formulaFailure("unit", `Unsupported unit ${suffix[0]}`);
5835
+ [unit, scale] = definition;
5836
+ offset += suffix[0].length;
5837
+ }
5838
+ const value = Number(number2[0]) * scale;
5839
+ if (!Number.isFinite(value)) return formulaFailure("value", "Non-finite literal");
5840
+ left = node({ kind: "number", value, unit });
5841
+ } else {
5842
+ const identifier = /^(?:Sheet\.\d+!)?[A-Za-z_][A-Za-z_0-9]*(?:\.[A-Za-z_0-9]+)*/i.exec(
5843
+ source.slice(offset)
5844
+ );
5845
+ if (!identifier) return formulaFailure("syntax", `Unsupported formula token at ${offset}`);
5846
+ offset += identifier[0].length;
5847
+ whitespace();
5848
+ if (source[offset] === "(") {
5849
+ if (!/^[A-Za-z_][A-Za-z_0-9]*$/.test(identifier[0]))
5850
+ return formulaFailure("syntax", "Qualified function names are unsupported");
5851
+ offset++;
5852
+ const args = [];
5853
+ whitespace();
5854
+ if (source[offset] !== ")") {
5855
+ for (; ; ) {
5856
+ args.push(expression(0, depth + 1));
5857
+ whitespace();
5858
+ if (source[offset] !== ",") break;
5859
+ offset++;
5860
+ }
5861
+ }
5862
+ if (source[offset++] !== ")")
5863
+ return formulaFailure("syntax", "Expected function closing parenthesis");
5864
+ left = node({ kind: "call", name: identifier[0].toUpperCase(), args });
5865
+ } else {
5866
+ const ref = /^Sheet\.(\d+)!(.+)$/i.exec(identifier[0]);
5867
+ left = node({
5868
+ kind: "reference",
5869
+ reference: ref ? { shapeId: ref[1], cell: ref[2] } : { cell: identifier[0] }
5870
+ });
5871
+ }
5872
+ }
5873
+ }
5874
+ for (; ; ) {
5875
+ whitespace();
5876
+ const operator = /^(?:<=|>=|<>|!=|=|<|>|\+|-|\*|\/|\^)/.exec(source.slice(offset))?.[0];
5877
+ if (!operator) break;
5878
+ const rank = ["=", "<>", "!=", "<", ">", "<=", ">="].includes(operator) ? 1 : ["+", "-"].includes(operator) ? 2 : ["*", "/"].includes(operator) ? 3 : 4;
5879
+ if (rank <= precedence) break;
5880
+ offset += operator.length;
5881
+ left = node({
5882
+ kind: "binary",
5883
+ operator,
5884
+ left,
5885
+ right: expression(operator === "^" ? rank - 1 : rank, depth + 1)
5886
+ });
5887
+ }
5888
+ return left;
5889
+ };
5890
+ whitespace();
5891
+ if (source[offset] === "=") offset++;
5892
+ const ast = expression();
5893
+ whitespace();
5894
+ if (offset !== source.length)
5895
+ return formulaFailure("syntax", `Trailing formula content at ${offset}`);
5896
+ return ast;
5897
+ }
5898
+
5899
+ // src/visio/cell-formula.ts
5900
+ function executableCellFormula(source) {
5901
+ return !source || source === "No Formula" || source === "Inh" ? void 0 : source;
5902
+ }
5903
+ function inertDoubleClickFormula(cell, source) {
5904
+ return cell === "EventDblClick" && /^\s*=?\s*(OPENTEXTWIN|DEFAULTEVENT)\(\s*\)\s*$/i.test(source);
5905
+ }
5906
+
5907
+ // src/visio/edit-recalculate-index.ts
5908
+ var key = (cell) => JSON.stringify([cell.pageId, cell.shapeId, cell.cell.toLowerCase()]);
5909
+ function bound(value, fallback) {
5910
+ const result = value ?? fallback;
5911
+ if (!Number.isSafeInteger(result) || result < 1)
5912
+ fail("INVALID_LIMITS", "Invalid recalculation limit.");
5913
+ return result;
5914
+ }
5915
+ function cellUnit(name, relative) {
5916
+ if (/^(Angle|TxtAngle)$/i.test(name)) return "angle";
5917
+ if (/^(Width|Height|PinX|PinY|LocPinX|LocPinY|BeginX|BeginY|EndX|EndY|TxtPinX|TxtPinY|TxtWidth|TxtHeight|TxtLocPinX|TxtLocPinY|LineWeight|Rounding)$/i.test(
5918
+ name
5919
+ ))
5920
+ return "length";
5921
+ return !relative && /\.[XY]\d+$/i.test(name) ? "length" : "scalar";
5922
+ }
5923
+ function indexCells(roots, options) {
5924
+ const cells3 = /* @__PURE__ */ new Map();
5925
+ const maxCells = bound(options.maxCells, 1e5);
5926
+ const add = (item) => {
5927
+ const id = key(item);
5928
+ if (cells3.has(id)) fail("EDIT_AMBIGUOUS_CELL", "Duplicate local ShapeSheet cell.");
5929
+ cells3.set(id, item);
5930
+ if (cells3.size > maxCells) fail("LIMIT_FORMULA_CELLS", "ShapeSheet cell limit exceeded.");
5931
+ };
5932
+ for (const [pageId, root] of roots) {
5933
+ if (root.hasAttribute("F"))
5934
+ fail("EDIT_UNKNOWN_DEPENDENCY", "Page root formula has unknown dependency scope.");
5935
+ const indexedNodes = /* @__PURE__ */ new Set();
5936
+ const connected = /* @__PURE__ */ new Set();
5937
+ for (const container of children(root, "Connects"))
5938
+ for (const connection of children(container, "Connect")) {
5939
+ for (const attr2 of ["FromSheet", "ToSheet"]) {
5940
+ const id = attribute2(connection, attr2);
5941
+ if (id) connected.add(id);
5942
+ }
5943
+ }
5944
+ const sheets = [];
5945
+ const ids = /* @__PURE__ */ new Set();
5946
+ const visit = (parent, inherited, nested) => {
5947
+ for (const container of children(parent, "Shapes"))
5948
+ for (const node of children(container, "Shape")) {
5949
+ options.check?.();
5950
+ const shapeId = attribute2(node, "ID");
5951
+ if (!shapeId || ids.has(shapeId)) fail("INVALID_SHAPE_ID", "Shape IDs must be unique.");
5952
+ ids.add(shapeId);
5953
+ const unsafe2 = inherited || nested || node.hasAttribute("Master") || node.hasAttribute("MasterShape") || attribute2(node, "Type") === "Group" || attribute2(node, "Type") !== void 0 && attribute2(node, "Type") !== "Shape" || connected.has(shapeId) || children(node, "ForeignData").length > 0 || children(node, "Rel").length > 0 || children(node, "Cell").some(
5954
+ (cell) => /^(BeginX|BeginY|EndX|EndY)$/i.test(attribute2(cell, "N") ?? "") || attribute2(cell, "N")?.toLowerCase() === "oned" && (attribute2(cell, "V") !== "0" || !!executableCellFormula(attribute2(cell, "F")) || attribute2(cell, "F") === "Inh")
5955
+ ) || children(node, "Shapes").length > 0 || ["1", "true"].includes(attribute2(node, "Del") ?? "");
5956
+ sheets.push({ node, shapeId, unsafe: unsafe2 });
5957
+ visit(node, unsafe2, true);
5958
+ }
5959
+ };
5960
+ visit(root, false, false);
5961
+ for (const node of children(root, "PageSheet"))
5962
+ sheets.push({ node, shapeId: "", unsafe: false });
5963
+ for (const sheet of sheets) {
5964
+ const addNode = (node, cell, relative = false, deleted = false) => {
5965
+ indexedNodes.add(node);
5966
+ const item = {
5967
+ pageId,
5968
+ shapeId: sheet.shapeId,
5969
+ cell,
5970
+ node,
5971
+ unsafe: sheet.unsafe || deleted,
5972
+ dependencies: [],
5973
+ unit: cellUnit(cell, relative)
5974
+ };
5975
+ const source = attribute2(node, "F");
5976
+ if (executableCellFormula(source) && !inertDoubleClickFormula(cell, source)) {
5977
+ try {
5978
+ item.formula = parseVisioFormula(source);
5979
+ const analysis = analyzeVisioFormula(item.formula);
5980
+ if (analysis.dynamic)
5981
+ fail(
5982
+ "EDIT_DYNAMIC_DEPENDENCY",
5983
+ "Dynamic ShapeSheet dependencies cannot be safely edited."
5984
+ );
5985
+ item.dependencies = analysis.references.map(
5986
+ (ref) => key({ pageId, shapeId: ref.shapeId ?? sheet.shapeId, cell: ref.cell })
5987
+ );
5988
+ } catch (error) {
5989
+ fail(
5990
+ "EDIT_UNKNOWN_DEPENDENCY",
5991
+ `Cannot prove ShapeSheet dependency closure: ${error instanceof Error ? error.message : "invalid formula"}`
5992
+ );
5993
+ }
5994
+ }
5995
+ if (source === "Inh") item.unsafe = true;
5996
+ add(item);
5997
+ };
5998
+ for (const node of children(sheet.node, "Cell")) addNode(node, attribute2(node, "N") ?? "");
5999
+ for (const section of children(sheet.node, "Section")) {
6000
+ const name = attribute2(section, "N") ?? "";
6001
+ const prefix = name === "Geometry" ? `Geometry${Number(attribute2(section, "IX") ?? "0") + 1}` : name;
6002
+ const deleted = ["1", "true"].includes(attribute2(section, "Del") ?? "");
6003
+ for (const node of children(section, "Cell"))
6004
+ addNode(node, `${prefix}.${attribute2(node, "N") ?? ""}`, false, deleted);
6005
+ for (const row of children(section, "Row")) {
6006
+ const rowName = attribute2(row, "N");
6007
+ const ix = attribute2(row, "IX") ?? "0";
6008
+ for (const node of children(row, "Cell")) {
6009
+ const n = attribute2(node, "N") ?? "";
6010
+ const rowCell = name === "Geometry" || ["Controls", "Scratch", "Connection"].includes(name) ? `${prefix}.${n}${ix}` : n === "Value" && ["User", "Prop"].includes(name) ? `${prefix}.${rowName ?? ix}` : `${prefix}.${rowName ?? ix}.${n}`;
6011
+ addNode(
6012
+ node,
6013
+ rowCell,
6014
+ (attribute2(row, "T") ?? "").startsWith("Rel"),
6015
+ deleted || ["1", "true"].includes(attribute2(row, "Del") ?? "")
6016
+ );
6017
+ }
6018
+ }
6019
+ }
6020
+ if (sheet.shapeId)
6021
+ for (const [cell, dimension, scale] of [
6022
+ ["LocPinX", "Width", 0.5],
6023
+ ["LocPinY", "Height", 0.5],
6024
+ ["TxtWidth", "Width", 1],
6025
+ ["TxtHeight", "Height", 1],
6026
+ ["TxtPinX", "Width", 0.5],
6027
+ ["TxtPinY", "Height", 0.5],
6028
+ ["TxtLocPinX", "TxtWidth", 0.5],
6029
+ ["TxtLocPinY", "TxtHeight", 0.5]
6030
+ ]) {
6031
+ const ref = { pageId, shapeId: sheet.shapeId, cell };
6032
+ if (!cells3.has(key(ref)))
6033
+ add({
6034
+ ...ref,
6035
+ unsafe: sheet.unsafe,
6036
+ unit: "length",
6037
+ dependencies: [key({ ...ref, cell: dimension })],
6038
+ formula: parseVisioFormula(`${dimension}*${scale}`)
6039
+ });
6040
+ }
6041
+ }
6042
+ for (const node of Array.from(root.getElementsByTagName("*"))) {
6043
+ options.check?.();
6044
+ if (node.hasAttribute("F") && !indexedNodes.has(node))
6045
+ fail(
6046
+ "EDIT_UNKNOWN_DEPENDENCY",
6047
+ "Formula outside admitted ShapeSheet cells has unknown dependencies."
6048
+ );
6049
+ }
6050
+ }
6051
+ return cells3;
6052
+ }
6053
+
6054
+ // src/visio/edit-recalculate.ts
6055
+ function recalculateVisioCells(roots, changed, options = {}) {
6056
+ const cells3 = indexCells(roots, options);
6057
+ const reverse = /* @__PURE__ */ new Map();
6058
+ for (const [id, item] of cells3)
6059
+ for (const dependency of item.dependencies) {
6060
+ const next = reverse.get(dependency) ?? [];
6061
+ next.push(id);
6062
+ reverse.set(dependency, next);
6063
+ }
6064
+ const affected = new Set(changed.map(key));
6065
+ const pending = [...affected];
6066
+ const maxAffected = bound(options.maxAffectedCells, 1e4);
6067
+ for (let i = 0; i < pending.length; i++) {
6068
+ options.check?.();
6069
+ for (const id of reverse.get(pending[i]) ?? [])
6070
+ if (!affected.has(id)) {
6071
+ affected.add(id);
6072
+ pending.push(id);
6073
+ }
6074
+ if (affected.size > maxAffected)
6075
+ fail("LIMIT_FORMULA_AFFECTED", "Affected ShapeSheet cell limit exceeded.");
6076
+ }
6077
+ const active = /* @__PURE__ */ new Set(), values = /* @__PURE__ */ new Map();
6078
+ let steps = bound(options.maxSteps, 1e5);
6079
+ const maxDepth = bound(options.maxDepth, 64);
6080
+ const evaluate = (id) => {
6081
+ options.check?.();
6082
+ if (--steps < 0)
6083
+ fail("LIMIT_FORMULA_STEPS", "ShapeSheet dependency evaluation limit exceeded.");
6084
+ const cached2 = values.get(id);
6085
+ if (cached2) return cached2;
6086
+ if (active.has(id)) fail("EDIT_FORMULA_CYCLE", "Affected ShapeSheet dependency cycle.");
6087
+ if (active.size >= maxDepth)
6088
+ fail("LIMIT_FORMULA_DEPTH", "ShapeSheet dependency depth limit exceeded.");
6089
+ const item = cells3.get(id);
6090
+ if (!item || item.unsafe)
6091
+ fail(
6092
+ "EDIT_UNSUPPORTED_DEPENDENCY",
6093
+ "Affected formula references missing, inherited or grouped cells."
6094
+ );
6095
+ if (item.node?.hasAttribute("E"))
6096
+ fail("EDIT_FORMULA_ERROR", "Affected ShapeSheet cell has an error cache.");
6097
+ active.add(id);
6098
+ let result;
6099
+ if (item.formula) {
6100
+ const analysis = analyzeVisioFormula(item.formula);
6101
+ if (analysis.unsupportedFunctions.length)
6102
+ fail("EDIT_UNSUPPORTED_FORMULA", "Affected formula uses unsupported functions.");
6103
+ result = evaluateVisioFormula(
6104
+ item.formula,
6105
+ (ref) => evaluate(
6106
+ key({ pageId: item.pageId, shapeId: ref.shapeId ?? item.shapeId, cell: ref.cell })
6107
+ ),
6108
+ {
6109
+ onStep: () => {
6110
+ if (--steps < 0)
6111
+ fail("LIMIT_FORMULA_STEPS", "ShapeSheet dependency evaluation limit exceeded.");
6112
+ }
6113
+ }
6114
+ );
6115
+ } else
6116
+ result = visioFormulaCachedValue(
6117
+ attribute2(item.node, "V") ?? "",
6118
+ attribute2(item.node, "U") ?? (item.unit === "length" ? "DL" : item.unit === "angle" ? "DA" : void 0)
6119
+ );
6120
+ if (result.unit === "scalar" && item.unit !== "scalar") result = { ...result, unit: item.unit };
6121
+ const declared = attribute2(item.node, "U");
6122
+ const expected = declared ? visioFormulaCachedValue("0", declared).unit : item.unit;
6123
+ if (result.unit !== expected)
6124
+ fail("EDIT_FORMULA_UNIT", "Affected formula cache has incompatible units.");
6125
+ if (!Number.isFinite(result.value) || Math.abs(result.value) > 1e9)
6126
+ fail("EDIT_FORMULA_VALUE", "Affected formula cache exceeds supported magnitude.");
6127
+ active.delete(id);
6128
+ values.set(id, result);
6129
+ return result;
6130
+ };
6131
+ for (const id of affected) evaluate(id);
6132
+ const pages = /* @__PURE__ */ new Set();
6133
+ for (const id of affected) {
6134
+ const item = cells3.get(id);
6135
+ if (item.node && item.formula) {
6136
+ const value = String(values.get(id).value);
6137
+ if (item.node.getAttribute("V") !== value) {
6138
+ item.node.setAttribute("V", value);
6139
+ pages.add(item.pageId);
6140
+ }
6141
+ }
6142
+ }
6143
+ return [...pages];
6144
+ }
6145
+ function assertVisioShapeUnreferenced(roots, pageId, shapeId, options = {}) {
6146
+ const cells3 = indexCells(roots, options);
6147
+ for (const cell of cells3.values()) {
6148
+ if (cell.pageId !== pageId || cell.shapeId === shapeId) continue;
6149
+ if (cell.dependencies.some((id) => {
6150
+ const parts = JSON.parse(id);
6151
+ return parts[0] === pageId && parts[1] === shapeId;
6152
+ }))
6153
+ fail("EDIT_REFERENCED_DELETE", "Shape is referenced by a ShapeSheet formula.");
6154
+ }
6155
+ const root = roots.get(pageId);
6156
+ if (root)
6157
+ for (const connections2 of children(root, "Connects"))
6158
+ for (const connection of children(connections2, "Connect")) {
6159
+ if (["FromSheet", "ToSheet"].some((name) => attribute2(connection, name) === shapeId))
6160
+ fail("EDIT_REFERENCED_DELETE", "Shape participates in a Connect record.");
6161
+ }
6162
+ }
6163
+ function visioCellDependsOn(roots, source, targets, options = {}) {
6164
+ return createVisioDependencyQuery(roots, options)(source, targets);
6165
+ }
6166
+ function createVisioDependencyQuery(roots, options = {}) {
6167
+ const cells3 = indexCells(roots, options);
6168
+ return (source, targets) => {
6169
+ const wanted = new Set(targets.map(key));
6170
+ const seen = /* @__PURE__ */ new Set();
6171
+ const pending = [key(source)];
6172
+ const maxSteps = bound(options.maxSteps, 1e5);
6173
+ let found = false;
6174
+ while (pending.length) {
6175
+ options.check?.();
6176
+ const id = pending.pop();
6177
+ if (seen.has(id)) continue;
6178
+ seen.add(id);
6179
+ if (seen.size > maxSteps) fail("LIMIT_FORMULA_STEPS", "Dependency analysis limit exceeded.");
6180
+ const item = cells3.get(id);
6181
+ if (!item || item.unsafe || item.node?.hasAttribute("E")) return false;
6182
+ if (item.formula && analyzeVisioFormula(item.formula).unsupportedFunctions.length)
6183
+ return false;
6184
+ if (wanted.has(id)) found = true;
6185
+ else pending.push(...item.dependencies);
6186
+ }
6187
+ return found;
6188
+ };
6189
+ }
6190
+
6191
+ // src/visio/edit-geometry-admission.ts
6192
+ var locks = [
6193
+ "LockMoveX",
6194
+ "LockMoveY",
6195
+ "LockWidth",
6196
+ "LockHeight",
6197
+ "LockAspect",
6198
+ "LockDelete"
6199
+ ];
6200
+ var cells = (shape) => new Map(children(shape, "Cell").map((node) => [attribute2(node, "N") ?? "", node]));
6201
+ function numeric3(node, fallback) {
6202
+ if (!node && fallback !== void 0) return fallback;
6203
+ if (!node || node.hasAttribute("E"))
6204
+ fail("UNSUPPORTED_GEOMETRY_EDIT", "A usable local numeric transform cache is required.");
6205
+ const value = visioFormulaCachedValue(attribute2(node, "V") ?? "", attribute2(node, "U")).value;
6206
+ if (Math.abs(value) > 1e6)
6207
+ fail("UNSUPPORTED_GEOMETRY_EDIT", "Geometry cache exceeds coordinate limits.");
6208
+ return value;
6209
+ }
6210
+ function editableCell(node) {
6211
+ if (!node) return;
6212
+ if (node.hasAttribute("E")) fail("UNSUPPORTED_GEOMETRY_EDIT", "Cannot overwrite an error cell.");
6213
+ const source = attribute2(node, "F");
6214
+ if (source === "Inh")
6215
+ fail("EDIT_PROTECTED_CELL", "Inherited transform formulas cannot be overwritten.");
6216
+ const formula = executableCellFormula(source);
6217
+ if (!formula) return;
6218
+ const analysis = analyzeVisioFormula(formula);
6219
+ if (analysis.guarded || analysis.dynamic || analysis.references.length || analysis.unsupportedFunctions.length)
6220
+ fail(
6221
+ "EDIT_PROTECTED_CELL",
6222
+ "Transform formulas, GUARD and SETATREF redirection cannot be overwritten."
6223
+ );
6224
+ evaluateVisioFormula(
6225
+ formula,
6226
+ () => fail("UNSUPPORTED_GEOMETRY_EDIT", "Unexpected formula dependency.")
6227
+ );
6228
+ }
6229
+ function setCell(shape, name, value, formula) {
6230
+ let node = cells(shape).get(name);
6231
+ if (!node) {
6232
+ node = shape.ownerDocument.createElementNS(shape.namespaceURI, "Cell");
6233
+ node.setAttribute("N", name);
6234
+ const before = Array.from(shape.childNodes).find(
6235
+ (child2) => child2.nodeType === 1 && child2.localName !== "Cell"
6236
+ );
6237
+ shape.insertBefore(node, before ?? null);
6238
+ }
6239
+ node.setAttribute("V", String(value));
6240
+ if (formula) node.setAttribute("F", formula);
6241
+ else if (attribute2(node, "F") !== "No Formula") node.removeAttribute("F");
6242
+ }
6243
+ function admitted(root, shapeId) {
6244
+ const containers = children(root, "Shapes");
6245
+ if (containers.length !== 1)
6246
+ fail("UNSUPPORTED_GEOMETRY_EDIT", "One local Shapes container is required.");
6247
+ const candidates = children(containers[0], "Shape").filter(
6248
+ (node) => attribute2(node, "ID") === shapeId
6249
+ );
6250
+ if (candidates.length !== 1)
6251
+ fail("EDIT_TARGET_NOT_FOUND", "A unique top-level local shape is required.");
6252
+ const shape = candidates[0];
6253
+ if (shape.hasAttribute("Master") || shape.hasAttribute("MasterShape") || ["1", "true"].includes(attribute2(shape, "Del") ?? "") || children(shape, "Shapes").length || children(shape, "ForeignData").length || children(shape, "Rel").length || attribute2(shape, "Type") && attribute2(shape, "Type") !== "Shape")
6254
+ fail(
6255
+ "UNSUPPORTED_GEOMETRY_EDIT",
6256
+ "Master, group, foreign, deleted and non-shape operations are unsupported."
6257
+ );
6258
+ const local = cells(shape);
6259
+ for (const name of ["Width", "Height", "PinX", "PinY", "LocPinX", "LocPinY"]) {
6260
+ const cell = local.get(name), unit = attribute2(cell, "U");
6261
+ if (unit && visioFormulaCachedValue("0", unit).unit !== "length")
6262
+ fail("EDIT_FORMULA_UNIT", "Transform cells must use length units.");
6263
+ }
6264
+ if (numeric3(local.get("OneD"), 0) !== 0 || ["BeginX", "BeginY", "EndX", "EndY"].some((name) => local.has(name)))
6265
+ fail(
6266
+ "UNSUPPORTED_GEOMETRY_EDIT",
6267
+ "Only local 2D shapes are admitted; line routing and glue are unsupported."
6268
+ );
6269
+ if (!(numeric3(local.get("Width")) > 0) || !(numeric3(local.get("Height")) > 0))
6270
+ fail("UNSUPPORTED_GEOMETRY_EDIT", "Positive local Width and Height caches are required.");
6271
+ return shape;
6272
+ }
6273
+ function protectedShape(shape, document) {
6274
+ const local = cells(shape);
6275
+ const styles = /* @__PURE__ */ new Map();
6276
+ for (const container of children(document, "StyleSheets"))
6277
+ for (const node of children(container, "StyleSheet")) {
6278
+ const id = attribute2(node, "ID");
6279
+ if (!id || styles.has(id)) fail("EDIT_PROTECTED_CELL", "Ambiguous protection style IDs.");
6280
+ styles.set(id, node);
6281
+ }
6282
+ const inactive = (cell) => {
6283
+ try {
6284
+ if (cell.hasAttribute("E") || visioFormulaCachedValue(attribute2(cell, "V") ?? "", attribute2(cell, "U")).unit !== "scalar" || numeric3(cell) !== 0)
6285
+ fail("EDIT_PROTECTED_CELL", "Inherited protection is active or invalid.");
6286
+ const source = executableCellFormula(attribute2(cell, "F"));
6287
+ if (!source) return;
6288
+ const analysis = analyzeVisioFormula(source);
6289
+ if (analysis.dynamic || analysis.references.length || analysis.unsupportedFunctions.length)
6290
+ fail("EDIT_PROTECTED_CELL", "Inherited protection formula cannot be resolved safely.");
6291
+ const result = evaluateVisioFormula(
6292
+ source,
6293
+ () => fail("EDIT_PROTECTED_CELL", "Inherited protection has dependencies.")
6294
+ );
6295
+ if (result.unit !== "scalar" || result.value !== 0)
6296
+ fail("EDIT_PROTECTED_CELL", "Inherited protection formula is active or stale.");
6297
+ } catch (error) {
6298
+ fail(
6299
+ "EDIT_PROTECTED_CELL",
6300
+ `Inherited protection cannot be proven inactive: ${error instanceof Error ? error.message : "invalid cache"}`
6301
+ );
6302
+ }
6303
+ };
6304
+ const resolve = (id, category, lock, seen = /* @__PURE__ */ new Set()) => {
6305
+ if (seen.size >= 64 || seen.has(id))
6306
+ fail("EDIT_PROTECTED_CELL", "Protection style ancestry is cyclic or too deep.");
6307
+ seen.add(id);
6308
+ const style = styles.get(id);
6309
+ if (!style) fail("EDIT_PROTECTED_CELL", "Protection style ancestry cannot be resolved.");
6310
+ const cell = cells(style).get(lock), parent = attribute2(style, category);
6311
+ if (cell && attribute2(cell, "F") !== "Inh") {
6312
+ inactive(cell);
6313
+ return cell;
6314
+ }
6315
+ if (cell) {
6316
+ inactive(cell);
6317
+ if (parent === void 0 || parent === id)
6318
+ fail("EDIT_PROTECTED_CELL", "Inherited protection has no provable parent.");
6319
+ const inherited = resolve(parent, category, lock, seen);
6320
+ if (!inherited)
6321
+ fail("EDIT_PROTECTED_CELL", "Inherited protection has no explicit inactive ancestor.");
6322
+ return inherited;
6323
+ }
6324
+ return parent !== void 0 && parent !== id ? resolve(parent, category, lock, seen) : void 0;
6325
+ };
6326
+ const defaults = children(document, "DocumentSheet")[0];
6327
+ for (const category of ["LineStyle", "FillStyle", "TextStyle"]) {
6328
+ const id = attribute2(shape, category) ?? attribute2(defaults, category) ?? (styles.has("0") ? "0" : void 0);
6329
+ if (id === void 0) continue;
6330
+ for (const lock of locks) resolve(id, category, lock);
6331
+ }
6332
+ for (const lock of locks)
6333
+ if (attribute2(local.get(lock), "F") === "Inh")
6334
+ fail("EDIT_PROTECTED_CELL", "Local inherited protection cannot be overridden.");
6335
+ for (const lock of locks) {
6336
+ const cell = local.get(lock);
6337
+ if (!cell) continue;
6338
+ if (visioFormulaCachedValue("0", attribute2(cell, "U")).unit !== "scalar")
6339
+ fail("EDIT_FORMULA_UNIT", "Protection cells must use scalar units.");
6340
+ const formula = executableCellFormula(attribute2(cell, "F"));
6341
+ if (formula) {
6342
+ const analysis = analyzeVisioFormula(formula);
6343
+ if (analysis.dynamic || analysis.references.length || analysis.unsupportedFunctions.length)
6344
+ fail("EDIT_PROTECTED_CELL", "Protection formula cannot be resolved safely.");
6345
+ const result = evaluateVisioFormula(
6346
+ formula,
6347
+ () => fail("EDIT_PROTECTED_CELL", "Protection dependency is unsupported.")
6348
+ );
6349
+ if (result.unit !== "scalar" || result.value !== numeric3(cell))
6350
+ fail("EDIT_PROTECTED_CELL", "Protection cache is stale.");
6351
+ }
6352
+ }
6353
+ }
6354
+ function resizeGeometry(shape, roots, edit, check) {
6355
+ const sections = children(shape, "Section").filter((node) => attribute2(node, "N") === "Geometry");
6356
+ if (!sections.length)
6357
+ fail("UNSUPPORTED_GEOMETRY_EDIT", "Resizing requires explicit supported local geometry.");
6358
+ for (const section of sections) {
6359
+ if (section.hasAttribute("Del"))
6360
+ fail("UNSUPPORTED_GEOMETRY_EDIT", "Deleted geometry is unsupported.");
6361
+ for (const row of children(section, "Row")) {
6362
+ const type = attribute2(row, "T");
6363
+ if (row.hasAttribute("Del") || !["MoveTo", "LineTo", "RelMoveTo", "RelLineTo"].includes(type ?? ""))
6364
+ fail("UNSUPPORTED_GEOMETRY_EDIT", "This bundle resizes only local line-based geometry.");
6365
+ for (const node of children(row, "Cell")) {
6366
+ if (!["X", "Y"].includes(attribute2(node, "N") ?? ""))
6367
+ fail("UNSUPPORTED_GEOMETRY_EDIT", "Unknown geometry row cells are unsupported.");
6368
+ if ((type ?? "").startsWith("Rel")) continue;
6369
+ const formula = executableCellFormula(attribute2(node, "F"));
6370
+ if (!formula && numeric3(node) === 0) continue;
6371
+ if (!formula)
6372
+ fail(
6373
+ "UNSUPPORTED_GEOMETRY_EDIT",
6374
+ "Absolute geometry needs a provable dimension-dependent formula."
6375
+ );
6376
+ const analysis = analyzeVisioFormula(formula);
6377
+ if (!analysis.references.length && numeric3(node) === 0) continue;
6378
+ const cell = `Geometry${Number(attribute2(section, "IX") ?? "0") + 1}.${attribute2(node, "N")}${attribute2(row, "IX")}`;
6379
+ if (!visioCellDependsOn(
6380
+ roots,
6381
+ { pageId: edit.pageId, shapeId: edit.shapeId, cell },
6382
+ ["Width", "Height"].map((cell2) => ({
6383
+ pageId: edit.pageId,
6384
+ shapeId: edit.shapeId,
6385
+ cell: cell2
6386
+ })),
6387
+ { check }
6388
+ ))
6389
+ fail(
6390
+ "UNSUPPORTED_GEOMETRY_EDIT",
6391
+ "Absolute geometry scaling must explicitly reference Width or Height."
6392
+ );
6393
+ }
6394
+ }
6395
+ }
6396
+ }
6397
+
6398
+ // src/visio/edit-geometry.ts
6399
+ function createRectangle(root, edit) {
6400
+ const doc = root.ownerDocument;
6401
+ const node = (name) => doc.createElementNS(root.namespaceURI, name);
6402
+ let container = children(root, "Shapes")[0];
6403
+ if (children(root, "Shapes").length > 1) fail("INVALID_SHAPE_ID", "Duplicate Shapes containers.");
6404
+ const pending = [root];
6405
+ while (pending.length) {
6406
+ const parent = pending.pop();
6407
+ for (const shapes of children(parent, "Shapes"))
6408
+ for (const shape2 of children(shapes, "Shape")) {
6409
+ if (attribute2(shape2, "ID") === edit.shapeId)
6410
+ fail("INVALID_SHAPE_ID", "Shape ID already exists.");
6411
+ pending.push(shape2);
6412
+ }
6413
+ }
6414
+ if (!container) {
6415
+ container = node("Shapes");
6416
+ root.insertBefore(container, children(root, "Connects")[0] ?? null);
6417
+ }
6418
+ const shape = node("Shape");
6419
+ shape.setAttribute("ID", edit.shapeId);
6420
+ shape.setAttribute("Type", "Shape");
6421
+ for (const [name, value] of Object.entries({
6422
+ PinX: edit.x,
6423
+ PinY: edit.y,
6424
+ Width: edit.width,
6425
+ Height: edit.height,
6426
+ LocPinX: edit.width / 2,
6427
+ LocPinY: edit.height / 2,
6428
+ Angle: 0
6429
+ }))
6430
+ setCell(
6431
+ shape,
6432
+ name,
6433
+ value,
6434
+ name === "LocPinX" ? "Width*0.5" : name === "LocPinY" ? "Height*0.5" : void 0
6435
+ );
6436
+ const section = node("Section");
6437
+ section.setAttribute("N", "Geometry");
6438
+ section.setAttribute("IX", "0");
6439
+ for (const [index, [x, y]] of [
6440
+ [0, 0],
6441
+ [1, 0],
6442
+ [1, 1],
6443
+ [0, 1],
6444
+ [0, 0]
6445
+ ].entries()) {
6446
+ const row = node("Row");
6447
+ row.setAttribute("IX", String(index + 1));
6448
+ row.setAttribute("T", index ? "RelLineTo" : "RelMoveTo");
6449
+ setCell(row, "X", x);
6450
+ setCell(row, "Y", y);
6451
+ section.appendChild(row);
6452
+ }
6453
+ shape.appendChild(section);
6454
+ const text = node("Text");
6455
+ text.appendChild(doc.createTextNode(edit.text ?? ""));
6456
+ shape.appendChild(text);
6457
+ container.appendChild(shape);
6458
+ return shape;
6459
+ }
6460
+ function applyGeometryEdit(roots, document, edit, check) {
6461
+ check();
6462
+ const root = roots.get(edit.pageId);
6463
+ if (!root) fail("EDIT_TARGET_NOT_FOUND", "Page does not exist.");
6464
+ const changed = [];
6465
+ let expected;
6466
+ const add = (cell) => changed.push({ pageId: edit.pageId, shapeId: edit.shapeId, cell });
6467
+ if (edit.type === "create-rectangle") {
6468
+ createRectangle(root, edit);
6469
+ expected = { width: edit.width, height: edit.height, x: edit.x, y: edit.y };
6470
+ for (const name of ["Width", "Height", "PinX", "PinY"]) add(name);
6471
+ } else {
6472
+ const shape = admitted(root, edit.shapeId);
6473
+ protectedShape(shape, document);
6474
+ if (edit.type !== "delete-shape") {
6475
+ for (const connections2 of children(root, "Connects"))
6476
+ for (const connection of children(connections2, "Connect"))
6477
+ if (["FromSheet", "ToSheet"].some((name) => attribute2(connection, name) === edit.shapeId))
6478
+ fail(
6479
+ "UNSUPPORTED_GEOMETRY_EDIT",
6480
+ "Glued connections need routing and endpoint recalculation outside this subset."
6481
+ );
6482
+ }
6483
+ const local = cells(shape);
6484
+ const unlocked = (name) => {
6485
+ if (numeric3(local.get(name), 0) !== 0)
6486
+ fail("EDIT_PROTECTED_CELL", `${name} prevents this operation.`);
6487
+ };
6488
+ if (edit.type === "delete-shape") {
6489
+ unlocked("LockDelete");
6490
+ assertVisioShapeUnreferenced(roots, edit.pageId, edit.shapeId, { check });
6491
+ shape.parentNode.removeChild(shape);
6492
+ return [edit.pageId];
6493
+ }
6494
+ if (edit.type === "move-shape") {
6495
+ expected = {
6496
+ width: numeric3(local.get("Width")),
6497
+ height: numeric3(local.get("Height")),
6498
+ x: edit.x,
6499
+ y: edit.y
6500
+ };
6501
+ for (const [name, value, lock] of [
6502
+ ["PinX", edit.x, "LockMoveX"],
6503
+ ["PinY", edit.y, "LockMoveY"]
6504
+ ]) {
6505
+ if (numeric3(
6506
+ local.get(name),
6507
+ name === "PinX" ? numeric3(local.get("Width")) / 2 : numeric3(local.get("Height")) / 2
6508
+ ) === value)
6509
+ continue;
6510
+ unlocked(lock);
6511
+ editableCell(local.get(name));
6512
+ setCell(shape, name, value);
6513
+ add(name);
6514
+ }
6515
+ } else {
6516
+ const width = numeric3(local.get("Width")), height = numeric3(local.get("Height"));
6517
+ expected = {
6518
+ width: edit.width,
6519
+ height: edit.height,
6520
+ x: numeric3(local.get("PinX"), width / 2),
6521
+ y: numeric3(local.get("PinY"), height / 2)
6522
+ };
6523
+ if (width === edit.width && height === edit.height) return [];
6524
+ resizeGeometry(shape, roots, edit, check);
6525
+ if (numeric3(local.get("LockAspect"), 0) !== 0 && Math.abs(edit.width / width - edit.height / height) > 1e-9)
6526
+ fail("EDIT_PROTECTED_CELL", "LockAspect prevents changing the aspect ratio.");
6527
+ for (const [name, value] of [
6528
+ ["PinX", width / 2],
6529
+ ["PinY", height / 2]
6530
+ ])
6531
+ if (!local.has(name)) setCell(shape, name, value);
6532
+ for (const [name, value, lock] of [
6533
+ ["Width", edit.width, "LockWidth"],
6534
+ ["Height", edit.height, "LockHeight"]
6535
+ ]) {
6536
+ if (numeric3(local.get(name)) === value) continue;
6537
+ unlocked(lock);
6538
+ editableCell(local.get(name));
6539
+ setCell(shape, name, value);
6540
+ add(name);
6541
+ }
6542
+ for (const [name, dimension, value] of [
6543
+ ["LocPinX", "Width", edit.width / 2],
6544
+ ["LocPinY", "Height", edit.height / 2]
6545
+ ])
6546
+ if (!local.has(name)) setCell(shape, name, value, `${dimension}*0.5`);
6547
+ }
6548
+ }
6549
+ if (!changed.length) return [];
6550
+ const affectedPages = recalculateVisioCells(roots, changed, { check });
6551
+ const result = cells(admitted(root, edit.shapeId));
6552
+ if (expected && (numeric3(result.get("Width")) !== expected.width || numeric3(result.get("Height")) !== expected.height || numeric3(result.get("PinX"), expected.width / 2) !== expected.x || numeric3(result.get("PinY"), expected.height / 2) !== expected.y))
6553
+ fail(
6554
+ "EDIT_UNSUPPORTED_DEPENDENCY",
6555
+ "Dependent formulas would violate the requested geometry or fixed rotation pin."
6556
+ );
6557
+ return [.../* @__PURE__ */ new Set([edit.pageId, ...affectedPages])];
6558
+ }
6559
+
6560
+ // src/visio/edit-scope.ts
6561
+ var admitted2 = (node) => node.namespaceURI === VISIO_NS || node.namespaceURI === VISIO_LEGACY_NS;
6562
+ function cells2(sheet) {
6563
+ const result = /* @__PURE__ */ new Map();
6564
+ const add = (name, node) => {
6565
+ const key2 = name.toLowerCase();
6566
+ if (result.has(key2))
6567
+ fail("EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY", "Ambiguous inherited style cell.");
6568
+ result.set(key2, node);
6569
+ };
6570
+ for (const node of children(sheet, "Cell")) add(attribute2(node, "N") ?? "", node);
6571
+ for (const section of children(sheet, "Section")) {
6572
+ const name = attribute2(section, "N") ?? "";
6573
+ const prefix = name === "Geometry" ? `Geometry${Number(attribute2(section, "IX") ?? "0") + 1}` : name;
6574
+ for (const node of children(section, "Cell"))
6575
+ add(`${prefix}.${attribute2(node, "N") ?? ""}`, node);
6576
+ for (const row of children(section, "Row"))
6577
+ for (const node of children(row, "Cell")) {
6578
+ const n = attribute2(node, "N") ?? "", ix = attribute2(row, "IX") ?? "0", rowName = attribute2(row, "N") ?? ix;
6579
+ add(
6580
+ name === "Geometry" ? `${prefix}.${n}${ix}` : n === "Value" && ["User", "Prop"].includes(name) ? `${prefix}.${rowName}` : `${prefix}.${rowName}.${n}`,
6581
+ node
6582
+ );
6583
+ }
6584
+ }
6585
+ return result;
6586
+ }
6587
+ async function assertGeometryPackageScope(pkg, pagePaths, commands, check, roots) {
6588
+ const changed = commands.flatMap(
6589
+ (command2) => command2.type === "delete-shape" ? [] : (command2.type === "move-shape" ? ["PinX", "PinY"] : command2.type === "resize-shape" ? ["Width", "Height"] : ["PinX", "PinY", "Width", "Height"]).map((cell) => ({ pageId: command2.pageId, shapeId: command2.shapeId, cell }))
6590
+ );
6591
+ const deleting = commands.filter((command2) => command2.type === "delete-shape");
6592
+ let query;
6593
+ const hasMasterInstances = [...roots.values()].some(
6594
+ (root) => Array.from(root.getElementsByTagName("*")).some(
6595
+ (node) => admitted2(node) && node.localName === "Shape" && node.hasAttribute("Master")
6596
+ )
6597
+ );
6598
+ const dependencyCache = /* @__PURE__ */ new Map();
6599
+ const depends = (pageId, shapeId, cell) => {
6600
+ const key2 = JSON.stringify([pageId, shapeId, cell.toLowerCase()]);
6601
+ let result = dependencyCache.get(key2);
6602
+ if (result === void 0) {
6603
+ query ??= createVisioDependencyQuery(roots, { check });
6604
+ result = query({ pageId, shapeId, cell }, changed);
6605
+ dependencyCache.set(key2, result);
6606
+ }
6607
+ return result;
6608
+ };
6609
+ const affected = (shapeId, cell, pageId) => {
6610
+ const pages = pageId === void 0 ? [...roots.keys()] : [pageId];
6611
+ return pages.some(
6612
+ (page) => changed.some(
6613
+ (target) => target.pageId === page && target.shapeId === shapeId && target.cell.toLowerCase() === cell.toLowerCase()
6614
+ ) || deleting.some((target) => target.pageId === page && target.shapeId === shapeId) || depends(page, shapeId, cell)
6615
+ );
6616
+ };
6617
+ const inspectFormula = (node, path, pageId, localShapeId) => {
6618
+ const source = executableCellFormula(attribute2(node, "F"));
6619
+ if (!source) return;
6620
+ if (inertDoubleClickFormula(attribute2(node, "N") ?? "", source)) return;
6621
+ let analysis;
6622
+ try {
6623
+ analysis = analyzeVisioFormula(source);
6624
+ } catch (error) {
6625
+ fail(
6626
+ "EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY",
6627
+ `Cannot analyze formula in ${path}: ${error instanceof Error ? error.message : "unsupported formula"}`
6628
+ );
6629
+ }
6630
+ if (analysis.dynamic || analysis.references.some((ref) => {
6631
+ const shapeId = ref.shapeId ?? localShapeId;
6632
+ return shapeId !== void 0 && affected(shapeId, ref.cell, pageId);
6633
+ }))
6634
+ fail(
6635
+ "EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY",
6636
+ `Formula in ${path}, cell ${attribute2(node, "N") ?? node.localName} has affected or unknown dependencies outside editable page cells.`
6637
+ );
6638
+ };
6639
+ let documentRoot;
6640
+ for (const path of pkg.paths()) {
6641
+ if (!/^visio\/.*\.xml$/i.test(path) || pagePaths.has(path)) continue;
6642
+ if (/^visio\/masters\//i.test(path) && !hasMasterInstances) continue;
6643
+ const root = await pkg.readXml(path);
6644
+ const masterScope = /^visio\/masters\//i.test(path) || admitted2(root) && ["MasterContents", "Masters"].includes(root.localName);
6645
+ if (masterScope) {
6646
+ if (hasMasterInstances)
6647
+ for (const node of [root, ...Array.from(root.getElementsByTagName("*"))]) {
6648
+ check();
6649
+ if (!admitted2(node)) continue;
6650
+ const source = executableCellFormula(attribute2(node, "F"));
6651
+ if (!source || inertDoubleClickFormula(attribute2(node, "N") ?? "", source)) continue;
6652
+ try {
6653
+ if (analyzeVisioFormula(source).dynamic)
6654
+ fail(
6655
+ "EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY",
6656
+ "Used master formulas have unknown dynamic dependencies."
6657
+ );
6658
+ } catch (error) {
6659
+ fail(
6660
+ "EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY",
6661
+ `Cannot prove used master formula independence: ${error instanceof Error ? error.message : "invalid formula"}`
6662
+ );
6663
+ }
6664
+ }
6665
+ continue;
6666
+ }
6667
+ if (admitted2(root) && root.localName === "VisioDocument") {
6668
+ if (documentRoot)
6669
+ fail("EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY", "Multiple document scopes are ambiguous.");
6670
+ documentRoot = root;
6671
+ }
6672
+ const pending = [
6673
+ { node: root, style: false }
6674
+ ];
6675
+ while (pending.length) {
6676
+ check();
6677
+ const item = pending.pop(), node = item.node;
6678
+ const pageId = admitted2(node) && node.localName === "Page" && root.localName === "Pages" ? attribute2(node, "ID") : item.pageId;
6679
+ const style = item.style || admitted2(node) && node.localName === "StyleSheets";
6680
+ if (admitted2(node) && !style) {
6681
+ inspectFormula(node, path, pageId);
6682
+ if (deleting.some(
6683
+ (target) => (pageId === void 0 || target.pageId === pageId) && ["FromSheet", "ToSheet", "ShapeID", "SheetID"].some(
6684
+ (name) => attribute2(node, name) === target.shapeId
6685
+ )
6686
+ ))
6687
+ fail("EDIT_REFERENCED_DELETE", "Scoped metadata references the deleted shape.");
6688
+ }
6689
+ for (const child2 of Array.from(node.childNodes))
6690
+ if (child2.nodeType === 1)
6691
+ pending.push({
6692
+ node: child2,
6693
+ style,
6694
+ ...pageId === void 0 ? {} : { pageId }
6695
+ });
6696
+ }
6697
+ }
6698
+ if (!documentRoot) return;
6699
+ const styles = /* @__PURE__ */ new Map();
6700
+ for (const container of children(documentRoot, "StyleSheets"))
6701
+ for (const style of children(container, "StyleSheet")) {
6702
+ const id = attribute2(style, "ID");
6703
+ if (!id || styles.has(id))
6704
+ fail("EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY", "Style IDs must be unique.");
6705
+ styles.set(id, style);
6706
+ }
6707
+ const styleCache = /* @__PURE__ */ new Map();
6708
+ const documentSheet = children(documentRoot, "DocumentSheet")[0];
6709
+ const inherited = (id, category, seen = /* @__PURE__ */ new Set()) => {
6710
+ check();
6711
+ const cacheKey = `${category}:${id}`;
6712
+ const cached2 = styleCache.get(cacheKey);
6713
+ if (cached2) return cached2;
6714
+ if (seen.has(id) || seen.size >= 64)
6715
+ fail("EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY", "Style ancestry is cyclic or too deep.");
6716
+ const style = styles.get(id);
6717
+ if (!style)
6718
+ fail("EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY", "Selected style ancestry cannot be resolved.");
6719
+ seen.add(id);
6720
+ const parent = attribute2(style, category);
6721
+ const result = parent !== void 0 && parent !== id ? new Map(inherited(parent, category, seen)) : /* @__PURE__ */ new Map();
6722
+ for (const [name, node] of cells2(style))
6723
+ if (attribute2(node, "F") !== "Inh") result.set(name, node);
6724
+ styleCache.set(cacheKey, result);
6725
+ return result;
6726
+ };
6727
+ for (const [pageId, root] of roots) {
6728
+ const shapes = Array.from(root.getElementsByTagNameNS(root.namespaceURI, "Shape"));
6729
+ for (const command2 of commands)
6730
+ if (command2.type === "create-rectangle" && command2.pageId === pageId && !shapes.some((shape) => attribute2(shape, "ID") === command2.shapeId)) {
6731
+ const shape = root.ownerDocument.createElementNS(root.namespaceURI, "Shape");
6732
+ shape.setAttribute("ID", command2.shapeId);
6733
+ for (const name of ["PinX", "PinY", "Width", "Height", "LocPinX", "LocPinY", "Angle"]) {
6734
+ const node = root.ownerDocument.createElementNS(root.namespaceURI, "Cell");
6735
+ node.setAttribute("N", name);
6736
+ shape.appendChild(node);
6737
+ }
6738
+ shapes.push(shape);
6739
+ }
6740
+ for (const shape of shapes) {
6741
+ check();
6742
+ const shapeId = attribute2(shape, "ID");
6743
+ if (!shapeId) continue;
6744
+ if (deleting.some((command2) => command2.pageId === pageId && command2.shapeId === shapeId))
6745
+ continue;
6746
+ const local = cells2(shape);
6747
+ for (const category of ["FillStyle", "LineStyle", "TextStyle"]) {
6748
+ const styleId = attribute2(shape, category) ?? attribute2(documentSheet, category) ?? (styles.has("0") ? "0" : void 0);
6749
+ if (styleId === void 0) continue;
6750
+ for (const [name, node] of inherited(styleId, category)) {
6751
+ const override = local.get(name);
6752
+ if (override && attribute2(override, "F") !== "Inh") continue;
6753
+ if (depends(pageId, shapeId, name))
6754
+ fail(
6755
+ "EDIT_UNSUPPORTED_PACKAGE_DEPENDENCY",
6756
+ "Affected inherited style cells cannot use synthetic page defaults."
6757
+ );
6758
+ inspectFormula(node, `inherited ${category} ${styleId}`, pageId, shapeId);
6759
+ }
6760
+ }
6761
+ }
6762
+ }
6763
+ }
6764
+
5417
6765
  // src/visio/edit.ts
5418
6766
  function positive(value) {
5419
6767
  if (!Number.isSafeInteger(value) || value <= 0)
@@ -5430,35 +6778,50 @@ async function editVsdx(input, edits, options = {}) {
5430
6778
  const check = () => {
5431
6779
  if (Date.now() >= deadline) fail("LIMIT_RUNTIME", "Visio edit deadline exceeded.");
5432
6780
  };
5433
- if (!Array.isArray(edits) || edits.length > maxEdits)
5434
- fail("LIMIT_EDITS", "Edit command count exceeds limit.");
5435
- let textLength = 0;
5436
- const commands = Array.from(edits, (edit) => {
5437
- if (!edit || edit.type !== "replace-plain-text" || typeof edit.pageId !== "string" || !edit.pageId || edit.pageId.length > 256 || typeof edit.shapeId !== "string" || !edit.shapeId || edit.shapeId.length > 256 || typeof edit.text !== "string")
5438
- fail("INVALID_EDIT", "Invalid plain-text edit command.");
5439
- textLength += edit.text.length;
5440
- if (textLength > maxText) fail("LIMIT_EDIT_TEXT", "Replacement text exceeds aggregate limit.");
5441
- if (/[\u0000-\u0008\u000b\u000c\u000d\u000e-\u001f\ufffe\uffff]|[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/u.test(
5442
- edit.text
5443
- ))
5444
- fail("INVALID_EDIT_TEXT", "Replacement text contains invalid XML characters.");
5445
- return { pageId: edit.pageId, shapeId: edit.shapeId, text: edit.text };
5446
- });
6781
+ const commands = snapshotVisioEdits(edits, maxEdits, maxText);
5447
6782
  const source = input instanceof Uint8Array ? input : new Uint8Array(input);
5448
6783
  if (source.length > limits.maxInputBytes) fail("LIMIT_INPUT", "ZIP input exceeds limit.");
5449
6784
  const original = new Uint8Array(source);
5450
6785
  const { pkg, parts, pages } = await openEditablePackage(original, limits, check);
5451
6786
  const dirty = /* @__PURE__ */ new Map();
6787
+ const roots = /* @__PURE__ */ new Map();
6788
+ const geometry = commands.some((command2) => command2.type !== "replace-plain-text");
6789
+ let document;
6790
+ if (geometry) {
6791
+ for (const [pageId, path2] of pages) {
6792
+ const sourceRoot = await visioXml(pkg, path2, "PageContents");
6793
+ roots.set(pageId, sourceRoot.ownerDocument.cloneNode(true).documentElement);
6794
+ }
6795
+ await assertGeometryPackageScope(
6796
+ pkg,
6797
+ new Set(pages.values()),
6798
+ commands.filter((command2) => command2.type !== "replace-plain-text"),
6799
+ check,
6800
+ roots
6801
+ );
6802
+ const path = await related(pkg, "", "document");
6803
+ document = await visioXml(pkg, path, "VisioDocument");
6804
+ }
6805
+ let textChanged = false;
5452
6806
  for (const command2 of commands) {
5453
6807
  check();
5454
6808
  const path = pages.get(command2.pageId);
5455
6809
  if (!path) fail("EDIT_TARGET_NOT_FOUND", "Page does not exist.");
5456
- let root = dirty.get(path);
6810
+ let root = roots.get(command2.pageId) ?? dirty.get(path);
5457
6811
  if (!root) {
5458
6812
  const sourceRoot = await visioXml(pkg, path, "PageContents");
5459
6813
  root = sourceRoot.ownerDocument.cloneNode(true).documentElement;
6814
+ roots.set(command2.pageId, root);
6815
+ }
6816
+ if (command2.type === "replace-plain-text") {
6817
+ if (replacePlainText(root, command2.shapeId, command2.text, check)) {
6818
+ dirty.set(path, root);
6819
+ textChanged = true;
6820
+ }
6821
+ } else {
6822
+ for (const pageId of applyGeometryEdit(roots, document, command2, check))
6823
+ dirty.set(pages.get(pageId), roots.get(pageId));
5460
6824
  }
5461
- if (replacePlainText(root, command2.shapeId, command2.text, check)) dirty.set(path, root);
5462
6825
  }
5463
6826
  let totalBytes = [...parts.values()].reduce((sum, bytes2) => sum + bytes2.length, 0);
5464
6827
  let nodes = 0;
@@ -5489,13 +6852,18 @@ async function editVsdx(input, edits, options = {}) {
5489
6852
  return {
5490
6853
  bytes,
5491
6854
  changedParts: [...dirty.keys()],
5492
- diagnostics: dirty.size ? [
6855
+ diagnostics: dirty.size && textChanged ? [
5493
6856
  {
5494
6857
  code: "edit-caches-not-recalculated",
5495
6858
  message: "Text was replaced without recalculating formulas or dependent caches. Native Visio reopen and rendering compatibility are unverified."
5496
6859
  }
6860
+ ] : dirty.size ? [
6861
+ {
6862
+ code: "edit-geometry-experimental",
6863
+ message: "Supported affected geometry caches were recalculated. Native Visio reopen and rendering fidelity remain unverified."
6864
+ }
5497
6865
  ] : []
5498
6866
  };
5499
6867
  }
5500
6868
 
5501
- export { VISIO_EMF_ADMISSION_LIMITS, VISIO_FOREIGN_VECTOR_LIMITS, VISIO_RASTER_IMAGE_LIMITS, VISIO_VISIBILITY_LIMITS, VisioForeignVectorError, VisioImageError, VisioPackageError, convertVisioMetafile, editVsdx, getVisioPageLayers, inspectVisioEmfAdmission, inspectVisioRasterImage, parseVsdx, resolveVisioPageVisibility, sanitizeVisioForeignVectorTree, validateVisioForeignVector };
6869
+ export { VISIO_EMF_ADMISSION_LIMITS, VISIO_FOREIGN_VECTOR_LIMITS, VISIO_RASTER_IMAGE_LIMITS, VISIO_VISIBILITY_LIMITS, VisioForeignVectorError, VisioFormulaError, VisioImageError, VisioPackageError, analyzeVisioFormula, convertVisioMetafile, editVsdx, evaluateVisioFormula, getVisioPageLayers, inspectVisioEmfAdmission, inspectVisioRasterImage, parseVisioFormula, parseVsdx, resolveVisioPageVisibility, sanitizeVisioForeignVectorTree, validateVisioForeignVector, visioFormulaCachedValue };