abstract-document 17.0.7 → 17.0.8

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 (35) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/abstract-document/styles/page-style.d.ts +9 -0
  3. package/lib/abstract-document/styles/page-style.d.ts.map +1 -1
  4. package/lib/abstract-document/styles/page-style.js +5 -1
  5. package/lib/abstract-document/styles/page-style.js.map +1 -1
  6. package/lib/abstract-document-exporters/pdf/measure.d.ts.map +1 -1
  7. package/lib/abstract-document-exporters/pdf/measure.js +4 -2
  8. package/lib/abstract-document-exporters/pdf/measure.js.map +1 -1
  9. package/lib/abstract-document-exporters/pdf/paginate.d.ts +4 -1
  10. package/lib/abstract-document-exporters/pdf/paginate.d.ts.map +1 -1
  11. package/lib/abstract-document-exporters/pdf/paginate.js +43 -23
  12. package/lib/abstract-document-exporters/pdf/paginate.js.map +1 -1
  13. package/lib/abstract-document-exporters/pdf/pre-process.js +1 -1
  14. package/lib/abstract-document-exporters/pdf/pre-process.js.map +1 -1
  15. package/lib/abstract-document-exporters/pdf/render.js +11 -7
  16. package/lib/abstract-document-exporters/pdf/render.js.map +1 -1
  17. package/lib/abstract-document-exporters/pdf/update-refs.js +2 -2
  18. package/lib/abstract-document-exporters/pdf/update-refs.js.map +1 -1
  19. package/lib/abstract-document-xml/xsd-template/styles.d.ts +2 -1
  20. package/lib/abstract-document-xml/xsd-template/styles.d.ts.map +1 -1
  21. package/lib/abstract-document-xml/xsd-template/styles.js +5 -0
  22. package/lib/abstract-document-xml/xsd-template/styles.js.map +1 -1
  23. package/lib/abstract-document-xml/xsd-template/xsd-template.d.ts +2 -2
  24. package/lib/abstract-document-xml/xsd-template/xsd-template.d.ts.map +1 -1
  25. package/lib/abstract-document-xml/xsd-template/xsd-template.js +1 -0
  26. package/lib/abstract-document-xml/xsd-template/xsd-template.js.map +1 -1
  27. package/package.json +3 -3
  28. package/src/abstract-document/styles/page-style.ts +16 -0
  29. package/src/abstract-document-exporters/pdf/measure.ts +9 -2
  30. package/src/abstract-document-exporters/pdf/paginate.ts +67 -32
  31. package/src/abstract-document-exporters/pdf/pre-process.ts +1 -1
  32. package/src/abstract-document-exporters/pdf/render.ts +40 -45
  33. package/src/abstract-document-exporters/pdf/update-refs.ts +2 -2
  34. package/src/abstract-document-xml/xsd-template/styles.ts +6 -0
  35. package/src/abstract-document-xml/xsd-template/xsd-template.ts +1 -0
@@ -3,13 +3,17 @@ import { getResources } from "../shared/get_resources.js";
3
3
  import { registerFonts } from "./font.js";
4
4
  import { measureTable } from "./measure.js";
5
5
 
6
+ export interface PageColumn {
7
+ readonly elements: ReadonlyArray<AD.SectionElement.SectionElement>;
8
+ }
9
+
6
10
  export interface Page {
7
11
  readonly pageNo: number;
8
12
  readonly namedDestionations: ReadonlyArray<string>;
9
13
  readonly pageOptions: any;
10
14
  readonly section: AD.Section.Section;
11
15
  readonly contentRect: AD.Rect.Rect;
12
- readonly elements: ReadonlyArray<AD.SectionElement.SectionElement>;
16
+ readonly columns: ReadonlyArray<PageColumn>;
13
17
  readonly header: ReadonlyArray<AD.SectionElement.SectionElement>;
14
18
  readonly footer: ReadonlyArray<AD.SectionElement.SectionElement>;
15
19
  }
