@softwear/latestcollectioncore 1.0.177 → 1.0.178
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 +1 -1
- package/dist/reports.js +14 -6
- package/package.json +1 -1
- package/src/pdf.ts +1 -1
- package/src/reports.ts +14 -6
- package/test/reports.spec.ts +73 -2
package/dist/pdf.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ 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
|
-
/**
|
|
57
|
+
/** Only effective on top-level `layout.objects` (PDF engine ignores this flag on nested containers). */
|
|
58
58
|
printOnlyAtStart?: boolean;
|
|
59
59
|
}
|
|
60
60
|
export type LayoutObject = TextLayoutObject | FieldLayoutObject | RectangleLayoutObject | ImageLayoutObject | ContainerLayoutObject;
|
package/dist/reports.js
CHANGED
|
@@ -663,22 +663,26 @@ function genPDF(layout, printBuffer, options = {}) {
|
|
|
663
663
|
}
|
|
664
664
|
drawStaticPartOfPage(measurementDoc, layout, printBuffer, paperSize, options, measurementContext);
|
|
665
665
|
// Draw containers in measurement mode (same order as render)
|
|
666
|
+
let measurementTopPrintedStartBottomTenths = 0;
|
|
666
667
|
layout.objects
|
|
667
668
|
.filter((object) => object.type == 'container' && object.printOnlyAtStart)
|
|
668
669
|
.forEach((object) => {
|
|
669
|
-
addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
|
|
670
|
+
const lh = addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
|
|
671
|
+
measurementTopPrintedStartBottomTenths = Math.max(measurementTopPrintedStartBottomTenths, lh.y * 10);
|
|
670
672
|
});
|
|
671
673
|
layout.objects
|
|
672
674
|
.filter((object) => object.type == 'container' &&
|
|
673
675
|
!object.printOnlyAtEnd &&
|
|
674
676
|
!object.printOnlyAtStart)
|
|
675
677
|
.forEach((object) => {
|
|
676
|
-
|
|
678
|
+
const bump = Math.max(0, measurementTopPrintedStartBottomTenths - object.y);
|
|
679
|
+
addObjectToPDF(0, bump, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
|
|
677
680
|
});
|
|
678
681
|
layout.objects
|
|
679
682
|
.filter((object) => object.type == 'container' && object.printOnlyAtEnd)
|
|
680
683
|
.forEach((object) => {
|
|
681
|
-
|
|
684
|
+
const bump = Math.max(0, measurementTopPrintedStartBottomTenths - object.y);
|
|
685
|
+
addObjectToPDF(0, bump, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
|
|
682
686
|
});
|
|
683
687
|
// Get total page count from measurement pass
|
|
684
688
|
const totalPageCount = measurementContext.currentPageCount || 1;
|
|
@@ -697,10 +701,12 @@ function genPDF(layout, printBuffer, options = {}) {
|
|
|
697
701
|
defaultFontFamily: layout.defaultFontFamily,
|
|
698
702
|
};
|
|
699
703
|
drawStaticPartOfPage(doc, layout, printBuffer, paperSize, options, renderContext);
|
|
704
|
+
let renderTopPrintedStartBottomTenths = 0;
|
|
700
705
|
layout.objects
|
|
701
706
|
.filter((object) => object.type == 'container' && object.printOnlyAtStart)
|
|
702
707
|
.forEach((object) => {
|
|
703
|
-
addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
|
|
708
|
+
const lh = addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
|
|
709
|
+
renderTopPrintedStartBottomTenths = Math.max(renderTopPrintedStartBottomTenths, lh.y * 10);
|
|
704
710
|
});
|
|
705
711
|
// Draw containers in rendering mode (body containers after printOnlyAtStart)
|
|
706
712
|
layout.objects
|
|
@@ -708,12 +714,14 @@ function genPDF(layout, printBuffer, options = {}) {
|
|
|
708
714
|
!object.printOnlyAtEnd &&
|
|
709
715
|
!object.printOnlyAtStart)
|
|
710
716
|
.forEach((object) => {
|
|
711
|
-
|
|
717
|
+
const bump = Math.max(0, renderTopPrintedStartBottomTenths - object.y);
|
|
718
|
+
addObjectToPDF(0, bump, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
|
|
712
719
|
});
|
|
713
720
|
// Draw printOnlyAtEnd containers once at the end (at their layout coordinates on current page)
|
|
714
721
|
const printOnlyAtEndContainers = layout.objects.filter((object) => object.type == 'container' && object.printOnlyAtEnd);
|
|
715
722
|
printOnlyAtEndContainers.forEach((object) => {
|
|
716
|
-
|
|
723
|
+
const bump = Math.max(0, renderTopPrintedStartBottomTenths - object.y);
|
|
724
|
+
addObjectToPDF(0, bump, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
|
|
717
725
|
});
|
|
718
726
|
return doc;
|
|
719
727
|
});
|
package/package.json
CHANGED
package/src/pdf.ts
CHANGED
|
@@ -61,7 +61,7 @@ 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
|
-
/**
|
|
64
|
+
/** Only effective on top-level `layout.objects` (PDF engine ignores this flag on nested containers). */
|
|
65
65
|
printOnlyAtStart?: boolean
|
|
66
66
|
}
|
|
67
67
|
|
package/src/reports.ts
CHANGED
|
@@ -786,10 +786,12 @@ export async function genPDF(layout: Layout, printBuffer: any, options: GenPdfOp
|
|
|
786
786
|
drawStaticPartOfPage(measurementDoc, layout, printBuffer, paperSize, options, measurementContext)
|
|
787
787
|
|
|
788
788
|
// Draw containers in measurement mode (same order as render)
|
|
789
|
+
let measurementTopPrintedStartBottomTenths = 0
|
|
789
790
|
layout.objects
|
|
790
791
|
.filter((object) => object.type == 'container' && (object as ContainerLayoutObject).printOnlyAtStart)
|
|
791
792
|
.forEach((object) => {
|
|
792
|
-
addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext)
|
|
793
|
+
const lh = addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext)
|
|
794
|
+
measurementTopPrintedStartBottomTenths = Math.max(measurementTopPrintedStartBottomTenths, lh.y * 10)
|
|
793
795
|
})
|
|
794
796
|
layout.objects
|
|
795
797
|
.filter(
|
|
@@ -799,12 +801,14 @@ export async function genPDF(layout: Layout, printBuffer: any, options: GenPdfOp
|
|
|
799
801
|
!(object as ContainerLayoutObject).printOnlyAtStart
|
|
800
802
|
)
|
|
801
803
|
.forEach((object) => {
|
|
802
|
-
|
|
804
|
+
const bump = Math.max(0, measurementTopPrintedStartBottomTenths - object.y)
|
|
805
|
+
addObjectToPDF(0, bump, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext)
|
|
803
806
|
})
|
|
804
807
|
layout.objects
|
|
805
808
|
.filter((object) => object.type == 'container' && (object as ContainerLayoutObject).printOnlyAtEnd)
|
|
806
809
|
.forEach((object) => {
|
|
807
|
-
|
|
810
|
+
const bump = Math.max(0, measurementTopPrintedStartBottomTenths - object.y)
|
|
811
|
+
addObjectToPDF(0, bump, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext)
|
|
808
812
|
})
|
|
809
813
|
|
|
810
814
|
// Get total page count from measurement pass
|
|
@@ -826,10 +830,12 @@ export async function genPDF(layout: Layout, printBuffer: any, options: GenPdfOp
|
|
|
826
830
|
}
|
|
827
831
|
drawStaticPartOfPage(doc, layout, printBuffer, paperSize, options, renderContext)
|
|
828
832
|
|
|
833
|
+
let renderTopPrintedStartBottomTenths = 0
|
|
829
834
|
layout.objects
|
|
830
835
|
.filter((object) => object.type == 'container' && (object as ContainerLayoutObject).printOnlyAtStart)
|
|
831
836
|
.forEach((object) => {
|
|
832
|
-
addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext)
|
|
837
|
+
const lh = addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext)
|
|
838
|
+
renderTopPrintedStartBottomTenths = Math.max(renderTopPrintedStartBottomTenths, lh.y * 10)
|
|
833
839
|
})
|
|
834
840
|
|
|
835
841
|
// Draw containers in rendering mode (body containers after printOnlyAtStart)
|
|
@@ -841,7 +847,8 @@ export async function genPDF(layout: Layout, printBuffer: any, options: GenPdfOp
|
|
|
841
847
|
!(object as ContainerLayoutObject).printOnlyAtStart
|
|
842
848
|
)
|
|
843
849
|
.forEach((object) => {
|
|
844
|
-
|
|
850
|
+
const bump = Math.max(0, renderTopPrintedStartBottomTenths - object.y)
|
|
851
|
+
addObjectToPDF(0, bump, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext)
|
|
845
852
|
})
|
|
846
853
|
|
|
847
854
|
// Draw printOnlyAtEnd containers once at the end (at their layout coordinates on current page)
|
|
@@ -849,7 +856,8 @@ export async function genPDF(layout: Layout, printBuffer: any, options: GenPdfOp
|
|
|
849
856
|
(object) => object.type == 'container' && (object as ContainerLayoutObject).printOnlyAtEnd
|
|
850
857
|
)
|
|
851
858
|
printOnlyAtEndContainers.forEach((object) => {
|
|
852
|
-
|
|
859
|
+
const bump = Math.max(0, renderTopPrintedStartBottomTenths - object.y)
|
|
860
|
+
addObjectToPDF(0, bump, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext)
|
|
853
861
|
})
|
|
854
862
|
return doc
|
|
855
863
|
}
|
package/test/reports.spec.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
import { genPDF } from '../src/reports'
|
|
3
3
|
import type { Layout } from '../src/pdf'
|
|
4
|
-
// import fs from 'fs'
|
|
5
4
|
|
|
5
|
+
/** Tiny valid JPEG as data URL — avoids flaky network-dependent image fetch in CI/sandbox. */
|
|
6
|
+
function offlineJpegFixture(): string {
|
|
7
|
+
const b64 =
|
|
8
|
+
'/9j/4AAQSkZJRgABAQIAASABIAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAKAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHhAP/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAQkCf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8B/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgMBPwF//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQYPAj//xAAVEAEAAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQBPCQ//Z'
|
|
9
|
+
return `data:image/jpeg;base64,${b64}`
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// URL still used so collectImageUrlsFromLayout resolves the layout object
|
|
6
13
|
const imageUrl = 'https://upload.wikimedia.org/wikipedia/commons/thumb/7/79/Phillips_PM5538.jpg/500px-Phillips_PM5538.jpg'
|
|
7
14
|
|
|
8
15
|
describe('genPDF', () => {
|
|
@@ -59,7 +66,14 @@ describe('genPDF', () => {
|
|
|
59
66
|
title: 'Minimal PDF',
|
|
60
67
|
}
|
|
61
68
|
|
|
62
|
-
const doc = await genPDF(layout, printBuffer
|
|
69
|
+
const doc = await genPDF(layout, printBuffer, {
|
|
70
|
+
loadImage: async () => ({
|
|
71
|
+
dataUrl: offlineJpegFixture(),
|
|
72
|
+
format: 'JPEG',
|
|
73
|
+
width: 500,
|
|
74
|
+
height: 375,
|
|
75
|
+
}),
|
|
76
|
+
})
|
|
63
77
|
const pdf = doc.output('arraybuffer')
|
|
64
78
|
const pdfText = Buffer.from(pdf).toString('latin1')
|
|
65
79
|
|
|
@@ -138,4 +152,61 @@ describe('genPDF', () => {
|
|
|
138
152
|
expect(iStart).toBeLessThan(iMid)
|
|
139
153
|
expect(iMid).toBeLessThan(iEnd)
|
|
140
154
|
})
|
|
155
|
+
|
|
156
|
+
it('stacks top-level body containers below printOnlyAtStart when they share the same layout y', async () => {
|
|
157
|
+
const text = (name: string, label: string, y = 0, fontSize = 80) =>
|
|
158
|
+
({
|
|
159
|
+
type: 'text' as const,
|
|
160
|
+
name,
|
|
161
|
+
x: 20,
|
|
162
|
+
y,
|
|
163
|
+
width: 900,
|
|
164
|
+
height: 400,
|
|
165
|
+
active: true,
|
|
166
|
+
text: label,
|
|
167
|
+
textAlign: 1 as const,
|
|
168
|
+
fontFamily: 'Helvetica',
|
|
169
|
+
fontSize,
|
|
170
|
+
fontStyle: 0,
|
|
171
|
+
}) as const
|
|
172
|
+
|
|
173
|
+
const sharedY = 400
|
|
174
|
+
const layout: Layout = {
|
|
175
|
+
name: 'same-y-stack',
|
|
176
|
+
paperSize: { width: 210, height: 297, footerHeight: 20 },
|
|
177
|
+
objects: [
|
|
178
|
+
{
|
|
179
|
+
type: 'container',
|
|
180
|
+
x: 0,
|
|
181
|
+
y: sharedY,
|
|
182
|
+
width: 1000,
|
|
183
|
+
height: 900,
|
|
184
|
+
active: true,
|
|
185
|
+
source: '',
|
|
186
|
+
printOnlyAtStart: true,
|
|
187
|
+
children: [text('h', 'PAGE1_HEADER_BLOCK')],
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: 'container',
|
|
191
|
+
x: 0,
|
|
192
|
+
y: sharedY,
|
|
193
|
+
width: 1000,
|
|
194
|
+
height: 500,
|
|
195
|
+
active: true,
|
|
196
|
+
source: '',
|
|
197
|
+
children: [text('b', 'BODY_AFTER_STACK', 0, 32)],
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const doc = await genPDF(layout, {})
|
|
203
|
+
const pdfText = Buffer.from(doc.output('arraybuffer')).toString('latin1')
|
|
204
|
+
expect(pdfText).toContain('(PAGE1_HEADER_BLOCK)')
|
|
205
|
+
expect(pdfText).toContain('(BODY_AFTER_STACK)')
|
|
206
|
+
const iH = pdfText.indexOf('(PAGE1_HEADER_BLOCK)')
|
|
207
|
+
const iB = pdfText.indexOf('(BODY_AFTER_STACK)')
|
|
208
|
+
expect(iH).toBeGreaterThanOrEqual(0)
|
|
209
|
+
expect(iB).toBeGreaterThanOrEqual(0)
|
|
210
|
+
expect(iH).toBeLessThan(iB)
|
|
211
|
+
})
|
|
141
212
|
})
|