@softwear/latestcollectioncore 1.0.176 → 1.0.177

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/pdf.d.ts CHANGED
@@ -54,6 +54,8 @@ export interface ContainerLayoutObject extends BaseLayoutObject {
54
54
  minHeightBeforeBreak?: number;
55
55
  /** When true, container and children are drawn only once at the end of the report, not on every page */
56
56
  printOnlyAtEnd?: boolean;
57
+ /** When true, container and children are drawn only once before other body containers (first page header block) */
58
+ printOnlyAtStart?: boolean;
57
59
  }
58
60
  export type LayoutObject = TextLayoutObject | FieldLayoutObject | RectangleLayoutObject | ImageLayoutObject | ContainerLayoutObject;
59
61
  export interface Layout {
package/dist/reports.js CHANGED
@@ -43,6 +43,12 @@ function getProperty(propertyName, object) {
43
43
  }
44
44
  return property;
45
45
  }
46
+ /** Repeat once over the current buffer when the container is only a visual group (no `source` binding). */
47
+ function containerLoopSource(container, printBuffer) {
48
+ if (!container.source || container.source === '')
49
+ return [printBuffer];
50
+ return getProperty(container.source, printBuffer);
51
+ }
46
52
  /** jsPDF font style strings. fontStyle bitmask: 0=normal, 1=bold, 2=italic, 3=bold+italic */
47
53
  const FONT_STYLES = ['normal', 'bold', 'italic', 'bolditalic'];
48
54
  /** Recursively collect used custom fonts from layout objects (text/field with fontFamily) and layout default. */
@@ -160,7 +166,7 @@ function collectImageUrlsFromLayout(layout, printBuffer) {
160
166
  }
161
167
  if (object.type !== 'container')
162
168
  return;
163
- const source = object.printOnlyAtEnd === true && (!object.source || object.source === '') ? [currentPrintBuffer] : getProperty(object.source, currentPrintBuffer);
169
+ const source = containerLoopSource(object, currentPrintBuffer);
164
170
  if (!Array.isArray(source))
165
171
  return;
166
172
  source.forEach((containerPrintBuffer) => walk(object.children, containerPrintBuffer));