@@ -26,13 +30,13 @@ export function paginate(
26
30
  const pages = new Array<Page>();
27
31
  for (let section of document.children) {
28
32
  const previousPage = pages.length > 0 ? pages[pages.length - 1] : undefined;
29
- pages.push(...splitSection(pdfKit, document, resources, desiredSizes, previousPage, section));
33
+ pages.push(...paginateSection(pdfKit, document, resources, desiredSizes, previousPage, section));
30
34
  }
31
35
 
32
36
  return pages;
33
37
  }
34
38
 
35
- function splitSection(
39
+ function paginateSection(
36
40
  pdfKit: PDFKit.PDFDocument,
37
41
  document: AD.AbstractDoc.AbstractDoc,
38
42
  parentResources: AD.Resources.Resources,
@@ -44,6 +48,7 @@ function splitSection(
44
48
  const pages = new Array<Page>();
45
49
 
46
50
  let children = section.children;
51
+ let columns = new Array<PageColumn>();
47
52
  let elements = new Array<AD.SectionElement.SectionElement>();
48
53
  let elementsHeight = 0;
49
54
  let currentPage = previousPage;
@@ -51,8 +56,10 @@ function splitSection(
51
56
  const contentRect = getPageContentRect(desiredSizes, section, pages.length + 1);
52
57
  const element = children[i];
53
58
  if (element.type === "PageBreak") {
54
- currentPage = createPage(resources, desiredSizes, currentPage, section, elements, pages.length === 0);
59
+ columns.push({ elements });
60
+ currentPage = createPage(resources, desiredSizes, currentPage, section, columns, pages.length === 0);
55
61
  pages.push(currentPage);
62
+ columns = [];
56
63
  elements = [];
57
64
  elementsHeight = 0;
58
65
  continue;
@@ -103,28 +110,26 @@ function splitSection(
103
110
 
104
111
  //Add split table to children to process tableTail in next iteration
105
112
  children = [...children.slice(0, i), ...tableSplit, ...children.slice(i + 1)];
106
-
107
- currentPage = createPage(resources, desiredSizes, currentPage, section, elements, pages.length === 0);
108
- pages.push(currentPage);
109
- elements = [];
110
- elementsHeight = 0;
111
-
112
- continue;
113
- }
114
-
115
- if (elements.length > 1) {
113
+ } else if (elements.length > 1) {
116
114
  elements.pop();
117
115
  i--;
118
116
  }
119
- currentPage = createPage(resources, desiredSizes, currentPage, section, elements, pages.length === 0);
120
- pages.push(currentPage);
117
+
118
+ columns.push({ elements });
121
119
  elements = [];
122
120
  elementsHeight = 0;
121
+
122
+ if (columns.length === section.page.style.columnLayout.columnCount) {
123
+ currentPage = createPage(resources, desiredSizes, currentPage, section, columns, pages.length === 0);
124
+ pages.push(currentPage);
125
+ columns = [];
126
+ }
123
127
  }
124
128
  }
125
129
 
126
130
  if (elements.length > 0) {
127
- pages.push(createPage(resources, desiredSizes, currentPage, section, elements, pages.length === 0));
131
+ columns.push({ elements });
132
+ pages.push(createPage(resources, desiredSizes, currentPage, section, columns, pages.length === 0));
128
133
  }
129
134
 
130
135
  return pages;
@@ -191,7 +196,7 @@ function createPage(
191
196
  desiredSizes: Map<{}, AD.Size.Size>,
192
197
  previousPage: Page | undefined,
193
198
  section: AD.Section.Section,
194
- elements: ReadonlyArray<AD.SectionElement.SectionElement>,
199
+ columns: ReadonlyArray<PageColumn>,
195
200
  isFirst: boolean
196
201
  ): Page {
197
202
  const style = section.page.style;
@@ -210,33 +215,55 @@ function createPage(
210
215
  };
211
216
  const pageNo = previousPage ? previousPage.pageNo + 1 : 1;
212
217
 
218
+ const namedDestionations = [];
219
+
213
220
  const sectionName = isFirst && section.id !== "" ? [section.id] : [];
221
+ namedDestionations.push(...sectionName);
222
+
214
223
  // For now, only support link targets at base level. Tree search would be needed to find all targets.
215
- const targetNames = elements
216
- .flatMap((e) => (e.type === "Paragraph" ? e.children.map((c) => (c.type === "LinkTarget" ? c.name : "")) : []))
217
- .filter((t) => t !== "");
218
- const namedDestionations = [...sectionName, ...targetNames];
224
+ for (const { elements } of columns) {
225
+ const targetNames = elements
226
+ .flatMap((e) => (e.type === "Paragraph" ? e.children.map((c) => (c.type === "LinkTarget" ? c.name : "")) : []))
227
+ .filter((t) => t !== "");
228
+ namedDestionations.push(...targetNames);
229
+ }
219
230
 
220
231
  // Ignore leading space by expanding the content rect upwards
221
232
  const rect = getPageContentRect(desiredSizes, section, pageNo);
222
- const [leadingSpace] = getLeadingAndTrailingSpace(resources, section, elements);
233
+ let leadingSpace = undefined;
234
+ for (const { elements } of columns) {
235
+ const [columnLeadingSpace] = getLeadingAndTrailingSpace(resources, section, elements);
236
+ leadingSpace = Math.min(leadingSpace ?? columnLeadingSpace, columnLeadingSpace);
237
+ }
238
+ leadingSpace ||= 0;
223
239
  const contentRect = AD.Rect.create(rect.x, rect.y - leadingSpace, rect.width, rect.height + leadingSpace);
224
240
 
225
- const frontHeader = (section.page.frontHeader === undefined || section.page.frontHeader.length === 0) ? section.page.header : section.page.frontHeader;
226
- const frontFooter = (section.page.frontFooter === undefined || section.page.frontFooter.length === 0) ? section.page.footer : section.page.frontFooter;
241
+ const frontHeader =
242
+ section.page.frontHeader === undefined || section.page.frontHeader.length === 0
243
+ ? section.page.header
244
+ : section.page.frontHeader;
245
+
246
+ const frontFooter =
247
+ section.page.frontFooter === undefined || section.page.frontFooter.length === 0
248
+ ? section.page.footer
249
+ : section.page.frontFooter;
250
+
227
251
  return {
228
252
  pageNo: pageNo,
229
253
  namedDestionations: namedDestionations,
230
254
  pageOptions: pageOptions,
231
255
  section: section,
232
256
  contentRect: contentRect,
233
- elements: elements,
257
+ columns: columns,
234
258
  header: isFirst ? frontHeader : section.page.header,
235
259
  footer: isFirst ? frontFooter : section.page.footer,
236
260
  };
237
261
  }
238
262
 
239
- export function getHeaderAndFooter(section: AD.Section.Section, pageNo: number): {
263
+ export function getHeaderAndFooter(
264
+ section: AD.Section.Section,
265
+ pageNo: number
266
+ ): {
240
267
  readonly header: Array<AD.SectionElement.SectionElement>;
241
268
  readonly footer: Array<AD.SectionElement.SectionElement>;
242
269
  readonly headerMargins: AD.LayoutFoundation.LayoutFoundation;
@@ -245,7 +272,7 @@ export function getHeaderAndFooter(section: AD.Section.Section, pageNo: number):
245
272
  const FIRST_PAGE = 1;
246
273
  const EVEN_PAGE = 0;
247
274
  const ODD_PAGE = 1;
248
- switch(true) {
275
+ switch (true) {
249
276
  //first page
250
277
  case pageNo === FIRST_PAGE: {
251
278
  const normalHeader = section.page.frontHeader === undefined || section.page.frontHeader.length === 0;
@@ -253,8 +280,12 @@ export function getHeaderAndFooter(section: AD.Section.Section, pageNo: number):
253
280
  return {
254
281
  footer: normalFooter ? section.page.footer : section.page.frontFooter,
255
282
  header: normalHeader ? section.page.header : section.page.frontHeader,
256
- headerMargins: normalHeader ? section.page.style.headerMargins : (section.page.style.firstPageHeaderMargins ?? section.page.style.headerMargins),
257
- footerMargins: normalFooter ? section.page.style.footerMargins : (section.page.style.firstPageFooterMargins ?? section.page.style.footerMargins),
283
+ headerMargins: normalHeader
284
+ ? section.page.style.headerMargins
285
+ : section.page.style.firstPageHeaderMargins ?? section.page.style.headerMargins,
286
+ footerMargins: normalFooter
287
+ ? section.page.style.footerMargins
288
+ : section.page.style.firstPageFooterMargins ?? section.page.style.footerMargins,
258
289
  };
259
290
  }
260
291
  case pageNo === 0:
@@ -266,12 +297,16 @@ export function getHeaderAndFooter(section: AD.Section.Section, pageNo: number):
266
297
  footer: section.page.footer,
267
298
  headerMargins: section.page.style.headerMargins,
268
299
  footerMargins: section.page.style.footerMargins,
269
- }
300
+ };
270
301
  }
271
302
  }
272
303
  }
273
304
 
274
- function getPageContentRect(desiredSizes: Map<{}, AD.Size.Size>, section: AD.Section.Section, pageNo: number): AD.Rect.Rect {
305
+ function getPageContentRect(
306
+ desiredSizes: Map<{}, AD.Size.Size>,
307
+ section: AD.Section.Section,
308
+ pageNo: number
309
+ ): AD.Rect.Rect {
275
310
  const style = section.page.style;
276
311
  const pageWidth = AD.PageStyle.getWidth(style);
277
312
  const pageHeight = AD.PageStyle.getHeight(style);
@@ -55,7 +55,7 @@ function preProcessSection(s: AD.Section.Section, parentResources: AD.Resources.
55
55
  const frontHeader = (s.page.frontHeader ?? []).flatMap((e) => preProcessSectionElement(e, resources));
56
56
  const frontFooter = (s.page.frontFooter ?? []).flatMap((e) => preProcessSectionElement(e, resources));
57
57
  const page = AD.MasterPage.create({
58
- style: s.page.style,
58
+ style: AD.PageStyle.create(s.page.style),
59
59
  header: header,
60
60
  footer: footer,
61
61
  frontHeader: frontHeader,
@@ -124,19 +124,24 @@ function renderPage(
124
124
  }
125
125
 
126
126
  const elementStart = contentRect.y;
127
- let y = elementStart;
128
- for (const element of page.elements) {
129
- const elementSize = getDesiredSize(element, desiredSizes);
130
- const isAbsolute = AD.Position.isPositionAbsolute(element);
131
- renderSectionElement(
132
- resources,
133
- pdfKit,
134
- desiredSizes,
135
- AD.Rect.create(contentRect.x, isAbsolute ? elementStart : y, elementSize.width, elementSize.height),
136
- element
137
- );
138
- if (!isAbsolute) {
139
- y += elementSize.height;
127
+ const columnStep =
128
+ contentRect.width / section.page.style.columnLayout.columnCount + section.page.style.columnLayout.columnGap;
129
+ for (let columnIndex = 0; columnIndex < page.columns.length; columnIndex++) {
130
+ const x = contentRect.x + columnStep * columnIndex;
131
+ let y = elementStart;
132
+ for (const element of page.columns[columnIndex].elements) {
133
+ const elementSize = getDesiredSize(element, desiredSizes);
134
+ const isAbsolute = AD.Position.isPositionAbsolute(element);
135
+ renderSectionElement(
136
+ resources,
137
+ pdfKit,
138
+ desiredSizes,
139
+ AD.Rect.create(x, isAbsolute ? elementStart : y, elementSize.width, elementSize.height),
140
+ element
141
+ );
142
+ if (!isAbsolute) {
143
+ y += elementSize.height;
144
+ }
140
145
  }
141
146
  }
142
147
  }
@@ -284,14 +289,21 @@ function renderParagraph(
284
289
  let y = finalRect.y + style.margins.top;
285
290
  const alignment = parseAlignment(style.alignment);
286
291
  const newRows = rowsSplit(rows, availableWidth, desiredSizes, alignment);
287
- const { newDesiredSizes, combinedRows } = rowsCombineTextRuns(resources, pdfKit, newRows, desiredSizes, alignment, style.textStyle);
292
+ const { newDesiredSizes, combinedRows } = rowsCombineTextRuns(
293
+ resources,
294
+ pdfKit,
295
+ newRows,
296
+ desiredSizes,
297
+ alignment,
298
+ style.textStyle
299
+ );
288
300
  for (let r = 0; r < combinedRows.length; r++) {
289
301
  const row = combinedRows[r];
290
302
  const isLast = r === combinedRows.length - 1;
291
303
  if (row.length === 0) {
292
304
  continue;
293
305
  }
294
-
306
+
295
307
  const rowWidth = row.reduce((a, b) => a + getDesiredSize(b, newDesiredSizes).width, 0);
296
308
  const remainingWidth = availableWidth - rowWidth;
297
309
  const justificationWidth = !isLast && row.length > 1 ? remainingWidth / (row.length - 1) : 0;
@@ -368,16 +380,7 @@ function renderAtom(
368
380
  ): void {
369
381
  switch (atom.type) {
370
382
  case "TextField":
371
- renderTextField(
372
- resources,
373
- pdfKit,
374
- finalRect,
375
- textStyle,
376
- atom,
377
- alignment,
378
- isFirstAtom,
379
- isLastAtom,
380
- );
383
+ renderTextField(resources, pdfKit, finalRect, textStyle, atom, alignment, isFirstAtom, isLastAtom);
381
384
  return;
382
385
  case "TextRun":
383
386
  renderTextRun(resources, pdfKit, finalRect, textStyle, atom, alignment, isFirstAtom, isLastAtom);
@@ -416,7 +419,7 @@ function renderTextField(
416
419
  textField: AD.TextField.TextField,
417
420
  alignment: PdfKitAlignment,
418
421
  isFirstAtom: boolean,
419
- isLastAtom: boolean,
422
+ isLastAtom: boolean
420
423
  ): void {
421
424
  const style = AD.Resources.getStyle(
422
425
  textStyle,
@@ -427,15 +430,7 @@ function renderTextField(
427
430
  ) as AD.TextStyle.TextStyle;
428
431
  switch (textField.fieldType) {
429
432
  case "Date":
430
- drawText(
431
- pdfKit,
432
- finalRect,
433
- style,
434
- new Date(Date.now()).toDateString(),
435
- alignment,
436
- isFirstAtom,
437
- isLastAtom,
438
- );
433
+ drawText(pdfKit, finalRect, style, new Date(Date.now()).toDateString(), alignment, isFirstAtom, isLastAtom);
439
434
  return;
440
435
  case "PageNumber":
441
436
  case "TotalPages":
@@ -455,7 +450,7 @@ function renderTextRun(
455
450
  textRun: AD.TextRun.TextRun,
456
451
  alignment: PdfKitAlignment,
457
452
  isFirstAtom: boolean,
458
- isLastAtom: boolean,
453
+ isLastAtom: boolean
459
454
  ): void {
460
455
  const style = AD.Resources.getNestedStyle(
461
456
  textStyle,
@@ -625,7 +620,7 @@ function drawText(
625
620
  text: string,
626
621
  alignment: PdfKitAlignment,
627
622
  isFirst: boolean,
628
- isEnd: boolean,
623
+ isEnd: boolean
629
624
  ): void {
630
625
  const font = getFontNameStyle(textStyle);
631
626
  const fontSize = AD.TextStyle.calculateFontSize(textStyle, 10);
@@ -635,7 +630,7 @@ function drawText(
635
630
  .fillColor(textStyle.color || "black", textStyle.opacity ?? 1.0);
636
631
  applyTextOffset(pdf, textStyle);
637
632
 
638
- switch(alignment) {
633
+ switch (alignment) {
639
634
  case "justify": {
640
635
  pdf.text(text, finalRect.x, finalRect.y, {
641
636
  width: Infinity,
@@ -644,14 +639,14 @@ function drawText(
644
639
  indent: textStyle.indent || 0,
645
640
  baseline: textStyle.baseline || "top",
646
641
  strike: textStyle.strike,
647
- ...(textStyle.characterSpacing !== undefined ? { characterSpacing: textStyle.characterSpacing } : {}),
648
- ...(textStyle.lineGap !== undefined ? { lineGap: textStyle.lineGap } : {}),
642
+ ...(textStyle.characterSpacing !== undefined ? { characterSpacing: textStyle.characterSpacing } : {}),
643
+ ...(textStyle.lineGap !== undefined ? { lineGap: textStyle.lineGap } : {}),
649
644
  });
650
645
  break;
651
646
  }
652
647
 
653
648
  default: {
654
- if(isFirst) {
649
+ if (isFirst) {
655
650
  pdf.text(text, finalRect.x, finalRect.y, {
656
651
  width: Infinity,
657
652
  underline: textStyle.underline || false,
@@ -659,8 +654,8 @@ function drawText(
659
654
  indent: textStyle.indent || 0,
660
655
  baseline: textStyle.baseline || "top",
661
656
  strike: textStyle.strike,
662
- ...(textStyle.characterSpacing !== undefined ? { characterSpacing: textStyle.characterSpacing } : {}),
663
- ...(textStyle.lineGap !== undefined ? { lineGap: textStyle.lineGap } : {}),
657
+ ...(textStyle.characterSpacing !== undefined ? { characterSpacing: textStyle.characterSpacing } : {}),
658
+ ...(textStyle.lineGap !== undefined ? { lineGap: textStyle.lineGap } : {}),
664
659
  });
665
660
  } else {
666
661
  pdf.text(text, {
@@ -670,8 +665,8 @@ function drawText(
670
665
  indent: textStyle.indent || 0,
671
666
  baseline: textStyle.baseline || "top",
672
667
  strike: textStyle.strike,
673
- ...(textStyle.characterSpacing !== undefined ? { characterSpacing: textStyle.characterSpacing } : {}),
674
- ...(textStyle.lineGap !== undefined ? { lineGap: textStyle.lineGap } : {}),
668
+ ...(textStyle.characterSpacing !== undefined ? { characterSpacing: textStyle.characterSpacing } : {}),
669
+ ...(textStyle.lineGap !== undefined ? { lineGap: textStyle.lineGap } : {}),
675
670
  });
676
671
  }
677
672
  break;
@@ -6,12 +6,12 @@ export function updatePageRefs(pages: ReadonlyArray<Page>): Array<Page> {
6
6
  }
7
7
 
8
8
  export function updateRefsOnPage(page: Page, pages: ReadonlyArray<Page>): Page {
9
- const updatedElements = updateRefsInElements(page.elements, page, pages);
9
+ const updatedColumns = page.columns.map((c) => ({ ...c, elements: updateRefsInElements(c.elements, page, pages) }));
10
10
  const updatedHeader = updateRefsInElements(page.header, page, pages);
11
11
  const updatedFooter = updateRefsInElements(page.footer, page, pages);
12
12
  return {
13
13
  ...page,
14
- elements: updatedElements,
14
+ columns: updatedColumns,
15
15
  header: updatedHeader,
16
16
  footer: updatedFooter,
17
17
  };
@@ -237,6 +237,7 @@ export const masterPageStyle = `<xs:complexType name="MasterPageStyle">
237
237
  <xs:element name="firstPageHeaderMargins" type="LayoutFoundation" minOccurs="0" />
238
238
  <xs:element name="firstPageFooterMargins" type="LayoutFoundation" minOccurs="0" />
239
239
  <xs:element name="contentMargins" type="LayoutFoundation" />
240
+ <xs:element name="columnLayout" type="PageColumnLayout" minOccurs="0" maxOccurs="1" />
240
241
  </xs:all>
241
242
  <xs:attribute name="paperSize" use="required">
242
243
  <xs:simpleType>
@@ -276,3 +277,8 @@ export const layoutFoundation = `<xs:complexType name="LayoutFoundation">
276
277
  <xs:attribute name="left" type="xs:decimal" />
277
278
  <xs:attribute name="right" type="xs:decimal" />
278
279
  </xs:complexType>`;
280
+
281
+ export const pageColumnLayout = `<xs:complexType name="PageColumnLayout">
282
+ <xs:attribute name="columnCount" type="xs:decimal" />
283
+ <xs:attribute name="columnGap" type="xs:decimal" />
284
+ </xs:complexType>`;
@@ -4,6 +4,7 @@ import * as CustomElements from "./custom-elements.js";
4
4
  import { parseXsd } from "handlebars-xml";
5
5
 
6
6
  const commonParts = `${Styles.layoutFoundation}
7
+ ${Styles.pageColumnLayout}
7
8
  ${Elements.section}
8
9
  ${Elements.sectionElement}
9
10
  ${Elements.headerFooter}