@cj-tech-master/excelts 9.0.0 → 9.1.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/browser/index.browser.d.ts +2 -0
- package/dist/browser/index.browser.js +2 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/modules/excel/image.d.ts +27 -2
- package/dist/browser/modules/excel/image.js +23 -1
- package/dist/browser/modules/excel/stream/worksheet-writer.d.ts +16 -1
- package/dist/browser/modules/excel/stream/worksheet-writer.js +68 -0
- package/dist/browser/modules/excel/types.d.ts +72 -0
- package/dist/browser/modules/excel/utils/drawing-utils.d.ts +4 -0
- package/dist/browser/modules/excel/utils/drawing-utils.js +5 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +4 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.js +15 -0
- package/dist/browser/modules/excel/utils/watermark-image.d.ts +67 -0
- package/dist/browser/modules/excel/utils/watermark-image.js +383 -0
- package/dist/browser/modules/excel/worksheet.d.ts +39 -1
- package/dist/browser/modules/excel/worksheet.js +99 -0
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +3 -2
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +6 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-fill-xform.d.ts +2 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-fill-xform.js +0 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +3 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.js +22 -6
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +3 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.js +5 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +19 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +103 -4
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +135 -8
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +53 -1
- package/dist/browser/modules/pdf/core/pdf-writer.d.ts +1 -1
- package/dist/browser/modules/pdf/core/pdf-writer.js +2 -1
- package/dist/browser/modules/pdf/index.d.ts +1 -1
- package/dist/browser/modules/pdf/render/page-renderer.d.ts +29 -1
- package/dist/browser/modules/pdf/render/page-renderer.js +394 -25
- package/dist/browser/modules/pdf/render/pdf-exporter.js +84 -47
- package/dist/browser/modules/pdf/types.d.ts +235 -0
- package/dist/cjs/index.js +5 -2
- package/dist/cjs/modules/excel/image.js +23 -1
- package/dist/cjs/modules/excel/stream/worksheet-writer.js +68 -0
- package/dist/cjs/modules/excel/utils/drawing-utils.js +5 -0
- package/dist/cjs/modules/excel/utils/ooxml-paths.js +19 -0
- package/dist/cjs/modules/excel/utils/watermark-image.js +386 -0
- package/dist/cjs/modules/excel/worksheet.js +99 -0
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +3 -2
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +6 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/blip-fill-xform.js +0 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/blip-xform.js +22 -6
- package/dist/cjs/modules/excel/xlsx/xform/drawing/pic-xform.js +5 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +103 -4
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +134 -7
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +52 -0
- package/dist/cjs/modules/pdf/core/pdf-writer.js +2 -1
- package/dist/cjs/modules/pdf/render/page-renderer.js +396 -25
- package/dist/cjs/modules/pdf/render/pdf-exporter.js +83 -46
- package/dist/esm/index.browser.js +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/modules/excel/image.js +23 -1
- package/dist/esm/modules/excel/stream/worksheet-writer.js +68 -0
- package/dist/esm/modules/excel/utils/drawing-utils.js +5 -0
- package/dist/esm/modules/excel/utils/ooxml-paths.js +15 -0
- package/dist/esm/modules/excel/utils/watermark-image.js +383 -0
- package/dist/esm/modules/excel/worksheet.js +99 -0
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +3 -2
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +6 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/blip-fill-xform.js +0 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/blip-xform.js +22 -6
- package/dist/esm/modules/excel/xlsx/xform/drawing/pic-xform.js +5 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +103 -4
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +135 -8
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +53 -1
- package/dist/esm/modules/pdf/core/pdf-writer.js +2 -1
- package/dist/esm/modules/pdf/render/page-renderer.js +394 -25
- package/dist/esm/modules/pdf/render/pdf-exporter.js +84 -47
- package/dist/iife/excelts.iife.js +2390 -469
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +47 -47
- package/dist/types/index.browser.d.ts +2 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/modules/excel/image.d.ts +27 -2
- package/dist/types/modules/excel/stream/worksheet-writer.d.ts +16 -1
- package/dist/types/modules/excel/types.d.ts +72 -0
- package/dist/types/modules/excel/utils/drawing-utils.d.ts +4 -0
- package/dist/types/modules/excel/utils/ooxml-paths.d.ts +4 -0
- package/dist/types/modules/excel/utils/watermark-image.d.ts +67 -0
- package/dist/types/modules/excel/worksheet.d.ts +39 -1
- package/dist/types/modules/excel/xlsx/xform/drawing/blip-fill-xform.d.ts +2 -1
- package/dist/types/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +3 -1
- package/dist/types/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +3 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +19 -0
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +1 -0
- package/dist/types/modules/pdf/core/pdf-writer.d.ts +1 -1
- package/dist/types/modules/pdf/index.d.ts +1 -1
- package/dist/types/modules/pdf/render/page-renderer.d.ts +29 -1
- package/dist/types/modules/pdf/types.d.ts +235 -0
- package/package.json +1 -1
|
@@ -8,6 +8,8 @@ const form_control_1 = require("../../../form-control.js");
|
|
|
8
8
|
class VmlDrawingXform extends base_xform_1.BaseXform {
|
|
9
9
|
constructor() {
|
|
10
10
|
super();
|
|
11
|
+
// Internal state for parsing header image shapes
|
|
12
|
+
this._parsingHeaderImage = false;
|
|
11
13
|
this.map = {
|
|
12
14
|
"v:shape": new vml_shape_xform_1.VmlShapeXform()
|
|
13
15
|
};
|
|
@@ -23,8 +25,10 @@ class VmlDrawingXform extends base_xform_1.BaseXform {
|
|
|
23
25
|
const renderModel = (model || this.model);
|
|
24
26
|
const comments = renderModel.comments;
|
|
25
27
|
const formControls = renderModel.formControls;
|
|
28
|
+
const headerImage = renderModel.headerImage;
|
|
26
29
|
const hasComments = comments && comments.length > 0;
|
|
27
30
|
const hasFormControls = formControls && formControls.length > 0;
|
|
31
|
+
const hasHeaderImage = !!headerImage;
|
|
28
32
|
xmlStream.openXml(writer_1.StdDocAttributes);
|
|
29
33
|
xmlStream.openNode(this.tag, VmlDrawingXform.DRAWING_ATTRIBUTES);
|
|
30
34
|
// Shape layout - shared by both notes and form controls
|
|
@@ -62,6 +66,40 @@ class VmlDrawingXform extends base_xform_1.BaseXform {
|
|
|
62
66
|
xmlStream.leafNode("o:lock", { "v:ext": "edit", shapetype: "t" });
|
|
63
67
|
xmlStream.closeNode();
|
|
64
68
|
}
|
|
69
|
+
// Shapetype 75 for header/footer image (watermark)
|
|
70
|
+
if (hasHeaderImage) {
|
|
71
|
+
xmlStream.openNode("v:shapetype", {
|
|
72
|
+
id: "_x0000_t75",
|
|
73
|
+
coordsize: "21600,21600",
|
|
74
|
+
"o:spt": "75",
|
|
75
|
+
"o:preferrelative": "t",
|
|
76
|
+
path: "m@4@5l@4@11@9@11@9@5xe",
|
|
77
|
+
filled: "f",
|
|
78
|
+
stroked: "f"
|
|
79
|
+
});
|
|
80
|
+
xmlStream.leafNode("v:stroke", { joinstyle: "miter" });
|
|
81
|
+
xmlStream.openNode("v:formulas");
|
|
82
|
+
xmlStream.leafNode("v:f", { eqn: "if lineDrawn pixelLineWidth 0" });
|
|
83
|
+
xmlStream.leafNode("v:f", { eqn: "sum @0 1 0" });
|
|
84
|
+
xmlStream.leafNode("v:f", { eqn: "sum 0 0 @1" });
|
|
85
|
+
xmlStream.leafNode("v:f", { eqn: "prod @2 1 2" });
|
|
86
|
+
xmlStream.leafNode("v:f", { eqn: "prod @3 21600 pixelWidth" });
|
|
87
|
+
xmlStream.leafNode("v:f", { eqn: "prod @3 21600 pixelHeight" });
|
|
88
|
+
xmlStream.leafNode("v:f", { eqn: "sum @0 0 1" });
|
|
89
|
+
xmlStream.leafNode("v:f", { eqn: "prod @6 1 2" });
|
|
90
|
+
xmlStream.leafNode("v:f", { eqn: "prod @7 21600 pixelWidth" });
|
|
91
|
+
xmlStream.leafNode("v:f", { eqn: "sum @8 21600 0" });
|
|
92
|
+
xmlStream.leafNode("v:f", { eqn: "prod @7 21600 pixelHeight" });
|
|
93
|
+
xmlStream.leafNode("v:f", { eqn: "sum @10 21600 0" });
|
|
94
|
+
xmlStream.closeNode(); // v:formulas
|
|
95
|
+
xmlStream.leafNode("v:path", {
|
|
96
|
+
"o:extrusionok": "f",
|
|
97
|
+
gradientshapeok: "t",
|
|
98
|
+
"o:connecttype": "rect"
|
|
99
|
+
});
|
|
100
|
+
xmlStream.leafNode("o:lock", { "v:ext": "edit", aspectratio: "t" });
|
|
101
|
+
xmlStream.closeNode(); // v:shapetype
|
|
102
|
+
}
|
|
65
103
|
// Render comment shapes
|
|
66
104
|
if (hasComments) {
|
|
67
105
|
for (let i = 0; i < comments.length; i++) {
|
|
@@ -74,8 +112,32 @@ class VmlDrawingXform extends base_xform_1.BaseXform {
|
|
|
74
112
|
this._renderCheckboxShape(xmlStream, control);
|
|
75
113
|
}
|
|
76
114
|
}
|
|
115
|
+
// Render header/footer image shape
|
|
116
|
+
if (hasHeaderImage) {
|
|
117
|
+
this._renderHeaderImageShape(xmlStream, headerImage);
|
|
118
|
+
}
|
|
77
119
|
xmlStream.closeNode();
|
|
78
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Render a header/footer image shape for watermark
|
|
123
|
+
*/
|
|
124
|
+
_renderHeaderImageShape(xmlStream, headerImage) {
|
|
125
|
+
const width = headerImage.width ?? 467.25;
|
|
126
|
+
const height = headerImage.height ?? 311.25;
|
|
127
|
+
// CH = Center Header, used by Excel for center-positioned header images
|
|
128
|
+
xmlStream.openNode("v:shape", {
|
|
129
|
+
id: "CH",
|
|
130
|
+
"o:spid": "_x0000_s2049",
|
|
131
|
+
type: "#_x0000_t75",
|
|
132
|
+
style: `position:absolute;margin-left:0;margin-top:0;width:${width}pt;height:${height}pt;z-index:1`
|
|
133
|
+
});
|
|
134
|
+
xmlStream.leafNode("v:imagedata", {
|
|
135
|
+
"o:relid": headerImage.imageRelId,
|
|
136
|
+
"o:title": "watermark"
|
|
137
|
+
});
|
|
138
|
+
xmlStream.leafNode("o:lock", { "v:ext": "edit", rotation: "t" });
|
|
139
|
+
xmlStream.closeNode(); // v:shape
|
|
140
|
+
}
|
|
79
141
|
/**
|
|
80
142
|
* Render a checkbox form control shape
|
|
81
143
|
*/
|
|
@@ -144,7 +206,7 @@ class VmlDrawingXform extends base_xform_1.BaseXform {
|
|
|
144
206
|
xmlStream.closeNode(); // x:ClientData
|
|
145
207
|
xmlStream.closeNode(); // v:shape
|
|
146
208
|
}
|
|
147
|
-
// Parsing - delegate to VmlShapeXform for notes
|
|
209
|
+
// Parsing - delegate to VmlShapeXform for notes, handle header images directly
|
|
148
210
|
parseOpen(node) {
|
|
149
211
|
if (this.parser) {
|
|
150
212
|
this.parser.parseOpen(node);
|
|
@@ -158,10 +220,34 @@ class VmlDrawingXform extends base_xform_1.BaseXform {
|
|
|
158
220
|
formControls: []
|
|
159
221
|
};
|
|
160
222
|
break;
|
|
223
|
+
case "v:shape":
|
|
224
|
+
// Check if this is a header image shape (type="#_x0000_t75")
|
|
225
|
+
if (node.attributes.type === "#_x0000_t75") {
|
|
226
|
+
this._parsingHeaderImage = true;
|
|
227
|
+
// Extract width/height from style
|
|
228
|
+
const style = node.attributes.style || "";
|
|
229
|
+
const widthMatch = /width:([0-9.]+)pt/.exec(style);
|
|
230
|
+
const heightMatch = /height:([0-9.]+)pt/.exec(style);
|
|
231
|
+
this._headerImageWidth = widthMatch ? parseFloat(widthMatch[1]) : undefined;
|
|
232
|
+
this._headerImageHeight = heightMatch ? parseFloat(heightMatch[1]) : undefined;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
// Regular shape — delegate to VmlShapeXform (comments)
|
|
236
|
+
this.parser = this.map[node.name];
|
|
237
|
+
if (this.parser) {
|
|
238
|
+
this.parser.parseOpen(node);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
161
242
|
default:
|
|
162
|
-
this.
|
|
163
|
-
|
|
164
|
-
|
|
243
|
+
if (this._parsingHeaderImage && node.name === "v:imagedata") {
|
|
244
|
+
this._headerImageRelId = node.attributes["o:relid"];
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
this.parser = this.map[node.name];
|
|
248
|
+
if (this.parser) {
|
|
249
|
+
this.parser.parseOpen(node);
|
|
250
|
+
}
|
|
165
251
|
}
|
|
166
252
|
break;
|
|
167
253
|
}
|
|
@@ -181,6 +267,19 @@ class VmlDrawingXform extends base_xform_1.BaseXform {
|
|
|
181
267
|
return true;
|
|
182
268
|
}
|
|
183
269
|
switch (name) {
|
|
270
|
+
case "v:shape":
|
|
271
|
+
if (this._parsingHeaderImage && this._headerImageRelId) {
|
|
272
|
+
this.model.headerImage = {
|
|
273
|
+
imageRelId: this._headerImageRelId,
|
|
274
|
+
width: this._headerImageWidth,
|
|
275
|
+
height: this._headerImageHeight
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
this._parsingHeaderImage = false;
|
|
279
|
+
this._headerImageRelId = undefined;
|
|
280
|
+
this._headerImageWidth = undefined;
|
|
281
|
+
this._headerImageHeight = undefined;
|
|
282
|
+
return true;
|
|
184
283
|
case this.tag:
|
|
185
284
|
return false;
|
|
186
285
|
default:
|
|
@@ -235,6 +235,8 @@ class WorkSheetXform extends base_xform_1.BaseXform {
|
|
|
235
235
|
// Process background and image media entries
|
|
236
236
|
const backgroundMedia = [];
|
|
237
237
|
const imageMedia = [];
|
|
238
|
+
const watermarkMedia = [];
|
|
239
|
+
const headerImageMedia = [];
|
|
238
240
|
model.media.forEach(medium => {
|
|
239
241
|
if (medium.type === "background") {
|
|
240
242
|
backgroundMedia.push(medium);
|
|
@@ -242,6 +244,12 @@ class WorkSheetXform extends base_xform_1.BaseXform {
|
|
|
242
244
|
else if (medium.type === "image") {
|
|
243
245
|
imageMedia.push(medium);
|
|
244
246
|
}
|
|
247
|
+
else if (medium.type === "watermark") {
|
|
248
|
+
watermarkMedia.push(medium);
|
|
249
|
+
}
|
|
250
|
+
else if (medium.type === "headerImage") {
|
|
251
|
+
headerImageMedia.push(medium);
|
|
252
|
+
}
|
|
245
253
|
});
|
|
246
254
|
// Handle background images
|
|
247
255
|
backgroundMedia.forEach(medium => {
|
|
@@ -279,6 +287,107 @@ class WorkSheetXform extends base_xform_1.BaseXform {
|
|
|
279
287
|
drawing.anchors.push(...result.anchors);
|
|
280
288
|
drawing.rels = result.rels;
|
|
281
289
|
}
|
|
290
|
+
// Handle watermark overlay images — placed as a full-sheet drawing with transparency
|
|
291
|
+
if (watermarkMedia.length > 0) {
|
|
292
|
+
let { drawing } = model;
|
|
293
|
+
if (!drawing) {
|
|
294
|
+
drawing = model.drawing = {
|
|
295
|
+
rId: nextRid(rels),
|
|
296
|
+
name: `drawing${++options.drawingsCount}`,
|
|
297
|
+
anchors: [],
|
|
298
|
+
rels: []
|
|
299
|
+
};
|
|
300
|
+
options.drawings.push(drawing);
|
|
301
|
+
rels.push({
|
|
302
|
+
Id: drawing.rId,
|
|
303
|
+
Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
|
|
304
|
+
Target: (0, ooxml_paths_1.drawingRelTargetFromWorksheet)(drawing.name)
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
for (const medium of watermarkMedia) {
|
|
308
|
+
const bookImage = options.media[medium.imageId];
|
|
309
|
+
if (!bookImage) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const rIdImage = nextRid(drawing.rels);
|
|
313
|
+
drawing.rels.push({
|
|
314
|
+
Id: rIdImage,
|
|
315
|
+
Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
|
316
|
+
Target: (0, drawing_utils_1.resolveMediaTarget)(bookImage)
|
|
317
|
+
});
|
|
318
|
+
// Convert opacity (0-1) to OOXML percentage (0-100000), clamped
|
|
319
|
+
const rawOpacity = medium.opacity !== undefined ? medium.opacity : 0.15;
|
|
320
|
+
const clampedOpacity = Math.max(0, Math.min(1, rawOpacity));
|
|
321
|
+
const alphaModFix = Math.round(clampedOpacity * 100000);
|
|
322
|
+
// Compute coverage based on actual worksheet dimensions.
|
|
323
|
+
// Use the model's dimensions if available, otherwise use generous defaults.
|
|
324
|
+
const dims = model.dimensions;
|
|
325
|
+
const maxCol = dims ? Math.max(dims.model?.right ?? 100, 100) : 100;
|
|
326
|
+
const maxRow = dims ? Math.max(dims.model?.bottom ?? 200, 200) : 200;
|
|
327
|
+
drawing.anchors.push({
|
|
328
|
+
picture: {
|
|
329
|
+
rId: rIdImage,
|
|
330
|
+
alphaModFix
|
|
331
|
+
},
|
|
332
|
+
// Cover the full data area with extra margin
|
|
333
|
+
range: {
|
|
334
|
+
editAs: "absolute",
|
|
335
|
+
tl: { nativeCol: 0, nativeColOff: 0, nativeRow: 0, nativeRowOff: 0 },
|
|
336
|
+
br: { nativeCol: maxCol, nativeColOff: 0, nativeRow: maxRow, nativeRowOff: 0 }
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Handle header watermark images — VML header/footer image
|
|
342
|
+
if (headerImageMedia.length > 0) {
|
|
343
|
+
const medium = headerImageMedia[0]; // Only one header image per sheet
|
|
344
|
+
const bookImage = options.media[medium.imageId];
|
|
345
|
+
if (bookImage) {
|
|
346
|
+
const rIdVml = nextRid(rels);
|
|
347
|
+
rels.push({
|
|
348
|
+
Id: rIdVml,
|
|
349
|
+
Type: rel_type_1.RelType.VmlDrawing,
|
|
350
|
+
Target: (0, ooxml_paths_1.vmlDrawingHFRelTargetFromWorksheet)(fileIndex)
|
|
351
|
+
});
|
|
352
|
+
// Store header image info on the model for the VML writer and worksheet render
|
|
353
|
+
model.headerImage = {
|
|
354
|
+
vmlRelId: rIdVml,
|
|
355
|
+
imageId: medium.imageId,
|
|
356
|
+
bookImage,
|
|
357
|
+
headerWidth: medium.headerWidth,
|
|
358
|
+
headerHeight: medium.headerHeight
|
|
359
|
+
};
|
|
360
|
+
// Flag for content-types registration
|
|
361
|
+
options.hasHeaderWatermark = true;
|
|
362
|
+
// Update headerFooter to include &G placeholder.
|
|
363
|
+
// Respects the applyTo option: "all" (default), "odd", "even", "first".
|
|
364
|
+
if (!model.headerFooter) {
|
|
365
|
+
model.headerFooter = {};
|
|
366
|
+
}
|
|
367
|
+
const applyTo = medium.applyTo || "all";
|
|
368
|
+
const insertG = (field) => {
|
|
369
|
+
const existing = model.headerFooter[field] || "";
|
|
370
|
+
if (existing.includes("&G")) {
|
|
371
|
+
return existing;
|
|
372
|
+
}
|
|
373
|
+
if (existing.includes("&C")) {
|
|
374
|
+
return existing.replace("&C", "&C&G");
|
|
375
|
+
}
|
|
376
|
+
return existing + "&C&G";
|
|
377
|
+
};
|
|
378
|
+
if (applyTo === "all" || applyTo === "odd") {
|
|
379
|
+
model.headerFooter.oddHeader = insertG("oddHeader");
|
|
380
|
+
}
|
|
381
|
+
if (applyTo === "all" || applyTo === "even") {
|
|
382
|
+
model.headerFooter.evenHeader = insertG("evenHeader");
|
|
383
|
+
model.headerFooter.differentOddEven = true;
|
|
384
|
+
}
|
|
385
|
+
if (applyTo === "all" || applyTo === "first") {
|
|
386
|
+
model.headerFooter.firstHeader = insertG("firstHeader");
|
|
387
|
+
model.headerFooter.differentFirst = true;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
282
391
|
// prepare tables
|
|
283
392
|
model.tables.forEach(table => {
|
|
284
393
|
// relationships
|
|
@@ -439,10 +548,18 @@ class WorkSheetXform extends base_xform_1.BaseXform {
|
|
|
439
548
|
// NOTE: Excel is picky about worksheet child element order; legacyDrawing must come before controls.
|
|
440
549
|
model.rels.forEach(rel => {
|
|
441
550
|
if (rel.Type === rel_type_1.RelType.VmlDrawing) {
|
|
551
|
+
// Skip VML rels that are for header images (they use legacyDrawingHF instead)
|
|
552
|
+
if (model.headerImage && rel.Id === model.headerImage.vmlRelId) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
442
555
|
xmlStream.leafNode("legacyDrawing", { "r:id": rel.Id });
|
|
443
556
|
}
|
|
444
557
|
});
|
|
445
558
|
}
|
|
559
|
+
// legacyDrawingHF — VML drawing for header/footer images (watermark in header mode)
|
|
560
|
+
if (model.headerImage) {
|
|
561
|
+
xmlStream.leafNode("legacyDrawingHF", { "r:id": model.headerImage.vmlRelId });
|
|
562
|
+
}
|
|
446
563
|
// Controls section for legacy form controls (checkboxes, etc.)
|
|
447
564
|
// Excel expects <controls> entries that reference ctrlProp relationships.
|
|
448
565
|
if (model.formControls && model.formControls.length > 0) {
|
|
@@ -658,13 +775,23 @@ class WorkSheetXform extends base_xform_1.BaseXform {
|
|
|
658
775
|
// Also extract images to model.media for backward compatibility
|
|
659
776
|
drawing.anchors.forEach(anchor => {
|
|
660
777
|
if (anchor.medium) {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
778
|
+
// Detect overlay watermarks: drawings that carry alphaModFix
|
|
779
|
+
const hasAlpha = anchor.medium.alphaModFix !== undefined && anchor.medium.alphaModFix < 100000;
|
|
780
|
+
if (hasAlpha) {
|
|
781
|
+
model.media.push({
|
|
782
|
+
type: "watermark",
|
|
783
|
+
imageId: anchor.medium.index,
|
|
784
|
+
opacity: anchor.medium.alphaModFix / 100000
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
model.media.push({
|
|
789
|
+
type: "image",
|
|
790
|
+
imageId: anchor.medium.index,
|
|
791
|
+
range: anchor.range,
|
|
792
|
+
hyperlinks: anchor.picture.hyperlinks
|
|
793
|
+
});
|
|
794
|
+
}
|
|
668
795
|
}
|
|
669
796
|
});
|
|
670
797
|
}
|
|
@@ -909,6 +909,17 @@ class XLSX {
|
|
|
909
909
|
const vmlDrawing = await xform.parseStream(entry);
|
|
910
910
|
model.vmlDrawings[(0, ooxml_paths_1.vmlDrawingRelTargetFromWorksheetName)(name)] = vmlDrawing;
|
|
911
911
|
}
|
|
912
|
+
async _processVmlDrawingHFEntry(entry, model, _name) {
|
|
913
|
+
const xform = new vml_drawing_xform_1.VmlDrawingXform();
|
|
914
|
+
const vmlDrawing = await xform.parseStream(entry);
|
|
915
|
+
// Store parsed header image info for reconciliation
|
|
916
|
+
if (vmlDrawing && vmlDrawing.headerImage) {
|
|
917
|
+
if (!model.vmlDrawingHF) {
|
|
918
|
+
model.vmlDrawingHF = {};
|
|
919
|
+
}
|
|
920
|
+
model.vmlDrawingHF[_name] = vmlDrawing.headerImage;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
912
923
|
async _processThemeEntry(stream, model, name) {
|
|
913
924
|
await new Promise((resolve, reject) => {
|
|
914
925
|
const streamBuf = this.createStreamBuf();
|
|
@@ -1023,6 +1034,13 @@ class XLSX {
|
|
|
1023
1034
|
await this._processVmlDrawingEntry(stream, model, vmlDrawingName);
|
|
1024
1035
|
return true;
|
|
1025
1036
|
}
|
|
1037
|
+
// VML header/footer drawings (watermark in header mode).
|
|
1038
|
+
// Parse to extract header image info for round-trip preservation.
|
|
1039
|
+
const vmlHFName = (0, ooxml_paths_1.getVmlDrawingHFNameFromPath)(entryName);
|
|
1040
|
+
if (vmlHFName) {
|
|
1041
|
+
await this._processVmlDrawingHFEntry(stream, model, vmlHFName);
|
|
1042
|
+
return true;
|
|
1043
|
+
}
|
|
1026
1044
|
const commentsIndex = (0, ooxml_paths_1.getCommentsIndexFromPath)(entryName);
|
|
1027
1045
|
if (commentsIndex) {
|
|
1028
1046
|
await this._processCommentEntry(stream, model, `comments${commentsIndex}`);
|
|
@@ -1229,6 +1247,35 @@ class XLSX {
|
|
|
1229
1247
|
formControls: hasFormControls ? worksheet.formControls : []
|
|
1230
1248
|
});
|
|
1231
1249
|
}
|
|
1250
|
+
// Generate VML drawing for header/footer images (watermark in header mode)
|
|
1251
|
+
if (worksheet.headerImage) {
|
|
1252
|
+
const hdrImage = worksheet.headerImage;
|
|
1253
|
+
const bookImage = hdrImage.bookImage;
|
|
1254
|
+
const imageFileName = bookImage.name &&
|
|
1255
|
+
bookImage.extension &&
|
|
1256
|
+
bookImage.name.endsWith(`.${bookImage.extension}`)
|
|
1257
|
+
? bookImage.name
|
|
1258
|
+
: `${bookImage.name}.${bookImage.extension}`;
|
|
1259
|
+
const imageRelTarget = `../media/${imageFileName}`;
|
|
1260
|
+
// Write the VML file for the header image
|
|
1261
|
+
await this._renderToZip(zip, (0, ooxml_paths_1.vmlDrawingHFPath)(fileIndex), vmlDrawingXform, {
|
|
1262
|
+
comments: [],
|
|
1263
|
+
formControls: [],
|
|
1264
|
+
headerImage: {
|
|
1265
|
+
imageRelId: "rId1",
|
|
1266
|
+
width: hdrImage.headerWidth,
|
|
1267
|
+
height: hdrImage.headerHeight
|
|
1268
|
+
}
|
|
1269
|
+
});
|
|
1270
|
+
// Write the VML rels file referencing the image
|
|
1271
|
+
await this._renderToZip(zip, (0, ooxml_paths_1.vmlDrawingHFRelsPath)(fileIndex), relationshipsXform, [
|
|
1272
|
+
{
|
|
1273
|
+
Id: "rId1",
|
|
1274
|
+
Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
|
1275
|
+
Target: imageRelTarget
|
|
1276
|
+
}
|
|
1277
|
+
]);
|
|
1278
|
+
}
|
|
1232
1279
|
// Generate ctrlProp files for form controls
|
|
1233
1280
|
if (hasFormControls) {
|
|
1234
1281
|
for (const control of worksheet.formControls) {
|
|
@@ -1375,6 +1422,7 @@ class XLSX {
|
|
|
1375
1422
|
worksheetOptions.drawings = model.drawings = [];
|
|
1376
1423
|
worksheetOptions.commentRefs = model.commentRefs = [];
|
|
1377
1424
|
worksheetOptions.formControlRefs = model.formControlRefs = [];
|
|
1425
|
+
model.hasHeaderWatermark = false;
|
|
1378
1426
|
let tableCount = 0;
|
|
1379
1427
|
model.tables = [];
|
|
1380
1428
|
model.worksheets.forEach((worksheet, index) => {
|
|
@@ -1393,6 +1441,10 @@ class XLSX {
|
|
|
1393
1441
|
});
|
|
1394
1442
|
// ContentTypesXform expects this flag
|
|
1395
1443
|
model.hasCheckboxes = model.styles.hasCheckboxes;
|
|
1444
|
+
// Propagate header watermark flag from worksheet prepare options
|
|
1445
|
+
if (worksheetOptions.hasHeaderWatermark) {
|
|
1446
|
+
model.hasHeaderWatermark = true;
|
|
1447
|
+
}
|
|
1396
1448
|
// Build passthroughContentTypes for ContentTypesXform using PassthroughManager
|
|
1397
1449
|
const passthrough = model.passthrough || {};
|
|
1398
1450
|
const passthroughManager = new passthrough_manager_1.PassthroughManager();
|
|
@@ -132,11 +132,12 @@ class PdfWriter {
|
|
|
132
132
|
addPage(options) {
|
|
133
133
|
const objNum = this.allocObject();
|
|
134
134
|
const mediaBox = `[0 0 ${(0, pdf_object_1.pdfNumber)(options.width)} ${(0, pdf_object_1.pdfNumber)(options.height)}]`;
|
|
135
|
+
const contentsValue = typeof options.contentsRef === "string" ? options.contentsRef : (0, pdf_object_1.pdfRef)(options.contentsRef);
|
|
135
136
|
const dict = new pdf_object_1.PdfDict()
|
|
136
137
|
.set("Type", "/Page")
|
|
137
138
|
.set("Parent", (0, pdf_object_1.pdfRef)(options.parentRef))
|
|
138
139
|
.set("MediaBox", mediaBox)
|
|
139
|
-
.set("Contents",
|
|
140
|
+
.set("Contents", contentsValue)
|
|
140
141
|
.set("Resources", (0, pdf_object_1.pdfRef)(options.resourcesRef));
|
|
141
142
|
if (options.annotRefs && options.annotRefs.length > 0) {
|
|
142
143
|
dict.set("Annots", "[" + options.annotRefs.map(r => (0, pdf_object_1.pdfRef)(r)).join(" ") + "]");
|