@@ -450,8 +456,7 @@ addObjectToPDF = function (originX, originY, doc, object, printBuffer, paperSize
450
456
  // Recursively draw all child objects from a container object
451
457
  if (object.type == 'container') {
452
458
  let originX = x * 10, originY = y * 10;
453
- const isPrintOnlyAtEnd = object.printOnlyAtEnd === true;
454
- const source = isPrintOnlyAtEnd && (!object.source || object.source === '') ? [printBuffer] : getProperty(object.source, printBuffer);
459
+ const source = containerLoopSource(object, printBuffer);
455
460
  if (source === undefined) {
456
461
  (_a = options.onAlert) === null || _a === void 0 ? void 0 : _a.call(options, { header: 'containernotfound', body: object.source, type: 'warning', timeout: 10000 });
457
462
  return absoluteLowerRightHandSide;
@@ -657,9 +662,16 @@ function genPDF(layout, printBuffer, options = {}) {
657
662
  yield embedFontsInDoc(measurementDoc, usedFonts, options);
658
663
  }
659
664
  drawStaticPartOfPage(measurementDoc, layout, printBuffer, paperSize, options, measurementContext);
660
- // Draw containers in measurement mode (same order as render: normal first, then printOnlyAtEnd on fresh page)
665
+ // Draw containers in measurement mode (same order as render)
666
+ layout.objects
667
+ .filter((object) => object.type == 'container' && object.printOnlyAtStart)
668
+ .forEach((object) => {
669
+ addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
670
+ });
661
671
  layout.objects
662
- .filter((object) => object.type == 'container' && !object.printOnlyAtEnd)
672
+ .filter((object) => object.type == 'container' &&
673
+ !object.printOnlyAtEnd &&
674
+ !object.printOnlyAtStart)
663
675
  .forEach((object) => {
664
676
  addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
665
677
  });
@@ -685,9 +697,16 @@ function genPDF(layout, printBuffer, options = {}) {
685
697
  defaultFontFamily: layout.defaultFontFamily,
686
698
  };
687
699
  drawStaticPartOfPage(doc, layout, printBuffer, paperSize, options, renderContext);
688
- // Draw containers in rendering mode (normal containers first)
689
700
  layout.objects
690
- .filter((object) => object.type == 'container' && !object.printOnlyAtEnd)
701
+ .filter((object) => object.type == 'container' && object.printOnlyAtStart)
702
+ .forEach((object) => {
703
+ addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
704
+ });
705
+ // Draw containers in rendering mode (body containers after printOnlyAtStart)
706
+ layout.objects
707
+ .filter((object) => object.type == 'container' &&
708
+ !object.printOnlyAtEnd &&
709
+ !object.printOnlyAtStart)
691
710
  .forEach((object) => {
692
711
  addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
693
712
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwear/latestcollectioncore",
3
- "version": "1.0.176",
3
+ "version": "1.0.177",
4
4
  "description": "Core functions for LatestCollections applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/pdf.ts CHANGED
@@ -61,6 +61,8 @@ export interface ContainerLayoutObject extends BaseLayoutObject {
61
61
  minHeightBeforeBreak?: number
62
62
  /** When true, container and children are drawn only once at the end of the report, not on every page */
63
63
  printOnlyAtEnd?: boolean
64
+ /** When true, container and children are drawn only once before other body containers (first page header block) */
65
+ printOnlyAtStart?: boolean
64
66
  }
65
67
 
66
68
  export type LayoutObject = TextLayoutObject | FieldLayoutObject | RectangleLayoutObject | ImageLayoutObject | ContainerLayoutObject
package/src/reports.ts CHANGED
@@ -92,6 +92,12 @@ function getProperty(propertyName: string, object: any): any {
92
92
  return property
93
93
  }
94
94
 
95
+ /** Repeat once over the current buffer when the container is only a visual group (no `source` binding). */
96
+ function containerLoopSource(container: ContainerLayoutObject, printBuffer: any): any {
97
+ if (!container.source || container.source === '') return [printBuffer]
98
+ return getProperty(container.source, printBuffer)
99
+ }
100
+
95
101
  /** jsPDF font style strings. fontStyle bitmask: 0=normal, 1=bold, 2=italic, 3=bold+italic */
96
102
  const FONT_STYLES = ['normal', 'bold', 'italic', 'bolditalic'] as const
97
103
 
@@ -222,7 +228,7 @@ function collectImageUrlsFromLayout(layout: Layout, printBuffer: any): string[]
222
228
 
223
229
  if (object.type !== 'container') return
224
230
 
225
- const source = (object as any).printOnlyAtEnd === true && (!object.source || object.source === '') ? [currentPrintBuffer] : getProperty(object.source, currentPrintBuffer)
231
+ const source = containerLoopSource(object as ContainerLayoutObject, currentPrintBuffer)
226
232
  if (!Array.isArray(source)) return
227
233
  source.forEach((containerPrintBuffer) => walk(object.children, containerPrintBuffer))
228
234
  })
@@ -539,8 +545,7 @@ addObjectToPDF = function (
539
545
  let originX = x * 10,
540
546
  originY = y * 10
541
547
 
542
- const isPrintOnlyAtEnd = (object as any).printOnlyAtEnd === true
543
- const source = isPrintOnlyAtEnd && (!object.source || object.source === '') ? [printBuffer] : getProperty(object.source, printBuffer)
548
+ const source = containerLoopSource(object as ContainerLayoutObject, printBuffer)
544
549
  if (source === undefined) {
545
550
  options.onAlert?.({ header: 'containernotfound', body: object.source, type: 'warning', timeout: 10000 })
546
551
  return absoluteLowerRightHandSide
@@ -780,14 +785,24 @@ export async function genPDF(layout: Layout, printBuffer: any, options: GenPdfOp
780
785
  }
781
786
  drawStaticPartOfPage(measurementDoc, layout, printBuffer, paperSize, options, measurementContext)
782
787
 
783
- // Draw containers in measurement mode (same order as render: normal first, then printOnlyAtEnd on fresh page)
788
+ // Draw containers in measurement mode (same order as render)
789
+ layout.objects
790
+ .filter((object) => object.type == 'container' && (object as ContainerLayoutObject).printOnlyAtStart)
791
+ .forEach((object) => {
792
+ addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext)
793
+ })
784
794
  layout.objects
785
- .filter((object) => object.type == 'container' && !(object as any).printOnlyAtEnd)
795
+ .filter(
796
+ (object) =>
797
+ object.type == 'container' &&
798
+ !(object as ContainerLayoutObject).printOnlyAtEnd &&
799
+ !(object as ContainerLayoutObject).printOnlyAtStart
800
+ )
786
801
  .forEach((object) => {
787
802
  addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext)
788
803
  })
789
804
  layout.objects
790
- .filter((object) => object.type == 'container' && (object as any).printOnlyAtEnd)
805
+ .filter((object) => object.type == 'container' && (object as ContainerLayoutObject).printOnlyAtEnd)
791
806
  .forEach((object) => {
792
807
  addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext)
793
808
  })
@@ -811,15 +826,28 @@ export async function genPDF(layout: Layout, printBuffer: any, options: GenPdfOp
811
826
  }
812
827
  drawStaticPartOfPage(doc, layout, printBuffer, paperSize, options, renderContext)
813
828
 
814
- // Draw containers in rendering mode (normal containers first)
815
829
  layout.objects
816
- .filter((object) => object.type == 'container' && !(object as any).printOnlyAtEnd)
830
+ .filter((object) => object.type == 'container' && (object as ContainerLayoutObject).printOnlyAtStart)
831
+ .forEach((object) => {
832
+ addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext)
833
+ })
834
+
835
+ // Draw containers in rendering mode (body containers after printOnlyAtStart)
836
+ layout.objects
837
+ .filter(
838
+ (object) =>
839
+ object.type == 'container' &&
840
+ !(object as ContainerLayoutObject).printOnlyAtEnd &&
841
+ !(object as ContainerLayoutObject).printOnlyAtStart
842
+ )
817
843
  .forEach((object) => {
818
844
  addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext)
819
845
  })
