@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.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- /*! API v28.0. Copyright 2018-2023 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
1
+ /*! API v28.2. Copyright 2018-2023 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
2
2
 
3
3
  /**
4
4
  * add our tag to the map
@@ -1754,8 +1754,8 @@ export interface ImageAnnotationData {
1754
1754
  src: string;
1755
1755
 
1756
1756
  /**/
1757
- scale: string;
1758
- original_size: ImageSize;
1757
+ scale?: string;
1758
+ original_size?: ImageSize;
1759
1759
  }
1760
1760
 
1761
1761
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trebco/treb",
3
- "version": "28.0.5",
3
+ "version": "28.2.0",
4
4
  "license": "LGPL-3.0-or-later",
5
5
  "homepage": "https://treb.app",
6
6
  "repository": {
@@ -13,15 +13,16 @@
13
13
  "devDependencies": {
14
14
  "@types/html-minifier": "^4.0.2",
15
15
  "@types/node": "^20.8.5",
16
+ "@types/uzip": "^0.20201231.0",
16
17
  "@typescript-eslint/eslint-plugin": "^6.1.0",
17
18
  "@typescript-eslint/parser": "^6.1.0",
18
19
  "archiver": "^6.0.1",
20
+ "base64-js": "^1.5.1",
19
21
  "cssnano": "^6.0.0",
20
22
  "esbuild": "^0.19.2",
21
23
  "eslint": "^8.19.0",
22
24
  "fast-xml-parser": "^4.0.7",
23
25
  "html-minifier": "^4.0.0",
24
- "jszip": "^3.6.0",
25
26
  "sass": "^1.69.3",
26
27
  "treb-base-types": "file:treb-base-types",
27
28
  "treb-calculator": "file:treb-calculator",
@@ -33,7 +34,8 @@
33
34
  "treb-utils": "file:treb-utils",
34
35
  "ts-node-dev": "^2.0.0",
35
36
  "tslib": "^2.2.0",
36
- "typescript": "^5.0.2"
37
+ "typescript": "^5.0.2",
38
+ "uzip": "^0.20201231.0"
37
39
  },
38
40
  "scripts": {
39
41
  "build": "node esbuild-custom-element.mjs",
@@ -46,6 +46,7 @@ export interface AnchoredAnnotation {
46
46
  layout: AnnotationLayout;
47
47
  type?: AnnotationType;
48
48
  formula?: string;
49
+ data?: any;
49
50
  }
50
51
 
51
52
  /**
@@ -4390,7 +4390,8 @@ export class EmbeddedSpreadsheet {
4390
4390
  /**
4391
4391
  *
4392
4392
  */
4393
- protected async ImportXLSX(data: string, source: LoadSource): Promise<Blob | void> {
4393
+ protected async ImportXLSX( // data: string, source: LoadSource): Promise<Blob | void> {
4394
+ data: ArrayBuffer, source: LoadSource): Promise<Blob | void> {
4394
4395
 
4395
4396
  // this is inlined to ensure the code will be tree-shaken properly
4396
4397
  if (!process.env.XLSX_SUPPORT) {
@@ -4819,30 +4820,12 @@ export class EmbeddedSpreadsheet {
4819
4820
  this.LoadCSV(reader.result as string, source);
4820
4821
  }
4821
4822
  else if (process.env.XLSX_SUPPORT && /\.xls[xm]$/i.test(file.name)) {
4822
- let contents: string;
4823
-
4824
4823
  if (typeof reader.result === 'string') {
4825
- contents = reader.result;
4824
+ finalize('Unsupported file');
4826
4825
  }
4827
- else { // IE11
4828
-
4829
- /* can break on large blob
4830
- contents = String.fromCharCode.apply(null,
4831
- (new Uint8Array(reader.result as ArrayBuffer) as any));
4832
- */
4833
-
4834
- // FIXME: chunk
4835
-
4836
- contents = '';
4837
- const bytes = new Uint8Array(reader.result);
4838
- for (let i = 0; i < bytes.byteLength; i++) {
4839
- contents += String.fromCharCode(bytes[i]);
4840
- }
4841
-
4826
+ else {
4827
+ this.ImportXLSX(reader.result, source).then(() => finalize()).catch(err => finalize(err));
4842
4828
  }
4843
-
4844
- this.ImportXLSX(contents, source).then(() => finalize()).catch(err => finalize(err));
4845
-
4846
4829
  return;
4847
4830
  }
4848
4831
  else {
@@ -4864,13 +4847,8 @@ export class EmbeddedSpreadsheet {
4864
4847
  // FIXME: this should be done async, possibly in a worker
4865
4848
 
4866
4849
  setTimeout(() => {
4867
- if (/\.xlsx$/i.test(file.name)) {
4868
- if (reader.readAsBinaryString) {
4869
- reader.readAsBinaryString(file);
4870
- }
4871
- else {
4872
- reader.readAsArrayBuffer(file); // IE11
4873
- }
4850
+ if (/\.xls[xm]$/i.test(file.name)) {
4851
+ reader.readAsArrayBuffer(file);
4874
4852
  }
4875
4853
  else {
4876
4854
  reader.readAsText(file);
@@ -27,27 +27,22 @@ import { Importer } from '../import2';
27
27
  const ctx: Worker = self as any;
28
28
  const exporter = new Exporter();
29
29
 
30
- const ExportSheets = async (data: any) => {
30
+ const ExportSheets = (data: any) => {
31
31
 
32
32
  if (data.sheet) {
33
- await exporter.Init(data.decorated || []);
34
- await exporter.Export(data.sheet);
35
-
36
- const blob = await exporter.AsBlob(1);
37
- const corrected = (blob as Blob).slice(0, (blob as Blob).size,
38
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
39
-
40
- ctx.postMessage({ status: 'complete', blob: corrected });
33
+ exporter.Init(data.decorated || []);
34
+ exporter.Export(data.sheet);
35
+ ctx.postMessage({ status: 'complete', blob: exporter.Blob() });
41
36
  }
42
37
 
43
38
  };
44
39
 
45
- const ImportSheet = async (data: any) => {
40
+ const ImportSheet = (data: any) => {
46
41
 
47
42
  const importer = new Importer();
48
43
 
49
44
  try {
50
- await importer.Init(data.data);
45
+ importer.Init(data.data);
51
46
 
52
47
  const count = importer.SheetCount();
53
48
  const results = {
@@ -57,7 +52,7 @@ const ImportSheet = async (data: any) => {
57
52
  };
58
53
 
59
54
  for (let i = 0; i < count; i++) {
60
- const result = await importer.GetSheet(i);
55
+ const result = importer.GetSheet(i);
61
56
  if (result) {
62
57
  results.sheets.push(result);
63
58
  }
@@ -25,7 +25,10 @@
25
25
  * run, but it will take a bit more work.
26
26
  */
27
27
 
28
- import JSZip from 'jszip';
28
+ // import JSZip from 'jszip';
29
+
30
+ import UZip from 'uzip';
31
+ import * as Base64JS from 'base64-js';
29
32
 
30
33
  import { PixelsToColumnWidth } from './column-width';
31
34
 
@@ -62,10 +65,12 @@ import type { TwoCellAnchor } from './drawing2/drawing2';
62
65
  import { Drawing } from './drawing2/drawing2';
63
66
  import { ConditionalFormatOperators, type TableDescription, type TableFooterType } from './workbook2';
64
67
  import type { AnnotationData } from 'treb-grid/src/types/annotation';
68
+ import { ZipWrapper } from './zip-wrapper';
65
69
 
66
70
  export class Exporter {
67
71
 
68
- public zip?: JSZip;
72
+ // public zip?: JSZip;
73
+ public zip?: ZipWrapper;
69
74
 
70
75
  public xmloptions: Partial<XmlBuilderOptions> = {
71
76
  format: true,
@@ -129,19 +134,18 @@ export class Exporter {
129
134
  *
130
135
  * @param decorated_functions
131
136
  */
132
- public async Init(decorated_functions: Record<string, string> = {}): Promise<void> {
133
-
134
- // this.decorated_functions = decorated_functions.map(name => name.toLowerCase()); // normalized
137
+ public Init(decorated_functions: Record<string, string> = {}) {
135
138
 
136
139
  for (const key of Object.keys(decorated_functions)) {
137
140
  this.decorated_functions[key.toLowerCase()] = decorated_functions[key]; // normalized
138
141
  }
139
142
 
140
- this.zip = await new JSZip().loadAsync(template, {base64: true});
143
+ const parsed = Base64JS.toByteArray(template);
144
+ this.zip = new ZipWrapper(parsed);
141
145
 
142
146
  }
143
147
 
144
- public async WriteRels(rels: RelationshipMap, path: string, dump = false): Promise<void> {
148
+ public WriteRels(rels: RelationshipMap, path: string, dump = false) {
145
149
 
146
150
  if (!this.zip) {
147
151
  throw new Error('missing zip');
@@ -176,14 +180,15 @@ export class Exporter {
176
180
  if (dump) {
177
181
  console.info(xml);
178
182
  }
179
- await this.zip?.file(path, xml);
183
+
184
+ this.zip.Set(path, xml);
180
185
 
181
186
  }
182
187
 
183
188
  /**
184
189
  * format and write styles
185
190
  */
186
- public async WriteStyleCache(style_cache: StyleCache): Promise<void> {
191
+ public WriteStyleCache(style_cache: StyleCache) {
187
192
 
188
193
  if (!this.zip) {
189
194
  throw new Error('missing zip');
@@ -501,7 +506,7 @@ export class Exporter {
501
506
  const xml = XMLDeclaration + this.xmlbuilder1.build(dom);
502
507
  // console.info(xml);
503
508
 
504
- await this.zip?.file('xl/styles.xml', xml);
509
+ this.zip?.Set('xl/styles.xml', xml);
505
510
 
506
511
  }
507
512
 
@@ -509,7 +514,7 @@ export class Exporter {
509
514
  * format and write shared strings file to the zip archive. this will
510
515
  * replace any existing shared strings file.
511
516
  */
512
- public async WriteSharedStrings(shared_strings: SharedStrings): Promise<void> {
517
+ public WriteSharedStrings(shared_strings: SharedStrings) {
513
518
 
514
519
  // console.info({shared_strings});
515
520
 
@@ -534,7 +539,7 @@ export class Exporter {
534
539
 
535
540
  // console.info(xml);
536
541
 
537
- await this.zip?.file('xl/sharedStrings.xml', xml);
542
+ this.zip.Set('xl/sharedStrings.xml', xml);
538
543
 
539
544
  }
540
545
 
@@ -1157,13 +1162,13 @@ export class Exporter {
1157
1162
 
1158
1163
  }
1159
1164
 
1160
- public async Export(source: {
1165
+ public Export(source: {
1161
1166
  sheet_data: SerializedSheet[];
1162
1167
  active_sheet?: number;
1163
1168
  named_ranges?: {[index: string]: IArea};
1164
1169
  named_expressions?: Array<{ name: string, expression: string }>;
1165
1170
  decimal_mark: ','|'.';
1166
- }): Promise<void> {
1171
+ }) {
1167
1172
 
1168
1173
  // --- create a map --------------------------------------------------------
1169
1174
 
@@ -1198,11 +1203,11 @@ export class Exporter {
1198
1203
  const style_cache = new StyleCache();
1199
1204
  const theme = new Theme();
1200
1205
 
1201
- let data = await this.zip?.file('xl/theme/theme1.xml')?.async('text') as string;
1206
+ let data = this.zip?.Get('xl/theme/theme1.xml');
1202
1207
  theme.FromXML(this.xmlparser2.parse(data || ''));
1203
1208
  // console.info({data, xml: this.xmlparser2.parse(data)})
1204
1209
 
1205
- data = await this.zip?.file('xl/styles.xml')?.async('text') as string;
1210
+ data = this.zip?.Get('xl/styles.xml');
1206
1211
  style_cache.FromXML(this.xmlparser2.parse(data || ''), theme);
1207
1212
 
1208
1213
  // reset counters
@@ -2037,7 +2042,7 @@ export class Exporter {
2037
2042
  };
2038
2043
 
2039
2044
  const xml = XMLDeclaration + this.xmlbuilder1.build(table_dom);
2040
- await this.zip?.file(`xl/tables/table${table.index}.xml`, xml);
2045
+ this.zip?.Set(`xl/tables/table${table.index}.xml`, xml);
2041
2046
 
2042
2047
  }
2043
2048
 
@@ -2304,25 +2309,27 @@ export class Exporter {
2304
2309
  }
2305
2310
 
2306
2311
  for (const {image} of drawing.images) {
2307
- // console.info({image}, `xl/media/image${image.index}.${image.extension}`);
2308
- await this.zip?.file(`xl/media/image${image.index}.${image.extension}`, image.options.data||'', {
2309
- base64: image.options.encoding === 'base64'
2310
- });
2312
+ if (image.options.data) {
2313
+ this.zip?.SetBinary(
2314
+ `xl/media/image${image.index}.${image.extension}`,
2315
+ image.options.data,
2316
+ image.options.encoding);
2317
+ }
2311
2318
  // no media rels!
2312
2319
  }
2313
2320
 
2314
2321
  for (const {chart} of drawing.charts) {
2315
2322
  const dom = chart.toJSON();
2316
2323
  const xml = XMLDeclaration + this.xmlbuilder1.build(dom);
2317
- await this.zip?.file(`xl/charts/chart${chart.index}.xml`, xml);
2318
- await this.WriteRels(chart.relationships, `xl/charts/_rels/chart${chart.index}.xml.rels`);
2324
+ this.zip?.Set(`xl/charts/chart${chart.index}.xml`, xml);
2325
+ this.WriteRels(chart.relationships, `xl/charts/_rels/chart${chart.index}.xml.rels`);
2319
2326
  }
2320
2327
 
2321
- await this.WriteRels(drawing.relationships, `xl/drawings/_rels/drawing${drawing.index}.xml.rels`);
2328
+ this.WriteRels(drawing.relationships, `xl/drawings/_rels/drawing${drawing.index}.xml.rels`);
2322
2329
 
2323
2330
  const xml = XMLDeclaration + this.xmlbuilder1.build(drawing.toJSON());
2324
2331
 
2325
- await this.zip?.file(`xl/drawings/drawing${drawing.index}.xml`, xml);
2332
+ this.zip?.Set(`xl/drawings/drawing${drawing.index}.xml`, xml);
2326
2333
 
2327
2334
  drawings.push(drawing); // for [ContentTypes]
2328
2335
 
@@ -2358,7 +2365,7 @@ export class Exporter {
2358
2365
 
2359
2366
  // write this into the file
2360
2367
 
2361
- await this.zip?.file(`xl/worksheets/sheet${sheet_index + 1}.xml`, xml);
2368
+ this.zip?.Set(`xl/worksheets/sheet${sheet_index + 1}.xml`, xml);
2362
2369
  if (Object.keys(sheet_rels).length) {
2363
2370
  this.WriteRels(sheet_rels, `xl/worksheets/_rels/sheet${sheet_index + 1}.xml.rels`);
2364
2371
  }
@@ -2369,8 +2376,8 @@ export class Exporter {
2369
2376
 
2370
2377
  // these are workbook global so after all sheets are done
2371
2378
 
2372
- await this.WriteSharedStrings(shared_strings);
2373
- await this.WriteStyleCache(style_cache);
2379
+ this.WriteSharedStrings(shared_strings);
2380
+ this.WriteStyleCache(style_cache);
2374
2381
 
2375
2382
  // now have to write/update
2376
2383
  //
@@ -2389,7 +2396,7 @@ export class Exporter {
2389
2396
  `worksheets/sheet${index + 1}.xml`,
2390
2397
  ));
2391
2398
 
2392
- await this.WriteRels(workbook_rels, `xl/_rels/workbook.xml.rels`);
2399
+ this.WriteRels(workbook_rels, `xl/_rels/workbook.xml.rels`);
2393
2400
 
2394
2401
  let definedNames: any = {definedName: []};
2395
2402
  if (source.named_ranges) {
@@ -2480,7 +2487,7 @@ export class Exporter {
2480
2487
 
2481
2488
  const workbook_xml = XMLDeclaration + this.xmlbuilder1.build(workbook_dom);
2482
2489
  // console.info(workbook_xml);
2483
- await this.zip?.file(`xl/workbook.xml`, workbook_xml);
2490
+ this.zip?.Set(`xl/workbook.xml`, workbook_xml);
2484
2491
 
2485
2492
  // const extensions: Array<{ Extension: string, ContentType: string }> = [];
2486
2493
  const extensions: Record<string, string> = {};
@@ -2560,11 +2567,26 @@ export class Exporter {
2560
2567
 
2561
2568
  const content_types_xml = XMLDeclaration + this.xmlbuilder1.build(content_types_dom);
2562
2569
  // console.info(content_types_xml);
2563
- await this.zip?.file(`[Content_Types].xml`, content_types_xml);
2570
+ this.zip?.Set(`[Content_Types].xml`, content_types_xml);
2564
2571
 
2565
2572
  }
2566
2573
 
2567
- /** zip -> binary string */
2574
+ public ArrayBuffer() {
2575
+ if (!this.zip) {
2576
+ throw new Error('missing zip');
2577
+ }
2578
+ return this.zip.ArrayBuffer();
2579
+ }
2580
+
2581
+ public Blob() {
2582
+ if (!this.zip) {
2583
+ throw new Error('missing zip');
2584
+ }
2585
+ const buffer = this.zip.ArrayBuffer();
2586
+ return new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
2587
+ }
2588
+
2589
+ /* * zip -> binary string * /
2568
2590
  public async AsBinaryString(compression_level?: number) {
2569
2591
  if (!this.zip) {
2570
2592
  throw new Error('missing zip');
@@ -2578,7 +2600,7 @@ export class Exporter {
2578
2600
  return output;
2579
2601
  }
2580
2602
 
2581
- /** zip -> blob */
2603
+ /* * zip -> blob * /
2582
2604
  public async AsBlob(compression_level?: number) {
2583
2605
  if (!this.zip) {
2584
2606
  throw new Error('missing zip');
@@ -2591,5 +2613,7 @@ export class Exporter {
2591
2613
  const output = await this.zip.generateAsync(opts);
2592
2614
  return output;
2593
2615
  }
2616
+ */
2617
+
2594
2618
 
2595
2619
  }
@@ -19,10 +19,12 @@
19
19
  *
20
20
  */
21
21
 
22
- //import * as JSZip from 'jszip';
23
- import JSZip from 'jszip';
22
+ // import JSZip from 'jszip';
24
23
 
25
- import type { AnchoredChartDescription} from './workbook2';
24
+ import UZip from 'uzip';
25
+ import Base64JS from 'base64-js';
26
+
27
+ import type { AnchoredChartDescription, AnchoredImageDescription} from './workbook2';
26
28
  import { ChartType, ConditionalFormatOperators, Workbook } from './workbook2';
27
29
  import type { ParseResult } from 'treb-parser';
28
30
  import { Parser } from 'treb-parser';
@@ -38,6 +40,7 @@ import { XMLUtils } from './xml-utils';
38
40
  // import { one_hundred_pixels } from './constants';
39
41
  import { ColumnWidthToPixels } from './column-width';
40
42
  import type { AnnotationType, ConditionalFormat } from 'treb-grid';
43
+ import { ZipWrapper } from './zip-wrapper';
41
44
 
42
45
  interface SharedFormula {
43
46
  row: number;
@@ -55,22 +58,14 @@ export class Importer {
55
58
 
56
59
  public workbook?: Workbook;
57
60
 
58
- public archive?: JSZip;
59
-
60
- public async Init(data: string | JSZip): Promise<void> {
61
+ // public archive?: JSZip;
61
62
 
62
- if (typeof data === 'string') {
63
- this.archive = await JSZip().loadAsync(data);
64
- }
65
- else {
66
- this.archive = data;
67
- }
68
-
69
- if (this.archive) {
70
- this.workbook = new Workbook(this.archive);
71
- await this.workbook.Init();
72
- }
63
+ public zip?: ZipWrapper;
73
64
 
65
+ public Init(data: ArrayBuffer) {
66
+ this.zip = new ZipWrapper(data);
67
+ this.workbook = new Workbook(this.zip);
68
+ this.workbook.Init();
74
69
  }
75
70
 
76
71
  /** FIXME: accessor */
@@ -465,7 +460,7 @@ export class Importer {
465
460
  return undefined;
466
461
  }
467
462
 
468
- public async GetSheet(index = 0): Promise<ImportedSheetData> {
463
+ public GetSheet(index = 0): ImportedSheetData {
469
464
 
470
465
  if (!this.workbook) {
471
466
  throw new Error('missing workbook');
@@ -764,7 +759,7 @@ export class Importer {
764
759
  const relationship = sheet.rels[rel];
765
760
  if (relationship) {
766
761
  reference = relationship.target || '';
767
- const description = await this.workbook.ReadTable(reference);
762
+ const description = this.workbook.ReadTable(reference);
768
763
  if (description) {
769
764
 
770
765
  // console.info({description});
@@ -806,8 +801,10 @@ export class Importer {
806
801
 
807
802
  const drawings = FindAll('worksheet/drawing');
808
803
  const chart_descriptors: AnchoredChartDescription[] = [];
804
+ const image_descriptors: AnchoredImageDescription[] = [];
809
805
 
810
806
  for (const child of drawings) {
807
+
811
808
  const rel = child.a$ ? child.a$['r:id'] : undefined;
812
809
  if (rel) {
813
810
 
@@ -819,9 +816,18 @@ export class Importer {
819
816
  }
820
817
 
821
818
  if (reference) {
822
- const drawing = await this.workbook.ReadDrawing(reference);
819
+ const drawing = this.workbook.ReadDrawing(reference);
823
820
  if (drawing && drawing.length) {
824
- chart_descriptors.push(...drawing);
821
+ for (const entry of drawing) {
822
+ switch (entry.type) {
823
+ case 'chart':
824
+ chart_descriptors.push(entry);
825
+ break;
826
+ case 'image':
827
+ image_descriptors.push(entry);
828
+ break;
829
+ }
830
+ }
825
831
  }
826
832
  }
827
833
 
@@ -861,6 +867,40 @@ export class Importer {
861
867
 
862
868
  };
863
869
 
870
+ for (const descriptor of image_descriptors) {
871
+ if (descriptor && descriptor.image) {
872
+
873
+ const layout: AnnotationLayout = {
874
+ tl: AnchorToCorner(descriptor.anchor.from),
875
+ br: AnchorToCorner(descriptor.anchor.to),
876
+ };
877
+
878
+ let type: AnnotationType = 'image';
879
+ const data = Base64JS.fromByteArray(descriptor.image);
880
+ let imagetype: string = '';
881
+
882
+ if (descriptor.filename) {
883
+ if (/jpe*g$/i.test(descriptor.filename)) {
884
+ imagetype = 'jpeg';
885
+ }
886
+ else if (/png$/i.test(descriptor.filename)) {
887
+ imagetype = 'png';
888
+ }
889
+ else if (/gif$/i.test(descriptor.filename)) {
890
+ imagetype = 'gif';
891
+ }
892
+ }
893
+
894
+ if (imagetype && data) {
895
+ const src = 'data:image/' + imagetype + ';base64,' + data;
896
+ annotations.push({
897
+ layout, type, data: { src },
898
+ });
899
+ }
900
+
901
+ }
902
+ }
903
+
864
904
  for (const descriptor of chart_descriptors) {
865
905
  if (descriptor && descriptor.chart) {
866
906