@softwear/latestcollectioncore 1.0.178 → 1.0.180

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/reports.js CHANGED
@@ -43,9 +43,21 @@ 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). */
46
+ /** Repeat once when the container has no data binding (`source` empty / whitespace-only). */
47
+ function dummyLayoutSource(source) {
48
+ return source === undefined || String(source).trim() === '';
49
+ }
50
+ /** Like repeatOnOverflow: redraw after a page break so nested overflowing content can proceed. */
51
+ function containerRedrawsAfterContinuationPage(child) {
52
+ if (child.type !== 'container')
53
+ return false;
54
+ const c = child;
55
+ if (c.repeatOnOverflow)
56
+ return true;
57
+ return dummyLayoutSource(c.source);
58
+ }
47
59
  function containerLoopSource(container, printBuffer) {
48
- if (!container.source || container.source === '')
60
+ if (dummyLayoutSource(container.source))
49
61
  return [printBuffer];
50
62
  return getProperty(container.source, printBuffer);
51
63
  }
@@ -368,6 +380,8 @@ function drawSimpleObject(x, y, doc, object, printBuffer, width, height, options
368
380
  }
369
381
  }
370
382
  function addPage(originX, originY, doc, layout, rootPrintBuffer, paperSize, options, containerChain, context) {
383
+ context.pendingRootStartBumpTenths = 0;
384
+ context.applyRootStartBumpToThisCall = false;
371
385
  if (context.mode === 'measurement') {
372
386
  // Track page count in measurement mode
373
387
  if (context.currentPageCount === undefined) {
@@ -392,7 +406,7 @@ function addPage(originX, originY, doc, layout, rootPrintBuffer, paperSize, opti
392
406
  absoluteLowerRightHandSide.y = originY / 10;
393
407
  containerChain.forEach((link) => {
394
408
  link.object.children.forEach((child) => {
395
- if (!child.snapToBottom && (child.type != 'container' || child.repeatOnOverflow)) {
409
+ if (!child.snapToBottom && (child.type != 'container' || containerRedrawsAfterContinuationPage(child))) {
396
410
  const childLowerRightHandSide = addObjectToPDF(originX, originY, doc, child, link.printBuffer, paperSize, layout, options, rootPrintBuffer, [], context);
397
411
  absoluteLowerRightHandSide.x = Math.max(absoluteLowerRightHandSide.x, childLowerRightHandSide.x);
398
412
  absoluteLowerRightHandSide.y = Math.max(absoluteLowerRightHandSide.y, childLowerRightHandSide.y);
@@ -441,9 +455,15 @@ function drawBottomDwellers(object, originX, originY, absoluteLowerRightHandSide
441
455
  * Use originX,originY as origin to displace the object's own x,y coordinates
442
456
  */
443
457
  addObjectToPDF = function (originX, originY, doc, object, printBuffer, paperSize, layout, options, rootPrintBuffer, containerChain, context) {
444
- var _a;
458
+ var _a, _b;
459
+ let rootBump = 0;
460
+ if (context.applyRootStartBumpToThisCall) {
461
+ rootBump = (_a = context.pendingRootStartBumpTenths) !== null && _a !== void 0 ? _a : 0;
462
+ context.applyRootStartBumpToThisCall = false;
463
+ context.pendingRootStartBumpTenths = 0;
464
+ }
445
465
  const x = (originX + object.x) / 10;
446
- const y = (originY + object.y) / 10;
466
+ const y = (originY + object.y + rootBump) / 10;
447
467
  const width = object.width / 10;
448
468
  const height = object.height / 10;
449
469
  // Keep track of the lower righthandside of all objects in this container
@@ -458,7 +478,7 @@ addObjectToPDF = function (originX, originY, doc, object, printBuffer, paperSize
458
478
  let originX = x * 10, originY = y * 10;
459
479
  const source = containerLoopSource(object, printBuffer);
460
480
  if (source === undefined) {
461
- (_a = options.onAlert) === null || _a === void 0 ? void 0 : _a.call(options, { header: 'containernotfound', body: object.source, type: 'warning', timeout: 10000 });
481
+ (_b = options.onAlert) === null || _b === void 0 ? void 0 : _b.call(options, { header: 'containernotfound', body: object.source, type: 'warning', timeout: 10000 });
462
482
  return absoluteLowerRightHandSide;
463
483
  }
464
484
  const nrContainers = source.length;
@@ -675,14 +695,16 @@ function genPDF(layout, printBuffer, options = {}) {
675
695
  !object.printOnlyAtEnd &&
676
696
  !object.printOnlyAtStart)
677
697
  .forEach((object) => {
678
- const bump = Math.max(0, measurementTopPrintedStartBottomTenths - object.y);
679
- addObjectToPDF(0, bump, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
698
+ measurementContext.applyRootStartBumpToThisCall = true;
699
+ measurementContext.pendingRootStartBumpTenths = Math.max(0, measurementTopPrintedStartBottomTenths - object.y);
700
+ addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
680
701
  });
681
702
  layout.objects
682
703
  .filter((object) => object.type == 'container' && object.printOnlyAtEnd)
683
704
  .forEach((object) => {
684
- const bump = Math.max(0, measurementTopPrintedStartBottomTenths - object.y);
685
- addObjectToPDF(0, bump, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
705
+ measurementContext.applyRootStartBumpToThisCall = true;
706
+ measurementContext.pendingRootStartBumpTenths = Math.max(0, measurementTopPrintedStartBottomTenths - object.y);
707
+ addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
686
708
  });
687
709
  // Get total page count from measurement pass
688
710
  const totalPageCount = measurementContext.currentPageCount || 1;
@@ -714,14 +736,16 @@ function genPDF(layout, printBuffer, options = {}) {
714
736
  !object.printOnlyAtEnd &&
715
737
  !object.printOnlyAtStart)
716
738
  .forEach((object) => {
717
- const bump = Math.max(0, renderTopPrintedStartBottomTenths - object.y);
718
- addObjectToPDF(0, bump, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
739
+ renderContext.applyRootStartBumpToThisCall = true;
740
+ renderContext.pendingRootStartBumpTenths = Math.max(0, renderTopPrintedStartBottomTenths - object.y);
741
+ addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
719
742
  });
720
743
  // Draw printOnlyAtEnd containers once at the end (at their layout coordinates on current page)
721
744
  const printOnlyAtEndContainers = layout.objects.filter((object) => object.type == 'container' && object.printOnlyAtEnd);
722
745
  printOnlyAtEndContainers.forEach((object) => {
723
- const bump = Math.max(0, renderTopPrintedStartBottomTenths - object.y);
724
- addObjectToPDF(0, bump, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
746
+ renderContext.applyRootStartBumpToThisCall = true;
747
+ renderContext.pendingRootStartBumpTenths = Math.max(0, renderTopPrintedStartBottomTenths - object.y);
748
+ addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
725
749
  });
726
750
  return doc;
727
751
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwear/latestcollectioncore",
3
- "version": "1.0.178",
3
+ "version": "1.0.180",
4
4
  "description": "Core functions for LatestCollections applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/reports.ts CHANGED
@@ -46,6 +46,10 @@ interface RenderContext {
46
46
  imageAssets?: Map<string, PdfImageAsset>
47
47
  /** Layout default font used when object.fontFamily is undefined */
48
48
  defaultFontFamily?: string
49
+ /** Set with {@link pendingRootStartBumpTenths} only for top-level genPDF container draws; consumed on first addObjectToPDF read. */
50
+ applyRootStartBumpToThisCall?: boolean
51
+ /** Vertical pad (tenths-mm) so body roots sit below rendered printOnlyAtStart siblings; use with applyRootStartBumpToThisCall only. */
52
+ pendingRootStartBumpTenths?: number
49
53
  }
50
54
 
51
55
  // We have to declare some functions that will be called by other functions but also have to call those other functions
@@ -92,10 +96,22 @@ function getProperty(propertyName: string, object: any): any {
92
96
  return property
93
97
  }
94
98
 
95
- /** Repeat once over the current buffer when the container is only a visual group (no `source` binding). */
99
+ /** Repeat once when the container has no data binding (`source` empty / whitespace-only). */
100
+ function dummyLayoutSource(source?: string): boolean {
101
+ return source === undefined || String(source).trim() === ''
102
+ }
103
+
104
+ /** Like repeatOnOverflow: redraw after a page break so nested overflowing content can proceed. */
105
+ function containerRedrawsAfterContinuationPage(child: LayoutObject): boolean {
106
+ if (child.type !== 'container') return false
107
+ const c = child as ContainerLayoutObject
108
+ if (c.repeatOnOverflow) return true
109
+ return dummyLayoutSource(c.source)
110
+ }
111
+
96
112
  function containerLoopSource(container: ContainerLayoutObject, printBuffer: any): any {
97
- if (!container.source || container.source === '') return [printBuffer]
98
- return getProperty(container.source, printBuffer)
113
+ if (dummyLayoutSource(container.source)) return [printBuffer]
114
+ return getProperty(container.source as string, printBuffer)
99
115
  }
100
116
 
101
117
  /** jsPDF font style strings. fontStyle bitmask: 0=normal, 1=bold, 2=italic, 3=bold+italic */
@@ -433,6 +449,8 @@ function addPage(
433
449
  containerChain: { object: ContainerLayoutObject; printBuffer: any }[],
434
450
  context: RenderContext
435
451
  ): { originX: number; originY: number; x: number; y: number } {
452
+ context.pendingRootStartBumpTenths = 0
453
+ context.applyRootStartBumpToThisCall = false
436
454
  if (context.mode === 'measurement') {
437
455
  // Track page count in measurement mode
438
456
  if (context.currentPageCount === undefined) {
@@ -456,7 +474,7 @@ function addPage(
456
474
  absoluteLowerRightHandSide.y = originY / 10
457
475
  containerChain.forEach((link) => {
458
476
  link.object.children.forEach((child) => {
459
- if (!child.snapToBottom && (child.type != 'container' || child.repeatOnOverflow)) {
477
+ if (!child.snapToBottom && (child.type != 'container' || containerRedrawsAfterContinuationPage(child))) {
460
478
  const childLowerRightHandSide = addObjectToPDF(originX, originY, doc, child, link.printBuffer, paperSize, layout, options, rootPrintBuffer, [], context)
461
479
  absoluteLowerRightHandSide.x = Math.max(absoluteLowerRightHandSide.x, childLowerRightHandSide.x)
462
480
  absoluteLowerRightHandSide.y = Math.max(absoluteLowerRightHandSide.y, childLowerRightHandSide.y)
@@ -529,8 +547,14 @@ addObjectToPDF = function (
529
547
  containerChain: { object: ContainerLayoutObject; printBuffer: any }[],
530
548
  context: RenderContext
531
549
  ): { x: number; y: number } {
550
+ let rootBump = 0
551
+ if (context.applyRootStartBumpToThisCall) {
552
+ rootBump = context.pendingRootStartBumpTenths ?? 0
553
+ context.applyRootStartBumpToThisCall = false
554
+ context.pendingRootStartBumpTenths = 0
555
+ }
532
556
  const x = (originX + object.x) / 10
533
- const y = (originY + object.y) / 10
557
+ const y = (originY + object.y + rootBump) / 10
534
558
  const width = object.width / 10
535
559
  const height = object.height / 10
536
560
  // Keep track of the lower righthandside of all objects in this container
@@ -801,14 +825,16 @@ export async function genPDF(layout: Layout, printBuffer: any, options: GenPdfOp
801
825
  !(object as ContainerLayoutObject).printOnlyAtStart
802
826
  )
803
827
  .forEach((object) => {
804
- const bump = Math.max(0, measurementTopPrintedStartBottomTenths - object.y)
805
- addObjectToPDF(0, bump, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext)
828
+ measurementContext.applyRootStartBumpToThisCall = true
829
+ measurementContext.pendingRootStartBumpTenths = Math.max(0, measurementTopPrintedStartBottomTenths - object.y)
830
+ addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext)
806
831
  })
807
832
  layout.objects
808
833
  .filter((object) => object.type == 'container' && (object as ContainerLayoutObject).printOnlyAtEnd)
809
834
  .forEach((object) => {
810
- const bump = Math.max(0, measurementTopPrintedStartBottomTenths - object.y)
811
- addObjectToPDF(0, bump, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext)
835
+ measurementContext.applyRootStartBumpToThisCall = true
836
+ measurementContext.pendingRootStartBumpTenths = Math.max(0, measurementTopPrintedStartBottomTenths - object.y)
837
+ addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext)
812
838
  })
813
839
 
814
840
  // Get total page count from measurement pass
@@ -847,8 +873,9 @@ export async function genPDF(layout: Layout, printBuffer: any, options: GenPdfOp
847
873
  !(object as ContainerLayoutObject).printOnlyAtStart
848
874
  )
849
875
  .forEach((object) => {
850
- const bump = Math.max(0, renderTopPrintedStartBottomTenths - object.y)
851
- addObjectToPDF(0, bump, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext)
876
+ renderContext.applyRootStartBumpToThisCall = true
877
+ renderContext.pendingRootStartBumpTenths = Math.max(0, renderTopPrintedStartBottomTenths - object.y)
878
+ addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext)
852
879
  })
853
880
 
854
881
  // Draw printOnlyAtEnd containers once at the end (at their layout coordinates on current page)
@@ -856,8 +883,9 @@ export async function genPDF(layout: Layout, printBuffer: any, options: GenPdfOp
856
883
  (object) => object.type == 'container' && (object as ContainerLayoutObject).printOnlyAtEnd
857
884
  )
858
885
  printOnlyAtEndContainers.forEach((object) => {
859
- const bump = Math.max(0, renderTopPrintedStartBottomTenths - object.y)
860
- addObjectToPDF(0, bump, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext)
886
+ renderContext.applyRootStartBumpToThisCall = true
887
+ renderContext.pendingRootStartBumpTenths = Math.max(0, renderTopPrintedStartBottomTenths - object.y)
888
+ addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext)
861
889
  })
862
890
  return doc
863
891
  }
@@ -209,4 +209,58 @@ describe('genPDF', () => {
209
209
  expect(iB).toBeGreaterThanOrEqual(0)
210
210
  expect(iH).toBeLessThan(iB)
211
211
  })
212
+
213
+ it('redraws empty-source wrapper so inner vertical repeat spans multiple pages', async () => {
214
+ const rowField = {
215
+ type: 'field' as const,
216
+ name: 'ln',
217
+ x: 0,
218
+ y: 0,
219
+ width: 1600,
220
+ height: 80,
221
+ active: true,
222
+ source: 'line',
223
+ textAlign: 1 as const,
224
+ fontFamily: 'Helvetica',
225
+ fontSize: 36,
226
+ fontStyle: 0,
227
+ }
228
+ const ROWS = Array.from({ length: 40 }, (_, i) => ({ line: `ROW_${i}_MARK` }))
229
+ const layout: Layout = {
230
+ name: 'wrapper-overflow',
231
+ paperSize: { width: 210, height: 297, footerHeight: 20 },
232
+ objects: [
233
+ {
234
+ type: 'container',
235
+ name: 'wrap',
236
+ x: 50,
237
+ y: 100,
238
+ width: 1900,
239
+ height: 2800,
240
+ active: true,
241
+ source: '',
242
+ children: [
243
+ {
244
+ type: 'container',
245
+ name: 'rows',
246
+ x: 0,
247
+ y: 0,
248
+ width: 1800,
249
+ height: 120,
250
+ active: true,
251
+ source: 'ROWS',
252
+ repeatContainer: 'vertical' as const,
253
+ children: [rowField],
254
+ },
255
+ ],
256
+ },
257
+ ],
258
+ }
259
+
260
+ const doc = await genPDF(layout, { ROWS })
261
+ expect(doc.getNumberOfPages()).toBeGreaterThanOrEqual(2)
262
+ const pdfText = Buffer.from(doc.output('arraybuffer')).toString('latin1')
263
+ expect(pdfText).toContain('ROW_0_MARK')
264
+ expect(pdfText).toContain('ROW_35_MARK')
265
+ })
212
266
  })