@harbour-enterprises/superdoc 1.4.0-next.1 → 1.4.0-next.3

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.
@@ -19027,7 +19027,7 @@ function isMetafileExtension(extension) {
19027
19027
  const DRAWING_XML_TAG = "w:drawing";
19028
19028
  const SHAPE_URI = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
19029
19029
  const GROUP_URI = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup";
19030
- const normalizeTargetPath = (targetPath = "") => {
19030
+ const normalizeTargetPath$1 = (targetPath = "") => {
19031
19031
  if (!targetPath) return targetPath;
19032
19032
  const trimmed = targetPath.replace(/^\/+/, "");
19033
19033
  if (trimmed.startsWith("word/")) return trimmed;
@@ -19186,7 +19186,14 @@ function handleImageNode(node, params, isAnchor) {
19186
19186
  }
19187
19187
  const stretch = blipFill?.elements.find((el) => el.name === "a:stretch");
19188
19188
  const fillRect = stretch?.elements.find((el) => el.name === "a:fillRect");
19189
+ const srcRect = blipFill?.elements.find((el) => el.name === "a:srcRect");
19190
+ const srcRectAttrs = srcRect?.attributes || {};
19191
+ const srcRectHasNegativeValues = ["l", "t", "r", "b"].some((attr) => {
19192
+ const val = srcRectAttrs[attr];
19193
+ return val != null && parseFloat(val) < 0;
19194
+ });
19189
19195
  const shouldStretch = Boolean(stretch && fillRect);
19196
+ const shouldCover = shouldStretch && !srcRectHasNegativeValues;
19190
19197
  const spPr = picture.elements.find((el) => el.name === "pic:spPr");
19191
19198
  if (spPr) {
19192
19199
  const xfrm = spPr.elements.find((el) => el.name === "a:xfrm");
@@ -19215,7 +19222,7 @@ function handleImageNode(node, params, isAnchor) {
19215
19222
  }
19216
19223
  const { attributes: relAttributes } = rel;
19217
19224
  const targetPath = relAttributes["Target"];
19218
- const path = normalizeTargetPath(targetPath);
19225
+ const path = normalizeTargetPath$1(targetPath);
19219
19226
  const extension = path.substring(path.lastIndexOf(".") + 1);
19220
19227
  let finalSrc = path;
19221
19228
  let finalExtension = extension;
@@ -19263,7 +19270,7 @@ function handleImageNode(node, params, isAnchor) {
19263
19270
  wrapText: wrap2.attrs.wrapText
19264
19271
  } : {},
19265
19272
  wrapTopAndBottom: wrap2.type === "TopAndBottom",
19266
- shouldStretch,
19273
+ shouldCover,
19267
19274
  originalPadding: {
19268
19275
  distT: attributes["distT"],
19269
19276
  distB: attributes["distB"],
@@ -19456,7 +19463,7 @@ const handleShapeGroup = (params, node, graphicData, size, padding, marginOffset
19456
19463
  const { elements } = relationships || [];
19457
19464
  const rel = elements?.find((el) => el.attributes["Id"] === rEmbed);
19458
19465
  if (!rel) return null;
19459
- const targetPath = normalizeTargetPath(rel.attributes?.["Target"]);
19466
+ const targetPath = normalizeTargetPath$1(rel.attributes?.["Target"]);
19460
19467
  const path = targetPath;
19461
19468
  const nvPicPr = pic.elements?.find((el) => el.name === "pic:nvPicPr");
19462
19469
  const cNvPr = nvPicPr?.elements?.find((el) => el.name === "pic:cNvPr");
@@ -29560,6 +29567,10 @@ function filterOutRootInlineNodes(content = []) {
29560
29567
  if (!node || typeof node.type !== "string") return;
29561
29568
  const type = node.type;
29562
29569
  const preservableNodeName = PRESERVABLE_INLINE_XML_NAMES[type];
29570
+ if (type === "image" && node.attrs?.isAnchor) {
29571
+ result.push(node);
29572
+ return;
29573
+ }
29563
29574
  if (!INLINE_TYPES.has(type)) {
29564
29575
  result.push(node);
29565
29576
  } else if (preservableNodeName) {
@@ -29753,6 +29764,137 @@ function buildStyles(styleObject) {
29753
29764
  }
29754
29765
  return style;
29755
29766
  }
29767
+ function handleShapeImageImport({ params, pict }) {
29768
+ const shape = pict.elements?.find((el) => el.name === "v:shape");
29769
+ if (!shape) return null;
29770
+ const imagedata = shape.elements?.find((el) => el.name === "v:imagedata");
29771
+ if (!imagedata) return null;
29772
+ const { docx, filename } = params;
29773
+ const shapeAttrs = shape.attributes || {};
29774
+ const imagedataAttrs = imagedata.attributes || {};
29775
+ const rId = imagedataAttrs["r:id"];
29776
+ if (!rId) {
29777
+ console.warn("v:imagedata missing r:id attribute");
29778
+ return null;
29779
+ }
29780
+ const currentFile = filename || "document.xml";
29781
+ let rels = docx[`word/_rels/${currentFile}.rels`];
29782
+ if (!rels) rels = docx[`word/_rels/document.xml.rels`];
29783
+ const relationships = rels?.elements?.find((el) => el.name === "Relationships");
29784
+ const { elements } = relationships || [];
29785
+ const rel = elements?.find((el) => el.attributes["Id"] === rId);
29786
+ if (!rel) {
29787
+ console.warn(`Relationship not found for r:id="${rId}"`);
29788
+ return null;
29789
+ }
29790
+ const targetPath = rel.attributes["Target"];
29791
+ const normalizedPath = normalizeTargetPath(targetPath);
29792
+ const style = shapeAttrs.style || "";
29793
+ const styleObj = parseVmlStyle(style);
29794
+ const width = styleObj.width || "100px";
29795
+ const height = styleObj.height || "100px";
29796
+ const position = {
29797
+ type: styleObj.position || "absolute",
29798
+ marginLeft: styleObj["margin-left"] || "0",
29799
+ marginTop: styleObj["margin-top"] || "0"
29800
+ };
29801
+ const zIndex = styleObj["z-index"] ? parseInt(styleObj["z-index"], 10) : void 0;
29802
+ const hPosition = styleObj["mso-position-horizontal"] || "center";
29803
+ const vPosition = styleObj["mso-position-vertical"] || "center";
29804
+ const hRelativeTo = styleObj["mso-position-horizontal-relative"] || "margin";
29805
+ const vRelativeTo = styleObj["mso-position-vertical-relative"] || "margin";
29806
+ const gain = imagedataAttrs["gain"];
29807
+ const blacklevel = imagedataAttrs["blacklevel"];
29808
+ const title = imagedataAttrs["o:title"] || "Watermark";
29809
+ const imageNode = {
29810
+ type: "image",
29811
+ attrs: {
29812
+ src: normalizedPath,
29813
+ alt: title,
29814
+ extension: normalizedPath.substring(normalizedPath.lastIndexOf(".") + 1),
29815
+ title,
29816
+ rId,
29817
+ // Store VML-specific attributes for round-trip
29818
+ vmlWatermark: true,
29819
+ vmlStyle: style,
29820
+ vmlAttributes: shapeAttrs,
29821
+ vmlImagedata: imagedataAttrs,
29822
+ // Positioning
29823
+ isAnchor: true,
29824
+ inline: false,
29825
+ wrap: {
29826
+ type: "None",
29827
+ attrs: {
29828
+ behindDoc: Number.isFinite(zIndex) ? zIndex < 0 : true
29829
+ }
29830
+ },
29831
+ anchorData: {
29832
+ hRelativeFrom: hRelativeTo,
29833
+ vRelativeFrom: vRelativeTo,
29834
+ alignH: hPosition,
29835
+ alignV: vPosition
29836
+ },
29837
+ // Size
29838
+ size: {
29839
+ width: convertToPixels(width),
29840
+ height: convertToPixels(height)
29841
+ },
29842
+ marginOffset: {
29843
+ horizontal: convertToPixels(position.marginLeft),
29844
+ top: convertToPixels(position.marginTop)
29845
+ },
29846
+ // Image adjustments
29847
+ ...gain && { gain },
29848
+ ...blacklevel && { blacklevel }
29849
+ }
29850
+ };
29851
+ return imageNode;
29852
+ }
29853
+ function normalizeTargetPath(targetPath = "") {
29854
+ if (!targetPath) return targetPath;
29855
+ const trimmed = targetPath.replace(/^\/+/, "");
29856
+ if (trimmed.startsWith("word/")) return trimmed;
29857
+ if (trimmed.startsWith("media/")) return `word/${trimmed}`;
29858
+ return `word/${trimmed}`;
29859
+ }
29860
+ function parseVmlStyle(style) {
29861
+ const result = {};
29862
+ if (!style) return result;
29863
+ const declarations = style.split(";").filter((s) => s.trim());
29864
+ for (const decl of declarations) {
29865
+ const [prop, value] = decl.split(":").map((s) => s.trim());
29866
+ if (prop && value) {
29867
+ result[prop] = value;
29868
+ }
29869
+ }
29870
+ return result;
29871
+ }
29872
+ function convertToPixels(value) {
29873
+ if (typeof value === "number") return value;
29874
+ if (!value || typeof value !== "string") return 0;
29875
+ const match = value.match(/^([\d.]+)([a-z%]+)?$/i);
29876
+ if (!match) return 0;
29877
+ const num = parseFloat(match[1]);
29878
+ const unit = match[2] || "px";
29879
+ switch (unit.toLowerCase()) {
29880
+ case "px":
29881
+ return num;
29882
+ case "pt":
29883
+ return num * (96 / 72);
29884
+ // 1pt = 1/72 inch, 96 DPI
29885
+ case "in":
29886
+ return num * 96;
29887
+ case "cm":
29888
+ return num * (96 / 2.54);
29889
+ case "mm":
29890
+ return num * (96 / 25.4);
29891
+ case "pc":
29892
+ return num * 16;
29893
+ // 1pc = 12pt
29894
+ default:
29895
+ return num;
29896
+ }
29897
+ }
29756
29898
  function pictNodeTypeStrategy(node) {
29757
29899
  const shape = node.elements?.find((el) => el.name === "v:shape");
29758
29900
  const group = node.elements?.find((el) => el.name === "v:group");
@@ -29771,6 +29913,10 @@ function pictNodeTypeStrategy(node) {
29771
29913
  if (textbox) {
29772
29914
  return { type: "shapeContainer", handler: handleShapeTextboxImport };
29773
29915
  }
29916
+ const imagedata = shape.elements?.find((el) => el.name === "v:imagedata");
29917
+ if (imagedata) {
29918
+ return { type: "image", handler: handleShapeImageImport };
29919
+ }
29774
29920
  }
29775
29921
  return { type: "unknown", handler: null };
29776
29922
  }
@@ -29869,8 +30015,116 @@ function translateVRectContentBlock(params) {
29869
30015
  };
29870
30016
  return wrapTextInRun(pict);
29871
30017
  }
30018
+ function translateVmlWatermark(params) {
30019
+ const { node } = params;
30020
+ const { attrs } = node;
30021
+ if (attrs.vmlAttributes && attrs.vmlImagedata) {
30022
+ const shape2 = {
30023
+ name: "v:shape",
30024
+ attributes: attrs.vmlAttributes,
30025
+ elements: [
30026
+ {
30027
+ name: "v:imagedata",
30028
+ attributes: {
30029
+ ...attrs.vmlImagedata,
30030
+ "r:id": attrs.rId
30031
+ }
30032
+ }
30033
+ ]
30034
+ };
30035
+ const pict2 = {
30036
+ name: "w:pict",
30037
+ attributes: {
30038
+ "w14:anchorId": generateRandomSigned32BitIntStrId()
30039
+ },
30040
+ elements: [shape2]
30041
+ };
30042
+ const par2 = {
30043
+ name: "w:p",
30044
+ elements: [wrapTextInRun(pict2)]
30045
+ };
30046
+ return par2;
30047
+ }
30048
+ const style = buildVmlStyle(attrs);
30049
+ const shape = {
30050
+ name: "v:shape",
30051
+ attributes: {
30052
+ id: `WordPictureWatermark${generateRandomSigned32BitIntStrId().replace("-", "")}`,
30053
+ "o:spid": `_x0000_s${Math.floor(Math.random() * 1e4)}`,
30054
+ type: "#_x0000_t75",
30055
+ style,
30056
+ "o:allowincell": "f"
30057
+ },
30058
+ elements: [
30059
+ {
30060
+ name: "v:imagedata",
30061
+ attributes: {
30062
+ "r:id": attrs.rId,
30063
+ "o:title": attrs.title || attrs.alt || "Watermark",
30064
+ ...attrs.gain && { gain: attrs.gain },
30065
+ ...attrs.blacklevel && { blacklevel: attrs.blacklevel }
30066
+ }
30067
+ }
30068
+ ]
30069
+ };
30070
+ const pict = {
30071
+ name: "w:pict",
30072
+ attributes: {
30073
+ "w14:anchorId": generateRandomSigned32BitIntStrId()
30074
+ },
30075
+ elements: [shape]
30076
+ };
30077
+ const par = {
30078
+ name: "w:p",
30079
+ elements: [wrapTextInRun(pict)]
30080
+ };
30081
+ return par;
30082
+ }
30083
+ function buildVmlStyle(attrs) {
30084
+ const styles = [];
30085
+ styles.push("position:absolute");
30086
+ if (attrs.size) {
30087
+ if (attrs.size.width) {
30088
+ styles.push(`width:${convertToPt(attrs.size.width)}pt`);
30089
+ }
30090
+ if (attrs.size.height) {
30091
+ styles.push(`height:${convertToPt(attrs.size.height)}pt`);
30092
+ }
30093
+ }
30094
+ if (attrs.marginOffset) {
30095
+ if (attrs.marginOffset.horizontal !== void 0) {
30096
+ styles.push(`margin-left:${convertToPt(attrs.marginOffset.horizontal)}pt`);
30097
+ }
30098
+ if (attrs.marginOffset.top !== void 0) {
30099
+ styles.push(`margin-top:${convertToPt(attrs.marginOffset.top)}pt`);
30100
+ }
30101
+ }
30102
+ if (attrs.wrap?.attrs?.behindDoc) {
30103
+ styles.push("z-index:-251653120");
30104
+ }
30105
+ if (attrs.anchorData) {
30106
+ if (attrs.anchorData.alignH) {
30107
+ styles.push(`mso-position-horizontal:${attrs.anchorData.alignH}`);
30108
+ }
30109
+ if (attrs.anchorData.alignV) {
30110
+ styles.push(`mso-position-vertical:${attrs.anchorData.alignV}`);
30111
+ }
30112
+ if (attrs.anchorData.hRelativeFrom) {
30113
+ styles.push(`mso-position-horizontal-relative:${attrs.anchorData.hRelativeFrom}`);
30114
+ }
30115
+ if (attrs.anchorData.vRelativeFrom) {
30116
+ styles.push(`mso-position-vertical-relative:${attrs.anchorData.vRelativeFrom}`);
30117
+ }
30118
+ }
30119
+ styles.push("mso-width-percent:0");
30120
+ styles.push("mso-height-percent:0");
30121
+ return styles.join(";");
30122
+ }
30123
+ function convertToPt(pixels) {
30124
+ return pixels * 72 / 96;
30125
+ }
29872
30126
  const XML_NODE_NAME = "w:pict";
29873
- const SD_NODE_NAME = ["shapeContainer", "contentBlock"];
30127
+ const SD_NODE_NAME = ["shapeContainer", "contentBlock", "image"];
29874
30128
  const validXmlAttributes = [];
29875
30129
  function encode(params) {
29876
30130
  const { node, pNode } = params.extraParams;
@@ -29894,6 +30148,12 @@ function decode(params) {
29894
30148
  shapeContainer: () => translateShapeContainer(params),
29895
30149
  shapeTextbox: () => translateShapeTextbox(params),
29896
30150
  contentBlock: () => translateContentBlock(params),
30151
+ image: () => {
30152
+ if (node.attrs?.vmlWatermark) {
30153
+ return translateVmlWatermark(params);
30154
+ }
30155
+ return null;
30156
+ },
29897
30157
  default: () => null
29898
30158
  };
29899
30159
  const decoder = types[node.type] ?? types.default;
@@ -31009,7 +31269,7 @@ class SuperConverter {
31009
31269
  static getStoredSuperdocVersion(docx) {
31010
31270
  return SuperConverter.getStoredCustomProperty(docx, "SuperdocVersion");
31011
31271
  }
31012
- static setStoredSuperdocVersion(docx = this.convertedXml, version = "1.4.0-next.1") {
31272
+ static setStoredSuperdocVersion(docx = this.convertedXml, version = "1.4.0-next.3") {
31013
31273
  return SuperConverter.setStoredCustomProperty(docx, "SuperdocVersion", version, false);
31014
31274
  }
31015
31275
  /**