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