820
846
 
821
847
  // Draw printOnlyAtEnd containers once at the end (at their layout coordinates on current page)
822
- const printOnlyAtEndContainers = layout.objects.filter((object) => object.type == 'container' && (object as any).printOnlyAtEnd)
848
+ const printOnlyAtEndContainers = layout.objects.filter(
849
+ (object) => object.type == 'container' && (object as ContainerLayoutObject).printOnlyAtEnd
850
+ )
823
851
  printOnlyAtEndContainers.forEach((object) => {
824
852
  addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext)
825
853
  })
@@ -71,4 +71,71 @@ describe('genPDF', () => {
71
71
  expect(pdfText).toContain(printBuffer.title)
72
72
  // fs.writeFileSync('test.pdf', Buffer.from(pdf))
73
73
  })
74
+
75
+ it('draws printOnlyAtStart before body containers and printOnlyAtEnd after', async () => {
76
+ const textChild = (name: string, y: number, label: string) =>
77
+ ({
78
+ type: 'text' as const,
79
+ name,
80
+ x: 20,
81
+ y,
82
+ width: 400,
83
+ height: 40,
84
+ active: true,
85
+ text: label,
86
+ textAlign: 1 as const,
87
+ fontFamily: 'Helvetica',
88
+ fontSize: 24,
89
+ fontStyle: 0,
90
+ }) as const
91
+
92
+ const layout: Layout = {
93
+ name: 'order-test',
94
+ paperSize: { width: 210, height: 297, footerHeight: 20 },
95
+ objects: [
96
+ {
97
+ type: 'container',
98
+ x: 0,
99
+ y: 40,
100
+ width: 500,
101
+ height: 80,
102
+ active: true,
103
+ source: '',
104
+ printOnlyAtStart: true,
105
+ children: [textChild('start', 0, 'START_ONLY')],
106
+ },
107
+ {
108
+ type: 'container',
109
+ x: 0,
110
+ y: 120,
111
+ width: 500,
112
+ height: 80,
113
+ active: true,
114
+ source: '',
115
+ children: [textChild('mid', 0, 'BODY_MIDDLE')],
116
+ },
117
+ {
118
+ type: 'container',
119
+ x: 0,
120
+ y: 200,
121
+ width: 500,
122
+ height: 80,
123
+ active: true,
124
+ source: '',
125
+ printOnlyAtEnd: true,
126
+ children: [textChild('end', 0, 'END_ONLY')],
127
+ },
128
+ ],
129
+ }
130
+ const doc = await genPDF(layout, {})
131
+ const pdfText = Buffer.from(doc.output('arraybuffer')).toString('latin1')
132
+ const iStart = pdfText.indexOf('(START_ONLY)')
133
+ const iMid = pdfText.indexOf('(BODY_MIDDLE)')
134
+ const iEnd = pdfText.indexOf('(END_ONLY)')
135
+ expect(iStart).toBeGreaterThanOrEqual(0)
136
+ expect(iMid).toBeGreaterThanOrEqual(0)
137
+ expect(iEnd).toBeGreaterThanOrEqual(0)
138
+ expect(iStart).toBeLessThan(iMid)
139
+ expect(iMid).toBeLessThan(iEnd)
140
+ })
74
141
  })