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