abstract-document 15.3.2 → 16.0.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.
Files changed (25) hide show
  1. package/lib/abstract-document-exporters/docx2/render-image.d.ts +1 -1
  2. package/lib/abstract-document-exporters/docx2/render-image.d.ts.map +1 -1
  3. package/lib/abstract-document-exporters/docx2/render-image.js +13 -4
  4. package/lib/abstract-document-exporters/docx2/render-image.js.map +1 -1
  5. package/lib/abstract-document-exporters/docx2/render.d.ts +2 -2
  6. package/lib/abstract-document-exporters/docx2/render.d.ts.map +1 -1
  7. package/lib/abstract-document-exporters/docx2/render.js +26 -26
  8. package/lib/abstract-document-exporters/docx2/render.js.map +1 -1
  9. package/lib/abstract-document-exporters/pdf/render-image.d.ts +1 -1
  10. package/lib/abstract-document-exporters/pdf/render-image.d.ts.map +1 -1
  11. package/lib/abstract-document-exporters/pdf/render-image.js +72 -70
  12. package/lib/abstract-document-exporters/pdf/render-image.js.map +1 -1
  13. package/lib/abstract-document-exporters/pdf/render.d.ts +2 -2
  14. package/lib/abstract-document-exporters/pdf/render.d.ts.map +1 -1
  15. package/lib/abstract-document-exporters/pdf/render.js +26 -26
  16. package/lib/abstract-document-exporters/pdf/render.js.map +1 -1
  17. package/lib/abstract-document-exporters/shared/image-base64.d.ts +2 -0
  18. package/lib/abstract-document-exporters/shared/image-base64.d.ts.map +1 -0
  19. package/lib/abstract-document-exporters/shared/image-base64.js +17 -0
  20. package/lib/abstract-document-exporters/shared/image-base64.js.map +1 -0
  21. package/package.json +5 -7
  22. package/src/abstract-document-exporters/docx2/render-image.ts +22 -4
  23. package/src/abstract-document-exporters/docx2/render.ts +49 -26
  24. package/src/abstract-document-exporters/pdf/render-image.ts +104 -78
  25. package/src/abstract-document-exporters/pdf/render.ts +66 -26
@@ -1,5 +1,4 @@
1
1
  import * as AbstractImage from "abstract-image";
2
- import { fromByteArray } from "base64-js";
3
2
  import svgToPdfKit from "svg-to-pdfkit";
4
3
  import * as AD from "../../abstract-document/index.js";
5
4
  import { getFontNameStyle, getFontName, isFontAvailable } from "./font.js";
