@trebco/treb 28.0.5 → 28.2.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/treb-spreadsheet-light.mjs +4 -4
- package/dist/treb-spreadsheet.mjs +12 -12
- package/dist/treb.d.ts +3 -3
- package/package.json +5 -3
- package/treb-base-types/src/import.ts +1 -0
- package/treb-embed/src/embedded-spreadsheet.ts +7 -29
- package/treb-export/src/export-worker/export-worker.ts +7 -12
- package/treb-export/src/export2.ts +57 -33
- package/treb-export/src/import2.ts +61 -21
- package/treb-export/src/workbook2.ts +69 -24
- package/treb-export/src/zip-wrapper.ts +96 -0
- package/treb-grid/src/types/annotation.ts +2 -2
|
@@ -19,8 +19,12 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import
|
|
22
|
+
import UZip from 'uzip';
|
|
23
|
+
|
|
24
|
+
// import type JSZip from 'jszip';
|
|
23
25
|
// import * as xmlparser from 'fast-xml-parser';
|
|
26
|
+
|
|
27
|
+
|
|
24
28
|
import { XMLParser } from 'fast-xml-parser';
|
|
25
29
|
import { XMLUtils, XMLOptions, XMLOptions2 } from './xml-utils';
|
|
26
30
|
|
|
@@ -39,6 +43,7 @@ import { StyleCache } from './workbook-style2';
|
|
|
39
43
|
import { Theme } from './workbook-theme2';
|
|
40
44
|
import { Sheet, VisibleState } from './workbook-sheet2';
|
|
41
45
|
import type { RelationshipMap } from './relationship';
|
|
46
|
+
import { ZipWrapper } from './zip-wrapper';
|
|
42
47
|
|
|
43
48
|
|
|
44
49
|
/*
|
|
@@ -79,11 +84,21 @@ export interface ChartDescription {
|
|
|
79
84
|
series?: ChartSeries[];
|
|
80
85
|
}
|
|
81
86
|
|
|
87
|
+
export interface AnchoredImageDescription {
|
|
88
|
+
type: 'image';
|
|
89
|
+
image?: Uint8Array;
|
|
90
|
+
filename?: string;
|
|
91
|
+
anchor: TwoCellAnchor,
|
|
92
|
+
}
|
|
93
|
+
|
|
82
94
|
export interface AnchoredChartDescription {
|
|
95
|
+
type: 'chart';
|
|
83
96
|
chart?: ChartDescription,
|
|
84
97
|
anchor: TwoCellAnchor,
|
|
85
98
|
}
|
|
86
99
|
|
|
100
|
+
export type AnchoredDrawingPart = AnchoredChartDescription|AnchoredImageDescription;
|
|
101
|
+
|
|
87
102
|
export interface TableFooterType {
|
|
88
103
|
type: 'label'|'formula';
|
|
89
104
|
value: string;
|
|
@@ -138,17 +153,17 @@ export class Workbook {
|
|
|
138
153
|
return this.sheets.length;
|
|
139
154
|
}
|
|
140
155
|
|
|
141
|
-
constructor(public zip:
|
|
156
|
+
constructor(public zip: ZipWrapper) {
|
|
142
157
|
|
|
143
158
|
}
|
|
144
159
|
|
|
145
160
|
/**
|
|
146
161
|
* given a path in the zip file, read and parse the rels file
|
|
147
162
|
*/
|
|
148
|
-
public
|
|
163
|
+
public ReadRels(path: string): RelationshipMap {
|
|
149
164
|
|
|
150
165
|
const rels: RelationshipMap = {};
|
|
151
|
-
const data =
|
|
166
|
+
const data = this.zip.Has(path) ? this.zip.Get(path) : '';
|
|
152
167
|
|
|
153
168
|
//
|
|
154
169
|
// force array on <Relationship/> elements, but be slack on the rest
|
|
@@ -169,30 +184,30 @@ export class Workbook {
|
|
|
169
184
|
|
|
170
185
|
}
|
|
171
186
|
|
|
172
|
-
public
|
|
187
|
+
public Init() {
|
|
173
188
|
|
|
174
189
|
// read workbook rels
|
|
175
|
-
this.rels =
|
|
176
|
-
|
|
190
|
+
this.rels = this.ReadRels( 'xl/_rels/workbook.xml.rels');
|
|
191
|
+
|
|
177
192
|
// shared strings
|
|
178
|
-
let data =
|
|
193
|
+
let data = this.zip.Has('xl/sharedStrings.xml') ? this.zip.Get('xl/sharedStrings.xml') : '';
|
|
179
194
|
let xml = xmlparser2.parse(data || '');
|
|
180
195
|
this.shared_strings.FromXML(xml);
|
|
181
196
|
|
|
182
197
|
// theme
|
|
183
|
-
data =
|
|
198
|
+
data = this.zip.Get('xl/theme/theme1.xml');
|
|
184
199
|
xml = xmlparser2.parse(data);
|
|
185
200
|
this.theme.FromXML(xml);
|
|
186
201
|
|
|
187
202
|
// styles
|
|
188
|
-
data =
|
|
203
|
+
data = this.zip.Get('xl/styles.xml');
|
|
189
204
|
xml = xmlparser2.parse(data);
|
|
190
205
|
this.style_cache.FromXML(xml, this.theme);
|
|
191
206
|
|
|
192
207
|
// console.info({c: this.style_cache});
|
|
193
208
|
|
|
194
209
|
// read workbook
|
|
195
|
-
data =
|
|
210
|
+
data = this.zip.Get('xl/workbook.xml');
|
|
196
211
|
xml = xmlparser2.parse(data);
|
|
197
212
|
|
|
198
213
|
// defined names
|
|
@@ -240,9 +255,9 @@ export class Workbook {
|
|
|
240
255
|
worksheet.path = `xl/${this.rels[rid].target}`;
|
|
241
256
|
worksheet.rels_path = worksheet.path.replace('worksheets', 'worksheets/_rels') + '.rels';
|
|
242
257
|
|
|
243
|
-
data =
|
|
258
|
+
data = this.zip.Get(worksheet.path);
|
|
244
259
|
worksheet.sheet_data = xmlparser2.parse(data || '');
|
|
245
|
-
worksheet.rels =
|
|
260
|
+
worksheet.rels = this.ReadRels(worksheet.rels_path);
|
|
246
261
|
|
|
247
262
|
worksheet.Parse();
|
|
248
263
|
// console.info("TS", worksheet);
|
|
@@ -256,9 +271,9 @@ export class Workbook {
|
|
|
256
271
|
|
|
257
272
|
}
|
|
258
273
|
|
|
259
|
-
public
|
|
274
|
+
public ReadTable(reference: string): TableDescription|undefined {
|
|
260
275
|
|
|
261
|
-
const data =
|
|
276
|
+
const data = this.zip.Get(reference.replace(/^../, 'xl'));
|
|
262
277
|
|
|
263
278
|
if (!data) {
|
|
264
279
|
return undefined;
|
|
@@ -276,19 +291,21 @@ export class Workbook {
|
|
|
276
291
|
};
|
|
277
292
|
|
|
278
293
|
return table;
|
|
294
|
+
|
|
279
295
|
}
|
|
280
296
|
|
|
281
|
-
public
|
|
297
|
+
public ReadDrawing(reference: string): AnchoredDrawingPart[] | undefined {
|
|
282
298
|
|
|
283
|
-
const data =
|
|
299
|
+
const data = this.zip.Get(reference.replace(/^../, 'xl'));
|
|
300
|
+
|
|
284
301
|
if (!data) {
|
|
285
302
|
return undefined;
|
|
286
303
|
}
|
|
287
304
|
const xml = xmlparser2.parse(data);
|
|
288
305
|
|
|
289
|
-
const drawing_rels =
|
|
306
|
+
const drawing_rels = this.ReadRels(reference.replace(/^..\/drawings/, 'xl/drawings/_rels') + '.rels');
|
|
290
307
|
|
|
291
|
-
const results:
|
|
308
|
+
const results: AnchoredDrawingPart[] = [];
|
|
292
309
|
const anchor_nodes = XMLUtils.FindAll(xml, 'xdr:wsDr/xdr:twoCellAnchor');
|
|
293
310
|
|
|
294
311
|
/* FIXME: move to drawing? */
|
|
@@ -308,18 +325,46 @@ export class Workbook {
|
|
|
308
325
|
from: ParseAnchor(anchor_node['xdr:from']),
|
|
309
326
|
to: ParseAnchor(anchor_node['xdr:to']),
|
|
310
327
|
};
|
|
311
|
-
const result: AnchoredChartDescription = { anchor };
|
|
312
328
|
|
|
313
329
|
const chart_reference = XMLUtils.FindAll(anchor_node, `xdr:graphicFrame/a:graphic/a:graphicData/c:chart`)[0];
|
|
314
330
|
|
|
315
331
|
if (chart_reference && chart_reference.a$ && chart_reference.a$['r:id']) {
|
|
332
|
+
const result: AnchoredChartDescription = { type: 'chart', anchor };
|
|
316
333
|
const chart_rel = drawing_rels[chart_reference.a$['r:id']];
|
|
317
334
|
if (chart_rel && chart_rel.target) {
|
|
318
|
-
result.chart =
|
|
335
|
+
result.chart = this.ReadChart(chart_rel.target);
|
|
319
336
|
}
|
|
337
|
+
results.push(result);
|
|
320
338
|
}
|
|
339
|
+
else {
|
|
321
340
|
|
|
322
|
-
|
|
341
|
+
const media_reference = XMLUtils.FindAll(anchor_node, `xdr:pic/xdr:blipFill/a:blip`)[0];
|
|
342
|
+
if (media_reference && media_reference.a$['r:embed']) {
|
|
343
|
+
const media_rel = drawing_rels[media_reference.a$['r:embed']];
|
|
344
|
+
|
|
345
|
+
// const chart_rel = drawing_rels[chart_reference.a$['r:id']];
|
|
346
|
+
// console.info("Maybe an image?", media_reference, media_rel)
|
|
347
|
+
|
|
348
|
+
if (media_rel && media_rel.target) {
|
|
349
|
+
if (/(?:jpg|jpeg|png|gif)$/i.test(media_rel.target)) {
|
|
350
|
+
|
|
351
|
+
// const result: AnchoredImageDescription = { type: 'image' };
|
|
352
|
+
const path = media_rel.target.replace(/^\.\./, 'xl');
|
|
353
|
+
const filename = path.replace(/^.*\//, '');
|
|
354
|
+
|
|
355
|
+
const result: AnchoredImageDescription = {
|
|
356
|
+
type: 'image', anchor, image: this.zip.GetBinary(path), filename
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
results.push(result);
|
|
360
|
+
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
}
|
|
323
368
|
|
|
324
369
|
}
|
|
325
370
|
|
|
@@ -332,9 +377,9 @@ export class Workbook {
|
|
|
332
377
|
* FIXME: this is using the old options with old structure, just have
|
|
333
378
|
* not updated it yet
|
|
334
379
|
*/
|
|
335
|
-
public
|
|
380
|
+
public ReadChart(reference: string): ChartDescription|undefined {
|
|
336
381
|
|
|
337
|
-
const data =
|
|
382
|
+
const data = this.zip.Get(reference.replace(/^../, 'xl'));
|
|
338
383
|
if (!data) { return undefined; }
|
|
339
384
|
|
|
340
385
|
const xml = xmlparser1.parse(data);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
|
|
2
|
+
import UZip from 'uzip';
|
|
3
|
+
import Base64JS from 'base64-js';
|
|
4
|
+
|
|
5
|
+
export class ZipWrapper {
|
|
6
|
+
|
|
7
|
+
public records: UZip.UZIPFiles;
|
|
8
|
+
public text: Map<string, string> = new Map();
|
|
9
|
+
|
|
10
|
+
public constructor(buffer: ArrayBuffer) {
|
|
11
|
+
this.records = UZip.parse(buffer);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* check if entry exists
|
|
16
|
+
*/
|
|
17
|
+
public Has(path: string) {
|
|
18
|
+
return this.text.has(path) || !!this.records?.[path];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* nondestructive
|
|
23
|
+
*/
|
|
24
|
+
public ArrayBuffer() {
|
|
25
|
+
|
|
26
|
+
const records: Record<string, Uint8Array> = {};
|
|
27
|
+
if (this.records) {
|
|
28
|
+
for (const [key, value] of Object.entries(this.records)) {
|
|
29
|
+
records[key] = new Uint8Array(value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const encoder = new TextEncoder();
|
|
34
|
+
for (const [key, value] of this.text.entries()) {
|
|
35
|
+
records[key] = encoder.encode(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return UZip.encode(records);
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* set a binary file. set this directly in the records table,
|
|
44
|
+
* instead of the text table.
|
|
45
|
+
*/
|
|
46
|
+
public SetBinary(path: string, data: string, encoding?: 'base64'|'binary') {
|
|
47
|
+
|
|
48
|
+
if (encoding === 'base64') {
|
|
49
|
+
const bytes = Base64JS.toByteArray(data);
|
|
50
|
+
this.records[path] = bytes;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
throw new Error('unsupported encoding: ' + encoding);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public Set(path: string, text: string) {
|
|
59
|
+
|
|
60
|
+
this.text.set(path, text);
|
|
61
|
+
if (!!this.records[path]) {
|
|
62
|
+
delete this.records[path];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public GetBinary(path: string) {
|
|
67
|
+
const data = this.records[path];
|
|
68
|
+
if (data) {
|
|
69
|
+
return new Uint8Array(data);
|
|
70
|
+
}
|
|
71
|
+
throw new Error('path not in records: ' + path);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public Get(path: string) {
|
|
75
|
+
|
|
76
|
+
let text = this.text.get(path);
|
|
77
|
+
|
|
78
|
+
if (text) {
|
|
79
|
+
return text;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const data = this.records[path];
|
|
83
|
+
if (data) {
|
|
84
|
+
text = new TextDecoder().decode(data);
|
|
85
|
+
this.text.set(path, text);
|
|
86
|
+
delete this.records[path];
|
|
87
|
+
return text;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.info(this);
|
|
91
|
+
|
|
92
|
+
throw new Error('path not in zip file: ' + path);
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
}
|