@softwear/latestcollectioncore 1.0.182 → 1.0.184
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 +49 -16
- package/package.json +1 -1
- package/src/reports.ts +56 -14
- package/test/reports.spec.ts +68 -0
package/dist/reports.js
CHANGED
|
@@ -61,6 +61,26 @@ function containerLoopSource(container, printBuffer) {
|
|
|
61
61
|
return [printBuffer];
|
|
62
62
|
return getProperty(container.source, printBuffer);
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Top-level empty-source containers (invoice “billTo” bands, wrappers) sit beside overflowing repeats.
|
|
66
|
+
* They are not on the nested {@link containerChain}; redraw them whenever we append a continuation page.
|
|
67
|
+
* Omit the overflowing root when it is chain[0] — redrawing it would recurse (e.g. wrapper around lines).
|
|
68
|
+
*/
|
|
69
|
+
function redrawRootDummyBodyContainersOnNewPage(doc, layout, rootPrintBuffer, paperSize, options, context, containerChain) {
|
|
70
|
+
const overflowingRootContainer = containerChain.length > 0 ? containerChain[0].object : undefined;
|
|
71
|
+
layout.objects.forEach((object) => {
|
|
72
|
+
if (object.type !== 'container')
|
|
73
|
+
return;
|
|
74
|
+
if (overflowingRootContainer !== undefined && object === overflowingRootContainer)
|
|
75
|
+
return;
|
|
76
|
+
const c = object;
|
|
77
|
+
if (c.printOnlyAtStart === true || c.printOnlyAtEnd === true)
|
|
78
|
+
return;
|
|
79
|
+
if (!dummyLayoutSource(c.source))
|
|
80
|
+
return;
|
|
81
|
+
addObjectToPDF(0, 0, doc, object, rootPrintBuffer, paperSize, layout, options, rootPrintBuffer, [], context);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
64
84
|
/** jsPDF font style strings. fontStyle bitmask: 0=normal, 1=bold, 2=italic, 3=bold+italic */
|
|
65
85
|
const FONT_STYLES = ['normal', 'bold', 'italic', 'bolditalic'];
|
|
66
86
|
/** Recursively collect used custom fonts from layout objects (text/field with fontFamily) and layout default. */
|
|
@@ -380,6 +400,9 @@ function drawSimpleObject(x, y, doc, object, printBuffer, width, height, options
|
|
|
380
400
|
}
|
|
381
401
|
}
|
|
382
402
|
function addPage(originX, originY, doc, layout, rootPrintBuffer, paperSize, options, containerChain, context) {
|
|
403
|
+
/** Absolute tenths where the overflowing repeat resumes; callers already computed this correctly. */
|
|
404
|
+
const continuationOxTenths = originX;
|
|
405
|
+
const continuationOyTenths = originY;
|
|
383
406
|
context.pendingRootStartBumpTenths = 0;
|
|
384
407
|
context.applyRootStartBumpToThisCall = false;
|
|
385
408
|
context.remainingRootStartBumpTenths = 0;
|
|
@@ -396,45 +419,55 @@ function addPage(originX, originY, doc, layout, rootPrintBuffer, paperSize, opti
|
|
|
396
419
|
}
|
|
397
420
|
if (!context.measureOnly) {
|
|
398
421
|
drawStaticPartOfPage(doc, layout, rootPrintBuffer, paperSize, options, context);
|
|
422
|
+
redrawRootDummyBodyContainersOnNewPage(doc, layout, rootPrintBuffer, paperSize, options, context, containerChain);
|
|
399
423
|
}
|
|
400
|
-
|
|
401
|
-
|
|
424
|
+
// Bounding box tracked for snap-to-bottom; continuation cursor is ALWAYS the passed-in coords.
|
|
425
|
+
const absoluteLowerRightHandSide = { x: continuationOxTenths / 10, y: continuationOyTenths / 10 };
|
|
402
426
|
if (containerChain.length > 0) {
|
|
403
|
-
//
|
|
427
|
+
// Re-draw ancestor frames on the new page (local coordinates). Do NOT treat this pass as another
|
|
428
|
+
// repeat iteration: skip repeatContainer advancement on the innermost link—the real loop resumes
|
|
429
|
+
// after addPage returns with continuationOxTenths / continuationOyTenths.
|
|
404
430
|
originX = containerChain[0].object.x;
|
|
405
431
|
originY = containerChain[0].object.y;
|
|
406
|
-
absoluteLowerRightHandSide.x = originX / 10;
|
|
407
|
-
absoluteLowerRightHandSide.y = originY / 10;
|
|
432
|
+
absoluteLowerRightHandSide.x = Math.max(absoluteLowerRightHandSide.x, originX / 10);
|
|
433
|
+
absoluteLowerRightHandSide.y = Math.max(absoluteLowerRightHandSide.y, originY / 10);
|
|
408
434
|
containerChain.forEach((link, linkIdx) => {
|
|
409
435
|
var _a;
|
|
410
|
-
const
|
|
436
|
+
const deeperLinkChild = (_a = containerChain[linkIdx + 1]) === null || _a === void 0 ? void 0 : _a.object;
|
|
411
437
|
link.object.children.forEach((child) => {
|
|
412
438
|
if (child.snapToBottom)
|
|
413
439
|
return;
|
|
414
|
-
//
|
|
415
|
-
// innermost link draws their leaves). Without this, nested x/y offsets are lost on page 2+.
|
|
440
|
+
// Skip inner repeat container on chain: advancing origin by x,y is enough; the live loop draws rows.
|
|
416
441
|
if (child.type === 'container' &&
|
|
417
|
-
|
|
418
|
-
child ===
|
|
442
|
+
deeperLinkChild &&
|
|
443
|
+
child === deeperLinkChild &&
|
|
419
444
|
!containerRedrawsAfterContinuationPage(child)) {
|
|
420
445
|
const c = child;
|
|
421
446
|
originX += c.x;
|
|
422
447
|
originY += c.y;
|
|
423
448
|
return;
|
|
424
449
|
}
|
|
425
|
-
if (child.type
|
|
450
|
+
if (child.type !== 'container' || containerRedrawsAfterContinuationPage(child)) {
|
|
426
451
|
const childLowerRightHandSide = addObjectToPDF(originX, originY, doc, child, link.printBuffer, paperSize, layout, options, rootPrintBuffer, [], context);
|
|
427
452
|
absoluteLowerRightHandSide.x = Math.max(absoluteLowerRightHandSide.x, childLowerRightHandSide.x);
|
|
428
453
|
absoluteLowerRightHandSide.y = Math.max(absoluteLowerRightHandSide.y, childLowerRightHandSide.y);
|
|
429
454
|
}
|
|
430
455
|
});
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
456
|
+
const isLastLinkOnChain = linkIdx >= containerChain.length - 1;
|
|
457
|
+
if (!isLastLinkOnChain) {
|
|
458
|
+
if (link.object.repeatContainer === 'horizontal')
|
|
459
|
+
originX = absoluteLowerRightHandSide.x * 10;
|
|
460
|
+
if (link.object.repeatContainer === 'vertical')
|
|
461
|
+
originY = absoluteLowerRightHandSide.y * 10;
|
|
462
|
+
}
|
|
435
463
|
});
|
|
436
464
|
}
|
|
437
|
-
return {
|
|
465
|
+
return {
|
|
466
|
+
originX: continuationOxTenths,
|
|
467
|
+
originY: continuationOyTenths,
|
|
468
|
+
x: absoluteLowerRightHandSide.x,
|
|
469
|
+
y: absoluteLowerRightHandSide.y,
|
|
470
|
+
};
|
|
438
471
|
}
|
|
439
472
|
function drawBottomDwellers(object, originX, originY, absoluteLowerRightHandSide, bottomChildY, paperSize, doc, layout, rootPrintBuffer, options, newContainerChain, container, context) {
|
|
440
473
|
let childRelativeToBottomDrawn = false;
|
package/package.json
CHANGED
package/src/reports.ts
CHANGED
|
@@ -118,6 +118,33 @@ function containerLoopSource(container: ContainerLayoutObject, printBuffer: any)
|
|
|
118
118
|
return getProperty(container.source as string, printBuffer)
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Top-level empty-source containers (invoice “billTo” bands, wrappers) sit beside overflowing repeats.
|
|
123
|
+
* They are not on the nested {@link containerChain}; redraw them whenever we append a continuation page.
|
|
124
|
+
* Omit the overflowing root when it is chain[0] — redrawing it would recurse (e.g. wrapper around lines).
|
|
125
|
+
*/
|
|
126
|
+
function redrawRootDummyBodyContainersOnNewPage(
|
|
127
|
+
doc: jsPDF,
|
|
128
|
+
layout: Layout,
|
|
129
|
+
rootPrintBuffer: any,
|
|
130
|
+
paperSize: PaperSize,
|
|
131
|
+
options: GenPdfOptions,
|
|
132
|
+
context: RenderContext,
|
|
133
|
+
containerChain: { object: ContainerLayoutObject; printBuffer: any }[]
|
|
134
|
+
): void {
|
|
135
|
+
const overflowingRootContainer =
|
|
136
|
+
containerChain.length > 0 ? (containerChain[0].object as ContainerLayoutObject) : undefined
|
|
137
|
+
|
|
138
|
+
layout.objects.forEach((object) => {
|
|
139
|
+
if (object.type !== 'container') return
|
|
140
|
+
if (overflowingRootContainer !== undefined && object === overflowingRootContainer) return
|
|
141
|
+
const c = object as ContainerLayoutObject
|
|
142
|
+
if (c.printOnlyAtStart === true || c.printOnlyAtEnd === true) return
|
|
143
|
+
if (!dummyLayoutSource(c.source)) return
|
|
144
|
+
addObjectToPDF(0, 0, doc, object, rootPrintBuffer, paperSize, layout, options, rootPrintBuffer, [], context)
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
121
148
|
/** jsPDF font style strings. fontStyle bitmask: 0=normal, 1=bold, 2=italic, 3=bold+italic */
|
|
122
149
|
const FONT_STYLES = ['normal', 'bold', 'italic', 'bolditalic'] as const
|
|
123
150
|
|
|
@@ -453,6 +480,10 @@ function addPage(
|
|
|
453
480
|
containerChain: { object: ContainerLayoutObject; printBuffer: any }[],
|
|
454
481
|
context: RenderContext
|
|
455
482
|
): { originX: number; originY: number; x: number; y: number } {
|
|
483
|
+
/** Absolute tenths where the overflowing repeat resumes; callers already computed this correctly. */
|
|
484
|
+
const continuationOxTenths = originX
|
|
485
|
+
const continuationOyTenths = originY
|
|
486
|
+
|
|
456
487
|
context.pendingRootStartBumpTenths = 0
|
|
457
488
|
context.applyRootStartBumpToThisCall = false
|
|
458
489
|
context.remainingRootStartBumpTenths = 0
|
|
@@ -468,25 +499,28 @@ function addPage(
|
|
|
468
499
|
}
|
|
469
500
|
if (!context.measureOnly) {
|
|
470
501
|
drawStaticPartOfPage(doc, layout, rootPrintBuffer, paperSize, options, context)
|
|
502
|
+
redrawRootDummyBodyContainersOnNewPage(doc, layout, rootPrintBuffer, paperSize, options, context, containerChain)
|
|
471
503
|
}
|
|
472
|
-
|
|
473
|
-
|
|
504
|
+
// Bounding box tracked for snap-to-bottom; continuation cursor is ALWAYS the passed-in coords.
|
|
505
|
+
const absoluteLowerRightHandSide = { x: continuationOxTenths / 10, y: continuationOyTenths / 10 }
|
|
474
506
|
if (containerChain.length > 0) {
|
|
475
|
-
//
|
|
507
|
+
// Re-draw ancestor frames on the new page (local coordinates). Do NOT treat this pass as another
|
|
508
|
+
// repeat iteration: skip repeatContainer advancement on the innermost link—the real loop resumes
|
|
509
|
+
// after addPage returns with continuationOxTenths / continuationOyTenths.
|
|
476
510
|
originX = containerChain[0].object.x
|
|
477
511
|
originY = containerChain[0].object.y
|
|
478
|
-
absoluteLowerRightHandSide.x = originX / 10
|
|
479
|
-
absoluteLowerRightHandSide.y = originY / 10
|
|
512
|
+
absoluteLowerRightHandSide.x = Math.max(absoluteLowerRightHandSide.x, originX / 10)
|
|
513
|
+
absoluteLowerRightHandSide.y = Math.max(absoluteLowerRightHandSide.y, originY / 10)
|
|
514
|
+
|
|
480
515
|
containerChain.forEach((link, linkIdx) => {
|
|
481
|
-
const
|
|
516
|
+
const deeperLinkChild = containerChain[linkIdx + 1]?.object
|
|
482
517
|
link.object.children.forEach((child) => {
|
|
483
518
|
if (child.snapToBottom) return
|
|
484
|
-
//
|
|
485
|
-
// innermost link draws their leaves). Without this, nested x/y offsets are lost on page 2+.
|
|
519
|
+
// Skip inner repeat container on chain: advancing origin by x,y is enough; the live loop draws rows.
|
|
486
520
|
if (
|
|
487
521
|
child.type === 'container' &&
|
|
488
|
-
|
|
489
|
-
child ===
|
|
522
|
+
deeperLinkChild &&
|
|
523
|
+
child === deeperLinkChild &&
|
|
490
524
|
!containerRedrawsAfterContinuationPage(child)
|
|
491
525
|
) {
|
|
492
526
|
const c = child as ContainerLayoutObject
|
|
@@ -494,17 +528,25 @@ function addPage(
|
|
|
494
528
|
originY += c.y
|
|
495
529
|
return
|
|
496
530
|
}
|
|
497
|
-
if (child.type
|
|
531
|
+
if (child.type !== 'container' || containerRedrawsAfterContinuationPage(child)) {
|
|
498
532
|
const childLowerRightHandSide = addObjectToPDF(originX, originY, doc, child, link.printBuffer, paperSize, layout, options, rootPrintBuffer, [], context)
|
|
499
533
|
absoluteLowerRightHandSide.x = Math.max(absoluteLowerRightHandSide.x, childLowerRightHandSide.x)
|
|
500
534
|
absoluteLowerRightHandSide.y = Math.max(absoluteLowerRightHandSide.y, childLowerRightHandSide.y)
|
|
501
535
|
}
|
|
502
536
|
})
|
|
503
|
-
|
|
504
|
-
if (
|
|
537
|
+
const isLastLinkOnChain = linkIdx >= containerChain.length - 1
|
|
538
|
+
if (!isLastLinkOnChain) {
|
|
539
|
+
if (link.object.repeatContainer === 'horizontal') originX = absoluteLowerRightHandSide.x * 10
|
|
540
|
+
if (link.object.repeatContainer === 'vertical') originY = absoluteLowerRightHandSide.y * 10
|
|
541
|
+
}
|
|
505
542
|
})
|
|
506
543
|
}
|
|
507
|
-
return {
|
|
544
|
+
return {
|
|
545
|
+
originX: continuationOxTenths,
|
|
546
|
+
originY: continuationOyTenths,
|
|
547
|
+
x: absoluteLowerRightHandSide.x,
|
|
548
|
+
y: absoluteLowerRightHandSide.y,
|
|
549
|
+
}
|
|
508
550
|
}
|
|
509
551
|
|
|
510
552
|
function drawBottomDwellers(
|
package/test/reports.spec.ts
CHANGED
|
@@ -263,4 +263,72 @@ describe('genPDF', () => {
|
|
|
263
263
|
expect(pdfText).toContain('ROW_0_MARK')
|
|
264
264
|
expect(pdfText).toContain('ROW_35_MARK')
|
|
265
265
|
})
|
|
266
|
+
|
|
267
|
+
it('redraws top-level empty-source body containers on every continuation page beside an overflowing sibling', async () => {
|
|
268
|
+
const chromeText = {
|
|
269
|
+
type: 'text' as const,
|
|
270
|
+
name: 'chrome-label',
|
|
271
|
+
x: 0,
|
|
272
|
+
y: 0,
|
|
273
|
+
width: 300,
|
|
274
|
+
height: 48,
|
|
275
|
+
active: true,
|
|
276
|
+
text: 'CHROME_EACH_PAGE',
|
|
277
|
+
textAlign: 1 as const,
|
|
278
|
+
fontFamily: 'Helvetica',
|
|
279
|
+
fontSize: 28,
|
|
280
|
+
fontStyle: 0,
|
|
281
|
+
}
|
|
282
|
+
const lineField = {
|
|
283
|
+
type: 'field' as const,
|
|
284
|
+
name: 'ln',
|
|
285
|
+
x: 0,
|
|
286
|
+
y: 0,
|
|
287
|
+
width: 800,
|
|
288
|
+
height: 96,
|
|
289
|
+
active: true,
|
|
290
|
+
source: 'line',
|
|
291
|
+
textAlign: 1 as const,
|
|
292
|
+
fontFamily: 'Helvetica',
|
|
293
|
+
fontSize: 36,
|
|
294
|
+
fontStyle: 0,
|
|
295
|
+
}
|
|
296
|
+
const ITEMS = Array.from({ length: 48 }, (_, i) => ({ line: `SKU_${i}` }))
|
|
297
|
+
const layout: Layout = {
|
|
298
|
+
name: 'sibling-chrome',
|
|
299
|
+
paperSize: { width: 210, height: 297, footerHeight: 20 },
|
|
300
|
+
objects: [
|
|
301
|
+
{
|
|
302
|
+
type: 'container',
|
|
303
|
+
name: 'addressBand',
|
|
304
|
+
x: 60,
|
|
305
|
+
y: 440,
|
|
306
|
+
width: 400,
|
|
307
|
+
height: 280,
|
|
308
|
+
active: true,
|
|
309
|
+
source: '',
|
|
310
|
+
children: [chromeText],
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
type: 'container',
|
|
314
|
+
name: 'lines',
|
|
315
|
+
x: 500,
|
|
316
|
+
y: 440,
|
|
317
|
+
width: 1500,
|
|
318
|
+
height: 200,
|
|
319
|
+
active: true,
|
|
320
|
+
source: 'ITEMS',
|
|
321
|
+
repeatContainer: 'vertical' as const,
|
|
322
|
+
children: [lineField],
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const doc = await genPDF(layout, { ITEMS })
|
|
328
|
+
expect(doc.getNumberOfPages()).toBeGreaterThanOrEqual(2)
|
|
329
|
+
const pdfText = Buffer.from(doc.output('arraybuffer')).toString('latin1')
|
|
330
|
+
const chromeMatches = pdfText.match(/\(CHROME_EACH_PAGE\)/g)
|
|
331
|
+
expect(chromeMatches?.length ?? 0).toBeGreaterThanOrEqual(doc.getNumberOfPages())
|
|
332
|
+
expect(pdfText).toContain('SKU_20')
|
|
333
|
+
})
|
|
266
334
|
})
|