@@ -9,7 +8,8 @@ export function renderImage(
9
8
  pdf: PDFKit.PDFDocument,
10
9
  finalRect: AD.Rect.Rect,
11
10
  textStyle: AD.TextStyle.TextStyle,
12
- image: AD.Image.Image
11
+ image: AD.Image.Image,
12
+ imageDataByUrl: Record<string, Uint8Array | string>
13
13
  ): void {
14
14
  const aImage = image.imageResource.abstractImage;
15
15
  const position = AD.Point.create(finalRect.x, finalRect.y);
@@ -18,7 +18,9 @@ export function renderImage(
18
18
  const scale = Math.min(scaleX, scaleY);
19
19
  pdf.save();
20
20
  pdf.translate(position.x, position.y).scale(scale);
21
- aImage.components.forEach((c: AbstractImage.Component) => abstractComponentToPdf(resources, pdf, c, textStyle));
21
+ aImage.components.forEach((c: AbstractImage.Component) =>
22
+ abstractComponentToPdf(resources, pdf, c, textStyle, imageDataByUrl)
23
+ );
22
24
  pdf.restore();
23
25
  }
24
26
 
@@ -26,93 +28,51 @@ function abstractComponentToPdf(
26
28
  resources: AD.Resources.Resources,
27
29
  pdf: PDFKit.PDFDocument,
28
30
  component: AbstractImage.Component,
29
- textStyle: AD.TextStyle.TextStyle
31
+ textStyle: AD.TextStyle.TextStyle,
32
+ imageDataByUrl: Record<string, Uint8Array | string>
30
33
  ): void {
31
34
  switch (component.type) {
32
35
  case "group":
33
- component.children.forEach((c) => abstractComponentToPdf(resources, pdf, c, textStyle));
36
+ component.children.forEach((c) => abstractComponentToPdf(resources, pdf, c, textStyle, imageDataByUrl));
34
37
  break;
35
38
  case "binaryimage":
36
39
  const format = component.format.toLowerCase();
37
40
  const imageWidth = component.bottomRight.x - component.topLeft.x;
38
41
  const imageHeight = component.bottomRight.y - component.topLeft.y;
39
42
  if (component.data.type === "url") {
40
- pdf.image(component.data.url, component.topLeft.x, component.topLeft.y, {
41
- fit: [imageWidth, imageHeight],
42
- });
43
+ const imageData = imageDataByUrl[component.data.url];
44
+ if (typeof imageData === "string" && /^\s*<svg[\s>]/i.test(imageData)) {
45
+ addWithSvgToPdfKit(imageData, component, pdf, resources, textStyle);
46
+ } else {
47
+ pdf.image(
48
+ imageData instanceof Uint8Array
49
+ ? Buffer.from(imageData.buffer, imageData.byteOffset, imageData.byteLength)
50
+ : component.data.url,
51
+ component.topLeft.x,
52
+ component.topLeft.y,
53
+ { fit: [imageWidth, imageHeight] }
54
+ );
55
+ }
43
56
  } else if (format === "png") {
44
- const data = "data:image/png;base64," + fromByteArray(component.data.bytes);
45
- pdf.image(data, component.topLeft.x, component.topLeft.y, {
46
- fit: [imageWidth, imageHeight],
47
- });
48
- } else if (format === "jpg") {
49
- const data = "data:image/jpeg;base64," + fromByteArray(component.data.bytes);
50
- pdf.image(data, component.topLeft.x, component.topLeft.y, {
51
- fit: [imageWidth, imageHeight],
52
- });
53
- } else if (format === "svg") {
54
- const svg = new TextDecoder().decode(component.data.bytes);
55
-
56
- // Special to compensate for pdfKit demanding lower case
57
- // Remove when Svg-To-PdfKit has fixed "toLowerCase"
58
- // https://github.com/alafr/SVG-to-PDFKit/issues/152
59
- let svgUpdated = svg;
60
- ["fill=", "stroke=", "color="].forEach((t) => {
61
- let index = 0;
62
- // eslint-disable-next-line no-constant-condition
63
- while (true) {
64
- index = svgUpdated.indexOf(t, index);
65
- if (index === -1) break;
66
- let indexStart = svgUpdated.indexOf('"', index);
67
- let indexEnd = svgUpdated.indexOf('"', indexStart + 1);
68
- index = indexEnd;
69
-
70
- const color = svgUpdated.substring(indexStart, indexEnd);
71
- if (color !== color.toLowerCase() && color.toLowerCase().indexOf("url(") === -1)
72
- svgUpdated =
73
- svgUpdated.substring(0, indexStart) +
74
- color.toLowerCase() +
75
- svgUpdated.substring(indexEnd, svgUpdated.length);
57
+ pdf.image(
58
+ Buffer.from(component.data.bytes.buffer, component.data.bytes.byteOffset, component.data.bytes.byteLength),
59
+ component.topLeft.x,
60
+ component.topLeft.y,
61
+ {
62
+ fit: [imageWidth, imageHeight],
76
63
  }
77
- });
78
-
79
- ["stroke-dasharray="].forEach((t) => {
80
- let index = 0;
81
- // eslint-disable-next-line no-constant-condition
82
- while (true) {
83
- index = svgUpdated.indexOf(t, index);
84
- if (index === -1) break;
85
- let indexStart = svgUpdated.indexOf('"', index) + 1;
86
- let indexEnd = svgUpdated.indexOf('"', indexStart);
87
- index = indexEnd;
88
-
89
- let dasharray = svgUpdated.substring(indexStart, indexEnd);
90
-
91
- dasharray = dasharray
92
- .split(" ")
93
- .map((x) => parseFloat(x))
94
- .filter((x) => x !== 0)
95
- .join(" ");
96
-
97
- svgUpdated =
98
- svgUpdated.substring(0, indexStart) + dasharray + svgUpdated.substring(indexEnd, svgUpdated.length);
64
+ );
65
+ } else if (format === "jpg") {
66
+ pdf.image(
67
+ Buffer.from(component.data.bytes.buffer, component.data.bytes.byteOffset, component.data.bytes.byteLength),
68
+ component.topLeft.x,
69
+ component.topLeft.y,
70
+ {
71
+ fit: [imageWidth, imageHeight],
99
72
  }
100
- });
101
-
102
- const imageWidth = component.bottomRight.x - component.topLeft.x;
103
- const imageHeight = component.bottomRight.y - component.topLeft.y;
104
- svgToPdfKit(pdf, svgUpdated, component.topLeft.x, component.topLeft.y, {
105
- width: imageWidth,
106
- height: imageHeight,
107
- preserveAspectRatio: "xMinYMin",
108
- fontCallback: (family: string, _bold: boolean, _italic: boolean) => {
109
- if (isFontAvailable(family, resources)) {
110
- return family;
111
- } else {
112
- return getFontNameStyle(textStyle);
113
- }
114
- },
115
- });
73
+ );
74
+ } else if (format === "svg") {
75
+ addWithSvgToPdfKit(new TextDecoder().decode(component.data.bytes), component, pdf, resources, textStyle);
116
76
  }
117
77
  break;
118
78
  case "subimage":
@@ -209,6 +169,72 @@ function abstractComponentToPdf(
209
169
  }
210
170
  }
211
171
 
172
+ function addWithSvgToPdfKit(
173
+ svg: string,
174
+ component: AbstractImage.BinaryImage,
175
+ pdf: PDFKit.PDFDocument,
176
+ resources: AD.Resources.Resources,
177
+ textStyle: AD.TextStyle.TextStyle
178
+ ): void {
179
+ // Special to compensate for pdfKit demanding lower case
180
+ // Remove when Svg-To-PdfKit has fixed "toLowerCase"
181
+ // https://github.com/alafr/SVG-to-PDFKit/issues/152
182
+ let svgUpdated = svg;
183
+ ["fill=", "stroke=", "color="].forEach((t) => {
184
+ let index = 0;
185
+ // eslint-disable-next-line no-constant-condition
186
+ while (true) {
187
+ index = svgUpdated.indexOf(t, index);
188
+ if (index === -1) break;
189
+ let indexStart = svgUpdated.indexOf('"', index);
190
+ let indexEnd = svgUpdated.indexOf('"', indexStart + 1);
191
+ index = indexEnd;
192
+
193
+ const color = svgUpdated.substring(indexStart, indexEnd);
194
+ if (color !== color.toLowerCase() && color.toLowerCase().indexOf("url(") === -1)
195
+ svgUpdated =
196
+ svgUpdated.substring(0, indexStart) + color.toLowerCase() + svgUpdated.substring(indexEnd, svgUpdated.length);
197
+ }
198
+ });
199
+
200
+ ["stroke-dasharray="].forEach((t) => {
201
+ let index = 0;
202
+ // eslint-disable-next-line no-constant-condition
203
+ while (true) {
204
+ index = svgUpdated.indexOf(t, index);
205
+ if (index === -1) break;
206
+ let indexStart = svgUpdated.indexOf('"', index) + 1;
207
+ let indexEnd = svgUpdated.indexOf('"', indexStart);
208
+ index = indexEnd;
209
+
210
+ let dasharray = svgUpdated.substring(indexStart, indexEnd);
211
+
212
+ dasharray = dasharray
213
+ .split(" ")
214
+ .map((x) => parseFloat(x))
215
+ .filter((x) => x !== 0)
216
+ .join(" ");
217
+
218
+ svgUpdated = svgUpdated.substring(0, indexStart) + dasharray + svgUpdated.substring(indexEnd, svgUpdated.length);
219
+ }
220
+ });
221
+
222
+ const imageWidth = component.bottomRight.x - component.topLeft.x;
223
+ const imageHeight = component.bottomRight.y - component.topLeft.y;
224
+ svgToPdfKit(pdf, svgUpdated, component.topLeft.x, component.topLeft.y, {
225
+ width: imageWidth,
226
+ height: imageHeight,
227
+ preserveAspectRatio: "xMinYMin",
228
+ fontCallback: (family: string, _bold: boolean, _italic: boolean) => {
229
+ if (isFontAvailable(family, resources)) {
230
+ return family;
231
+ } else {
232
+ return getFontNameStyle(textStyle);
233
+ }
234
+ },
235
+ });
236
+ }
237
+
212
238
  function colorToOpacity(color: AbstractImage.Color): number {
213
239
  return color.a / 255;
214
240
  }
@@ -13,10 +13,11 @@ export type PdfExportOptions = {
13
13
  export function exportToHTML5Blob(
14
14
  pdfKit: PDFKit.PDFDocument,
15
15
  doc: AD.AbstractDoc.AbstractDoc,
16
- options: PdfExportOptions = { compress: false }
16
+ options: PdfExportOptions = { compress: false },
17
+ imageDataByUrl: Record<string, Uint8Array | string> = {}
17
18
  ): Promise<Blob> {
18
19
  return new Promise((resolve) => {
19
- let pdf = createDocument(pdfKit, options, doc);
20
+ let pdf = createDocument(pdfKit, options, doc, imageDataByUrl);
20
21
  const buffers = Array<BlobPart>();
21
22
  pdf.on("data", buffers.push.bind(buffers));
22
23
  pdf.on("end", () => resolve(new Blob(buffers, { type: "application/pdf" })));
@@ -35,16 +36,18 @@ export function exportToStream(
35
36
  pdfKit: PDFKit.PDFDocument,
36
37
  blobStream: any,
37
38
  doc: AD.AbstractDoc.AbstractDoc,
38
- options: PdfExportOptions = { compress: false }
39
+ options: PdfExportOptions = { compress: false },
40
+ imageDataByUrl: Record<string, Uint8Array | string> = {}
39
41
  ): void {
40
- let pdf = createDocument(pdfKit, options, doc);
42
+ let pdf = createDocument(pdfKit, options, doc, imageDataByUrl);
41
43
  pdf.pipe(blobStream);
42
44
  }
43
45
 
44
46
  function createDocument(
45
47
  pdfKit: PDFKit.PDFDocument,
46
48
  options: PdfExportOptions,
47
- ad: AD.AbstractDoc.AbstractDoc
49
+ ad: AD.AbstractDoc.AbstractDoc,
50
+ imageDataByUrl: Record<string, Uint8Array | string>
48
51
  ): PDFKit.PDFDocument {
49
52
  const pdf = new pdfKit({ ...options, autoFirstPage: false, bufferPages: true });
50
53
 
@@ -56,7 +59,7 @@ function createDocument(
56
59
  const pageDesiredSizes = measurePages(pdfKit, document, updatedPages);
57
60
 
58
61
  for (let page of updatedPages) {
59
- renderPage(document, pdf, pageDesiredSizes, page);
62
+ renderPage(document, pdf, pageDesiredSizes, page, imageDataByUrl);
60
63
  }
61
64
  pdf.end();
62
65
  return pdf;
@@ -66,7 +69,8 @@ function renderPage(
66
69
  parentResources: AD.Resources.Resources,
67
70
  pdfKit: PDFKit.PDFDocument,
68
71
  desiredSizes: Map<{}, AD.Size.Size>,
69
- page: Page
72
+ page: Page,
73
+ imageDataByUrl: Record<string, Uint8Array | string>
70
74
  ): void {
71
75
  const section = page.section;
72
76
  const style = section.page.style;
@@ -91,7 +95,8 @@ function renderPage(
91
95
  pdfKit,
92
96
  desiredSizes,
93
97
  AD.Rect.create(headerX, isAbsolute ? headerStart : headerY, elementSize.width, elementSize.height),
94
- element
98
+ element,
99
+ imageDataByUrl
95
100
  );
96
101
  if (!isAbsolute) {
97
102
  headerY += elementSize.height;
@@ -114,7 +119,8 @@ function renderPage(
114
119
  pdfKit,
115
120
  desiredSizes,
116
121
  AD.Rect.create(footerX, isAbsolute ? footerStart : footerY, elementSize.width, elementSize.height),
117
- element
122
+ element,
123
+ imageDataByUrl
118
124
  );
119
125
  if (!isAbsolute) {
120
126
  footerY += elementSize.height;
@@ -131,7 +137,8 @@ function renderPage(
131
137
  pdfKit,
132
138
  desiredSizes,
133
139
  AD.Rect.create(contentRect.x, isAbsolute ? elementStart : y, elementSize.width, elementSize.height),
134
- element
140
+ element,
141
+ imageDataByUrl
135
142
  );
136
143
  if (!isAbsolute) {
137
144
  y += elementSize.height;
@@ -160,18 +167,19 @@ function renderSectionElement(
160
167
  pdf: PDFKit.PDFDocument,
161
168
  desiredSizes: Map<{}, AD.Size.Size>,
162
169
  finalRect: AD.Rect.Rect,
163
- element: AD.SectionElement.SectionElement
170
+ element: AD.SectionElement.SectionElement,
171
+ imageDataByUrl: Record<string, Uint8Array | string>
164
172
  ): void {
165
173
  const resources = AD.Resources.mergeResources([parentResources, element]);
166
174
  switch (element.type) {
167
175
  case "Paragraph":
168
- renderParagraph(resources, pdf, desiredSizes, finalRect, element);
176
+ renderParagraph(resources, pdf, desiredSizes, finalRect, element, imageDataByUrl);
169
177
  return;
170
178
  case "Table":
171
- renderTable(resources, pdf, desiredSizes, finalRect, element);
179
+ renderTable(resources, pdf, desiredSizes, finalRect, element, imageDataByUrl);
172
180
  return;
173
181
  case "Group":
174
- renderGroup(resources, pdf, desiredSizes, finalRect, element);
182
+ renderGroup(resources, pdf, desiredSizes, finalRect, element, imageDataByUrl);
175
183
  return;
176
184
  }
177
185
  }
@@ -181,7 +189,8 @@ function renderGroup(
181
189
  pdfKit: PDFKit.PDFDocument,
182
190
  desiredSizes: Map<{}, AD.Size.Size>,
183
191
  finalRect: AD.Rect.Rect,
184
- group: AD.Group.Group
192
+ group: AD.Group.Group,
193
+ imageDataByUrl: Record<string, Uint8Array | string>
185
194
  ): void {
186
195
  const finalX = finalRect.x + group.style.margins.left;
187
196
  const startY = finalRect.y + group.style.margins.top;
@@ -194,7 +203,8 @@ function renderGroup(
194
203
  pdfKit,
195
204
  desiredSizes,
196
205
  AD.Rect.create(finalX, isAbsolute ? startY : y, elementSize.width, elementSize.height),
197
- element
206
+ element,
207
+ imageDataByUrl
198
208
  );
199
209
  if (!isAbsolute) {
200
210
  y += elementSize.height;
@@ -207,7 +217,8 @@ function renderParagraph(
207
217
  pdfKit: PDFKit.PDFDocument,
208
218
  desiredSizes: Map<{}, AD.Size.Size>,
209
219
  finalRect: AD.Rect.Rect,
210
- paragraph: AD.Paragraph.Paragraph
220
+ paragraph: AD.Paragraph.Paragraph,
221
+ imageDataByUrl: Record<string, Uint8Array | string>
211
222
  ): void {
212
223
  const style = AD.Resources.getStyle(
213
224
  undefined,
@@ -321,7 +332,8 @@ function renderParagraph(
321
332
  parseAlignment(style.alignment),
322
333
  availableWidth,
323
334
  i === 0,
324
- i === lastIndex
335
+ i === lastIndex,
336
+ imageDataByUrl
325
337
  );
326
338
 
327
339
  x += atomSize.width;
@@ -356,7 +368,8 @@ function renderAtom(
356
368
  alignment: AD.TextStyle.TextAlignment,
357
369
  availableWidth: number,
358
370
  isFirstAtom: boolean,
359
- isLastAtom: boolean
371
+ isLastAtom: boolean,
372
+ imageDataByUrl: Record<string, Uint8Array | string>
360
373
  ): void {
361
374
  switch (atom.type) {
362
375
  case "TextField":
@@ -376,7 +389,7 @@ function renderAtom(
376
389
  renderTextRun(resources, pdfKit, finalRect, textStyle, atom, alignment, isFirstAtom, isLastAtom, availableWidth);
377
390
  return;
378
391
  case "Image":
379
- renderImage(resources, pdfKit, finalRect, textStyle, atom);
392
+ renderImage(resources, pdfKit, finalRect, textStyle, atom, imageDataByUrl);
380
393
  return;
381
394
  case "HyperLink":
382
395
  renderHyperLink(
@@ -669,7 +682,8 @@ function renderTable(
669
682
  pdf: PDFKit.PDFDocument,
670
683
  desiredSizes: Map<{}, AD.Size.Size>,
671
684
  finalRect: AD.Rect.Rect,
672
- table: AD.Table.Table
685
+ table: AD.Table.Table,
686
+ imageDataByUrl: Record<string, Uint8Array | string>
673
687
  ): void {
674
688
  const style = AD.Resources.getStyle(
675
689
  undefined,
@@ -689,7 +703,19 @@ function renderTable(
689
703
  const rowRect = AD.Rect.create(x, y, rowSize.width, rowSize.height);
690
704
  const isTop = index === 0;
691
705
  const isBottom = index === rows.length - 1;
692
- renderRow(resources, pdf, desiredSizes, rowRect, style.cellStyle, table, row, index, isTop, isBottom);
706
+ renderRow(
707
+ resources,
708
+ pdf,
709
+ desiredSizes,
710
+ rowRect,
711
+ style.cellStyle,
712
+ table,
713
+ row,
714
+ index,
715
+ isTop,
716
+ isBottom,
717
+ imageDataByUrl
718
+ );
693
719
  y += rowSize.height;
694
720
  }
695
721
  }
@@ -704,7 +730,8 @@ function renderRow(
704
730
  row: AD.TableRow.TableRow,
705
731
  rowIndex: number,
706
732
  isTop: boolean,
707
- isBottom: boolean
733
+ isBottom: boolean,
734
+ imageDataByUrl: Record<string, Uint8Array | string>
708
735
  ): void {
709
736
  let x = finalRect.x;
710
737
  const rowSize = getDesiredSize(row, desiredSizes);
@@ -725,7 +752,19 @@ function renderRow(
725
752
  const cellRect = AD.Rect.create(x, finalRect.y, cellSize.width, height);
726
753
  const isFirst = cellIndex === 0;
727
754
  const isLast = cellIndex === row.children.length - 1;
728
- renderCell(resources, pdf, desiredSizes, cellRect, tableCellStyle, cell, isFirst, isLast, isTop, isBottom);
755
+ renderCell(
756
+ resources,
757
+ pdf,
758
+ desiredSizes,
759
+ cellRect,
760
+ tableCellStyle,
761
+ cell,
762
+ isFirst,
763
+ isLast,
764
+ isTop,
765
+ isBottom,
766
+ imageDataByUrl
767
+ );
729
768
  x += cellSize.width;
730
769
  }
731
770
  }
@@ -740,7 +779,8 @@ function renderCell(
740
779
  isFirst: boolean,
741
780
  isLast: boolean,
742
781
  isTop: boolean,
743
- isBottom: boolean
782
+ isBottom: boolean,
783
+ imageDataByUrl: Record<string, Uint8Array | string>
744
784
  ): void {
745
785
  const style = AD.Resources.getStyle(
746
786
  tableCellStyle,
@@ -769,7 +809,7 @@ function renderCell(
769
809
  const elementSize = getDesiredSize(element, desiredSizes);
770
810
  const isAbsolute = AD.Position.isPositionAbsolute(element);
771
811
  const elementRect = AD.Rect.create(x, isAbsolute ? startY : y, elementSize.width, elementSize.height);
772
- renderSectionElement(resources, pdf, desiredSizes, elementRect, element);
812
+ renderSectionElement(resources, pdf, desiredSizes, elementRect, element, imageDataByUrl);
773
813
  if (!isAbsolute) {
774
814
  y += elementSize.height;
775
815
  }