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