@softwear/latestcollectioncore 1.0.204 → 1.0.206
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/barcodeCanvas.d.ts +11 -0
- package/dist/barcodeCanvas.js +61 -0
- package/dist/pdf.d.ts +15 -3
- package/dist/reports.js +29 -5
- package/dist/types.d.ts +0 -1
- package/package.json +2 -1
- package/src/barcodeCanvas.ts +59 -0
- package/src/pdf.ts +11 -2
- package/src/reports.ts +23 -1
- package/src/types.ts +0 -1
- package/test/edifact-primadonna.json +0 -19
- package/test/reports.spec.ts +78 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface BarcodeCanvasResult {
|
|
2
|
+
dataUrl: string;
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Render a barcode to a PNG data URL.
|
|
8
|
+
* Module width is fixed; the caller must scale the image uniformly into the layout box
|
|
9
|
+
* so bars are not distorted.
|
|
10
|
+
*/
|
|
11
|
+
export declare function renderBarcodeToDataUrl(value: string): BarcodeCanvasResult | null;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.renderBarcodeToDataUrl = void 0;
|
|
7
|
+
const jsbarcode_1 = __importDefault(require("jsbarcode"));
|
|
8
|
+
/** Vite / CJS interop sometimes wraps the function as `{ default: fn }`. */
|
|
9
|
+
function resolveJsBarcode() {
|
|
10
|
+
const mod = jsbarcode_1.default;
|
|
11
|
+
if (typeof mod === 'function')
|
|
12
|
+
return mod;
|
|
13
|
+
if (mod && typeof mod.default === 'function')
|
|
14
|
+
return mod.default;
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
function createCanvasElement() {
|
|
18
|
+
if (typeof document !== 'undefined' && typeof document.createElement === 'function') {
|
|
19
|
+
return document.createElement('canvas');
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Render a barcode to a PNG data URL.
|
|
25
|
+
* Module width is fixed; the caller must scale the image uniformly into the layout box
|
|
26
|
+
* so bars are not distorted.
|
|
27
|
+
*/
|
|
28
|
+
function renderBarcodeToDataUrl(value) {
|
|
29
|
+
if (!value)
|
|
30
|
+
return null;
|
|
31
|
+
const canvas = createCanvasElement();
|
|
32
|
+
if (!canvas)
|
|
33
|
+
return null;
|
|
34
|
+
const JsBarcode = resolveJsBarcode();
|
|
35
|
+
if (!JsBarcode)
|
|
36
|
+
return null;
|
|
37
|
+
try {
|
|
38
|
+
JsBarcode(canvas, value, {
|
|
39
|
+
// Fixed module geometry — do not stretch independently in X/Y when placing on the page.
|
|
40
|
+
// CODE128 accepts arbitrary values (EAN auto-detect can throw on bad check digits).
|
|
41
|
+
format: 'CODE128',
|
|
42
|
+
width: 2,
|
|
43
|
+
height: 60,
|
|
44
|
+
displayValue: false,
|
|
45
|
+
margin: 0,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
if (typeof console !== 'undefined')
|
|
50
|
+
console.warn('Barcode render failed', value, e);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
if (!canvas.width || !canvas.height)
|
|
54
|
+
return null;
|
|
55
|
+
return {
|
|
56
|
+
dataUrl: canvas.toDataURL('image/png'),
|
|
57
|
+
width: canvas.width,
|
|
58
|
+
height: canvas.height,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
exports.renderBarcodeToDataUrl = renderBarcodeToDataUrl;
|
package/dist/pdf.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface PaperSize {
|
|
|
5
5
|
footerHeight: number;
|
|
6
6
|
}
|
|
7
7
|
export type TextAlign = 1 | 2 | 3;
|
|
8
|
-
export type ObjectType = 'text' | 'field' | 'rectangle' | 'image' | 'container';
|
|
8
|
+
export type ObjectType = 'text' | 'field' | 'rectangle' | 'image' | 'container' | 'barcode';
|
|
9
9
|
export type RepeatContainer = 'horizontal' | 'vertical' | undefined;
|
|
10
10
|
export interface BaseLayoutObject {
|
|
11
11
|
type: ObjectType;
|
|
@@ -44,6 +44,15 @@ export interface ImageLayoutObject extends BaseLayoutObject {
|
|
|
44
44
|
url?: string;
|
|
45
45
|
image?: HTMLImageElement;
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Renders a 1D barcode from printBuffer via `source` (default `barcode`).
|
|
49
|
+
* Drawn with uniform scale to fit width×height (never independently stretched).
|
|
50
|
+
*/
|
|
51
|
+
export interface BarcodeLayoutObject extends BaseLayoutObject {
|
|
52
|
+
type: 'barcode';
|
|
53
|
+
/** Path into the current print buffer item; defaults to `barcode`. */
|
|
54
|
+
source?: string;
|
|
55
|
+
}
|
|
47
56
|
export interface ContainerLayoutObject extends BaseLayoutObject {
|
|
48
57
|
type: 'container';
|
|
49
58
|
source: string;
|
|
@@ -51,7 +60,10 @@ export interface ContainerLayoutObject extends BaseLayoutObject {
|
|
|
51
60
|
repeatContainer?: RepeatContainer;
|
|
52
61
|
repeatOnOverflow?: boolean;
|
|
53
62
|
pageBreak?: boolean;
|
|
54
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Minimum remaining space before drawing the next item; if less, break to the next page first.
|
|
65
|
+
* Same units as layout x/y/width/height (tenths of mm), e.g. 220 ≈ 22mm.
|
|
66
|
+
*/
|
|
55
67
|
minHeightBeforeBreak?: number;
|
|
56
68
|
/** When true, container and children are drawn only once at the end of the report, not on every page */
|
|
57
69
|
printOnlyAtEnd?: boolean;
|
|
@@ -61,7 +73,7 @@ export interface ContainerLayoutObject extends BaseLayoutObject {
|
|
|
61
73
|
*/
|
|
62
74
|
printOnlyAtStart?: boolean;
|
|
63
75
|
}
|
|
64
|
-
export type LayoutObject = TextLayoutObject | FieldLayoutObject | RectangleLayoutObject | ImageLayoutObject | ContainerLayoutObject;
|
|
76
|
+
export type LayoutObject = TextLayoutObject | FieldLayoutObject | RectangleLayoutObject | ImageLayoutObject | BarcodeLayoutObject | ContainerLayoutObject;
|
|
65
77
|
export interface Layout {
|
|
66
78
|
name?: string;
|
|
67
79
|
group?: string;
|
package/dist/reports.js
CHANGED
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.genPDF = exports.computeImageDrawRect = void 0;
|
|
16
16
|
const jspdf_1 = require("jspdf");
|
|
17
17
|
const date_fns_1 = require("date-fns");
|
|
18
|
+
const barcodeCanvas_1 = require("./barcodeCanvas");
|
|
18
19
|
const deepCopy_1 = __importDefault(require("./deepCopy"));
|
|
19
20
|
const layoutFieldFormat_1 = require("./layoutFieldFormat");
|
|
20
21
|
/** Measurement uses context `currentPageNumber` (1-based). Rendering uses jsPDF’s `getCurrentPageInfo().pageNumber`, which is already 1-based (first page is 1). */
|
|
@@ -287,7 +288,7 @@ function truncateTextToFitWidth(doc, text, maxWidth) {
|
|
|
287
288
|
return truncatedText + ELLIPSIS;
|
|
288
289
|
}
|
|
289
290
|
function drawSimpleObject(x, y, doc, object, printBuffer, width, height, options, context) {
|
|
290
|
-
var _a, _b, _c, _d;
|
|
291
|
+
var _a, _b, _c, _d, _f;
|
|
291
292
|
let text = ''; // Will hold text to display for either 'text' or 'field'
|
|
292
293
|
// Get text from object for text objects
|
|
293
294
|
if (object.type == 'text') {
|
|
@@ -377,6 +378,28 @@ function drawSimpleObject(x, y, doc, object, printBuffer, width, height, options
|
|
|
377
378
|
}
|
|
378
379
|
}
|
|
379
380
|
}
|
|
381
|
+
// Draw barcode for barcode objects (uniform scale into the layout box — never stretch bars)
|
|
382
|
+
if (object.type == 'barcode') {
|
|
383
|
+
const barcodeObj = object;
|
|
384
|
+
const source = barcodeObj.source || 'barcode';
|
|
385
|
+
const raw = getProperty(source, printBuffer);
|
|
386
|
+
const value = raw == null ? '' : String(raw);
|
|
387
|
+
if (!value) {
|
|
388
|
+
(_d = options.onAlert) === null || _d === void 0 ? void 0 : _d.call(options, { header: 'fieldnotfound', body: source, type: 'warning', timeout: 10000 });
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
const rendered = (0, barcodeCanvas_1.renderBarcodeToDataUrl)(value);
|
|
392
|
+
if (rendered) {
|
|
393
|
+
const { x: drawX, y: drawY, width: drawWidth, height: drawHeight } = computeImageDrawRect(x, y, width, height, rendered.width, rendered.height);
|
|
394
|
+
try {
|
|
395
|
+
doc.addImage(rendered.dataUrl, 'PNG', drawX, drawY, drawWidth, drawHeight);
|
|
396
|
+
}
|
|
397
|
+
catch (_e) {
|
|
398
|
+
// Skip if jsPDF cannot embed the canvas data URL
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
380
403
|
// Draw text for text and field objets
|
|
381
404
|
if ((object.type == 'text' || object.type == 'field') && text) {
|
|
382
405
|
let textAlign;
|
|
@@ -394,7 +417,7 @@ function drawSimpleObject(x, y, doc, object, printBuffer, width, height, options
|
|
|
394
417
|
return;
|
|
395
418
|
const fontStyleStr = FONT_STYLES[object.fontStyle & 3] || 'normal';
|
|
396
419
|
// Use object font if it's a custom font; otherwise layout default overrides standard fonts (Helvetica etc.)
|
|
397
|
-
const fontFamily = object.fontFamily && ((
|
|
420
|
+
const fontFamily = object.fontFamily && ((_f = options.isCustomPdfFont) === null || _f === void 0 ? void 0 : _f.call(options, object.fontFamily)) ? object.fontFamily : context.defaultFontFamily || object.fontFamily || 'Helvetica';
|
|
398
421
|
doc.setFont(fontFamily, fontStyleStr);
|
|
399
422
|
doc.setFontSize(object.fontSize / 4);
|
|
400
423
|
doc.text(truncateTextToFitWidth(doc, text, width), x, y, { align: textAlign, baseline: 'hanging' });
|
|
@@ -531,9 +554,10 @@ addObjectToPDF = function (originX, originY, doc, object, printBuffer, paperSize
|
|
|
531
554
|
});
|
|
532
555
|
const container = source[containerIndex];
|
|
533
556
|
absoluteLowerRightHandSide = { x: originX / 10, y: originY / 10 };
|
|
534
|
-
|
|
535
|
-
const
|
|
536
|
-
|
|
557
|
+
// Layout units (tenths of mm), same as originY / paperSize after genPDF scales them ×10.
|
|
558
|
+
const minHeightBeforeBreak = object.minHeightBeforeBreak;
|
|
559
|
+
const useOrphanCheck = minHeightBeforeBreak != null && minHeightBeforeBreak > 0;
|
|
560
|
+
if (useOrphanCheck && (object.pageBreak || originY + minHeightBeforeBreak > paperSize.height - paperSize.footerHeight)) {
|
|
537
561
|
// We ran out of paper
|
|
538
562
|
originX = layoutXtenths;
|
|
539
563
|
// Continuation sheets must not reuse the page-1-only overlap bump: addPage hasn't run yet so getCurrentPageNumber is still 1.
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softwear/latestcollectioncore",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.206",
|
|
4
4
|
"description": "Core functions for LatestCollections applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"date-fns": "^2.29.3",
|
|
40
40
|
"filtrex": "^2.2.3",
|
|
41
|
+
"jsbarcode": "^3.12.3",
|
|
41
42
|
"jspdf": "^4.2.1"
|
|
42
43
|
}
|
|
43
44
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import JsBarcodeImport from 'jsbarcode'
|
|
2
|
+
|
|
3
|
+
export interface BarcodeCanvasResult {
|
|
4
|
+
dataUrl: string
|
|
5
|
+
width: number
|
|
6
|
+
height: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Vite / CJS interop sometimes wraps the function as `{ default: fn }`. */
|
|
10
|
+
function resolveJsBarcode(): ((element: unknown, value: string, options?: object) => void) | null {
|
|
11
|
+
const mod: any = JsBarcodeImport
|
|
12
|
+
if (typeof mod === 'function') return mod
|
|
13
|
+
if (mod && typeof mod.default === 'function') return mod.default
|
|
14
|
+
return null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function createCanvasElement(): HTMLCanvasElement | null {
|
|
18
|
+
if (typeof document !== 'undefined' && typeof document.createElement === 'function') {
|
|
19
|
+
return document.createElement('canvas')
|
|
20
|
+
}
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Render a barcode to a PNG data URL.
|
|
26
|
+
* Module width is fixed; the caller must scale the image uniformly into the layout box
|
|
27
|
+
* so bars are not distorted.
|
|
28
|
+
*/
|
|
29
|
+
export function renderBarcodeToDataUrl(value: string): BarcodeCanvasResult | null {
|
|
30
|
+
if (!value) return null
|
|
31
|
+
const canvas = createCanvasElement()
|
|
32
|
+
if (!canvas) return null
|
|
33
|
+
|
|
34
|
+
const JsBarcode = resolveJsBarcode()
|
|
35
|
+
if (!JsBarcode) return null
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
JsBarcode(canvas, value, {
|
|
39
|
+
// Fixed module geometry — do not stretch independently in X/Y when placing on the page.
|
|
40
|
+
// CODE128 accepts arbitrary values.
|
|
41
|
+
format: 'CODE128',
|
|
42
|
+
width: 2,
|
|
43
|
+
height: 60,
|
|
44
|
+
displayValue: false,
|
|
45
|
+
margin: 0,
|
|
46
|
+
})
|
|
47
|
+
} catch (e) {
|
|
48
|
+
if (typeof console !== 'undefined') console.warn('Barcode render failed', value, e)
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!canvas.width || !canvas.height) return null
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
dataUrl: canvas.toDataURL('image/png'),
|
|
56
|
+
width: canvas.width,
|
|
57
|
+
height: canvas.height,
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/pdf.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface PaperSize {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export type TextAlign = 1 | 2 | 3 // 1: left, 2: center, 3: right
|
|
10
|
-
export type ObjectType = 'text' | 'field' | 'rectangle' | 'image' | 'container'
|
|
10
|
+
export type ObjectType = 'text' | 'field' | 'rectangle' | 'image' | 'container' | 'barcode'
|
|
11
11
|
export type RepeatContainer = 'horizontal' | 'vertical' | undefined
|
|
12
12
|
|
|
13
13
|
export interface BaseLayoutObject {
|
|
@@ -52,6 +52,15 @@ export interface ImageLayoutObject extends BaseLayoutObject {
|
|
|
52
52
|
image?: HTMLImageElement
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Renders a 1D barcode from printBuffer via `source` (default `barcode`).
|
|
57
|
+
* Drawn with uniform scale to fit width×height (never independently stretched).
|
|
58
|
+
*/
|
|
59
|
+
export interface BarcodeLayoutObject extends BaseLayoutObject {
|
|
60
|
+
type: 'barcode'
|
|
61
|
+
source?: string
|
|
62
|
+
}
|
|
63
|
+
|
|
55
64
|
export interface ContainerLayoutObject extends BaseLayoutObject {
|
|
56
65
|
type: 'container'
|
|
57
66
|
source: string
|
|
@@ -70,7 +79,7 @@ export interface ContainerLayoutObject extends BaseLayoutObject {
|
|
|
70
79
|
printOnlyAtStart?: boolean
|
|
71
80
|
}
|
|
72
81
|
|
|
73
|
-
export type LayoutObject = TextLayoutObject | FieldLayoutObject | RectangleLayoutObject | ImageLayoutObject | ContainerLayoutObject
|
|
82
|
+
export type LayoutObject = TextLayoutObject | FieldLayoutObject | RectangleLayoutObject | ImageLayoutObject | BarcodeLayoutObject | ContainerLayoutObject
|
|
74
83
|
|
|
75
84
|
export interface Layout {
|
|
76
85
|
name?: string
|
package/src/reports.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsPDF } from 'jspdf'
|
|
2
2
|
import { format } from 'date-fns'
|
|
3
|
+
import { renderBarcodeToDataUrl } from './barcodeCanvas'
|
|
3
4
|
import deepCopy from './deepCopy'
|
|
4
5
|
import { resolveLayoutFieldText } from './layoutFieldFormat'
|
|
5
|
-
import { PaperSize, Layout, LayoutObject, ContainerLayoutObject } from './pdf'
|
|
6
|
+
import { PaperSize, Layout, LayoutObject, ContainerLayoutObject, BarcodeLayoutObject } from './pdf'
|
|
6
7
|
|
|
7
8
|
type RenderMode = 'measurement' | 'rendering'
|
|
8
9
|
|
|
@@ -453,6 +454,27 @@ function drawSimpleObject(x: number, y: number, doc: jsPDF, object: LayoutObject
|
|
|
453
454
|
}
|
|
454
455
|
}
|
|
455
456
|
|
|
457
|
+
// Draw barcode for barcode objects (uniform scale into the layout box — never stretch bars)
|
|
458
|
+
if (object.type == 'barcode') {
|
|
459
|
+
const barcodeObj = object as BarcodeLayoutObject
|
|
460
|
+
const source = barcodeObj.source || 'barcode'
|
|
461
|
+
const raw = getProperty(source, printBuffer)
|
|
462
|
+
const value = raw == null ? '' : String(raw)
|
|
463
|
+
if (!value) {
|
|
464
|
+
options.onAlert?.({ header: 'fieldnotfound', body: source, type: 'warning', timeout: 10000 })
|
|
465
|
+
} else {
|
|
466
|
+
const rendered = renderBarcodeToDataUrl(value)
|
|
467
|
+
if (rendered) {
|
|
468
|
+
const { x: drawX, y: drawY, width: drawWidth, height: drawHeight } = computeImageDrawRect(x, y, width, height, rendered.width, rendered.height)
|
|
469
|
+
try {
|
|
470
|
+
doc.addImage(rendered.dataUrl, 'PNG', drawX, drawY, drawWidth, drawHeight)
|
|
471
|
+
} catch (_e) {
|
|
472
|
+
// Skip if jsPDF cannot embed the canvas data URL
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
456
478
|
// Draw text for text and field objets
|
|
457
479
|
if ((object.type == 'text' || object.type == 'field') && text) {
|
|
458
480
|
let textAlign: 'left' | 'center' | 'right' | undefined
|
package/src/types.ts
CHANGED
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
"source": "latestCollection",
|
|
26
26
|
"mainColorCode": "",
|
|
27
27
|
"mainColorDescription": "",
|
|
28
|
-
"skuActive": true,
|
|
29
28
|
"valuePrice": 37.66,
|
|
30
29
|
"supplierName": "",
|
|
31
30
|
"sizeSupplier": "75C",
|
|
@@ -63,7 +62,6 @@
|
|
|
63
62
|
"source": "latestCollection",
|
|
64
63
|
"mainColorCode": "",
|
|
65
64
|
"mainColorDescription": "",
|
|
66
|
-
"skuActive": true,
|
|
67
65
|
"valuePrice": 37.66,
|
|
68
66
|
"supplierName": "",
|
|
69
67
|
"sizeSupplier": "80C",
|
|
@@ -101,7 +99,6 @@
|
|
|
101
99
|
"source": "latestCollection",
|
|
102
100
|
"mainColorCode": "",
|
|
103
101
|
"mainColorDescription": "",
|
|
104
|
-
"skuActive": true,
|
|
105
102
|
"valuePrice": 37.66,
|
|
106
103
|
"supplierName": "",
|
|
107
104
|
"sizeSupplier": "85C",
|
|
@@ -139,7 +136,6 @@
|
|
|
139
136
|
"source": "latestCollection",
|
|
140
137
|
"mainColorCode": "",
|
|
141
138
|
"mainColorDescription": "",
|
|
142
|
-
"skuActive": true,
|
|
143
139
|
"valuePrice": 37.66,
|
|
144
140
|
"supplierName": "",
|
|
145
141
|
"sizeSupplier": "90C",
|
|
@@ -177,7 +173,6 @@
|
|
|
177
173
|
"source": "latestCollection",
|
|
178
174
|
"mainColorCode": "",
|
|
179
175
|
"mainColorDescription": "",
|
|
180
|
-
"skuActive": true,
|
|
181
176
|
"valuePrice": 37.66,
|
|
182
177
|
"supplierName": "",
|
|
183
178
|
"sizeSupplier": "95C",
|
|
@@ -215,7 +210,6 @@
|
|
|
215
210
|
"source": "latestCollection",
|
|
216
211
|
"mainColorCode": "",
|
|
217
212
|
"mainColorDescription": "",
|
|
218
|
-
"skuActive": true,
|
|
219
213
|
"valuePrice": 37.66,
|
|
220
214
|
"supplierName": "",
|
|
221
215
|
"sizeSupplier": "100C",
|
|
@@ -253,7 +247,6 @@
|
|
|
253
247
|
"source": "latestCollection",
|
|
254
248
|
"mainColorCode": "",
|
|
255
249
|
"mainColorDescription": "",
|
|
256
|
-
"skuActive": true,
|
|
257
250
|
"valuePrice": 37.66,
|
|
258
251
|
"supplierName": "",
|
|
259
252
|
"sizeSupplier": "105C",
|
|
@@ -291,7 +284,6 @@
|
|
|
291
284
|
"source": "latestCollection",
|
|
292
285
|
"mainColorCode": "",
|
|
293
286
|
"mainColorDescription": "",
|
|
294
|
-
"skuActive": true,
|
|
295
287
|
"valuePrice": 37.66,
|
|
296
288
|
"supplierName": "",
|
|
297
289
|
"sizeSupplier": "75C",
|
|
@@ -329,7 +321,6 @@
|
|
|
329
321
|
"source": "latestCollection",
|
|
330
322
|
"mainColorCode": "",
|
|
331
323
|
"mainColorDescription": "",
|
|
332
|
-
"skuActive": true,
|
|
333
324
|
"valuePrice": 37.66,
|
|
334
325
|
"supplierName": "",
|
|
335
326
|
"sizeSupplier": "80C",
|
|
@@ -367,7 +358,6 @@
|
|
|
367
358
|
"source": "latestCollection",
|
|
368
359
|
"mainColorCode": "",
|
|
369
360
|
"mainColorDescription": "",
|
|
370
|
-
"skuActive": true,
|
|
371
361
|
"valuePrice": 37.66,
|
|
372
362
|
"supplierName": "",
|
|
373
363
|
"sizeSupplier": "85C",
|
|
@@ -405,7 +395,6 @@
|
|
|
405
395
|
"source": "latestCollection",
|
|
406
396
|
"mainColorCode": "",
|
|
407
397
|
"mainColorDescription": "",
|
|
408
|
-
"skuActive": true,
|
|
409
398
|
"valuePrice": 37.66,
|
|
410
399
|
"supplierName": "",
|
|
411
400
|
"sizeSupplier": "90C",
|
|
@@ -443,7 +432,6 @@
|
|
|
443
432
|
"source": "latestCollection",
|
|
444
433
|
"mainColorCode": "",
|
|
445
434
|
"mainColorDescription": "",
|
|
446
|
-
"skuActive": true,
|
|
447
435
|
"valuePrice": 37.66,
|
|
448
436
|
"supplierName": "",
|
|
449
437
|
"sizeSupplier": "95C",
|
|
@@ -481,7 +469,6 @@
|
|
|
481
469
|
"source": "latestCollection",
|
|
482
470
|
"mainColorCode": "",
|
|
483
471
|
"mainColorDescription": "",
|
|
484
|
-
"skuActive": true,
|
|
485
472
|
"valuePrice": 18.81,
|
|
486
473
|
"supplierName": "",
|
|
487
474
|
"sizeSupplier": "40",
|
|
@@ -518,7 +505,6 @@
|
|
|
518
505
|
"source": "latestCollection",
|
|
519
506
|
"mainColorCode": "",
|
|
520
507
|
"mainColorDescription": "",
|
|
521
|
-
"skuActive": true,
|
|
522
508
|
"valuePrice": 18.81,
|
|
523
509
|
"supplierName": "",
|
|
524
510
|
"sizeSupplier": "42",
|
|
@@ -555,7 +541,6 @@
|
|
|
555
541
|
"source": "latestCollection",
|
|
556
542
|
"mainColorCode": "",
|
|
557
543
|
"mainColorDescription": "",
|
|
558
|
-
"skuActive": true,
|
|
559
544
|
"valuePrice": 18.81,
|
|
560
545
|
"supplierName": "",
|
|
561
546
|
"sizeSupplier": "44",
|
|
@@ -592,7 +577,6 @@
|
|
|
592
577
|
"source": "latestCollection",
|
|
593
578
|
"mainColorCode": "",
|
|
594
579
|
"mainColorDescription": "",
|
|
595
|
-
"skuActive": true,
|
|
596
580
|
"valuePrice": 18.81,
|
|
597
581
|
"supplierName": "",
|
|
598
582
|
"sizeSupplier": "46",
|
|
@@ -629,7 +613,6 @@
|
|
|
629
613
|
"source": "latestCollection",
|
|
630
614
|
"mainColorCode": "",
|
|
631
615
|
"mainColorDescription": "",
|
|
632
|
-
"skuActive": true,
|
|
633
616
|
"valuePrice": 18.81,
|
|
634
617
|
"supplierName": "",
|
|
635
618
|
"sizeSupplier": "48",
|
|
@@ -666,7 +649,6 @@
|
|
|
666
649
|
"source": "latestCollection",
|
|
667
650
|
"mainColorCode": "",
|
|
668
651
|
"mainColorDescription": "",
|
|
669
|
-
"skuActive": true,
|
|
670
652
|
"valuePrice": 18.81,
|
|
671
653
|
"supplierName": "",
|
|
672
654
|
"sizeSupplier": "50",
|
|
@@ -703,7 +685,6 @@
|
|
|
703
685
|
"source": "latestCollection",
|
|
704
686
|
"mainColorCode": "",
|
|
705
687
|
"mainColorDescription": "",
|
|
706
|
-
"skuActive": true,
|
|
707
688
|
"valuePrice": 18.81,
|
|
708
689
|
"supplierName": "",
|
|
709
690
|
"sizeSupplier": "52",
|
package/test/reports.spec.ts
CHANGED
|
@@ -455,4 +455,82 @@ describe('genPDF', () => {
|
|
|
455
455
|
const pdfText = Buffer.from(doc.output('arraybuffer')).toString('latin1')
|
|
456
456
|
expect(pdfText).toMatch(/\(\s*.\s*5\.00\) Tj/)
|
|
457
457
|
})
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Check multi-page vertical repeat with minHeightBeforeBreak.
|
|
461
|
+
*/
|
|
462
|
+
function barcodeSheetLayout(): Layout {
|
|
463
|
+
return {
|
|
464
|
+
name: 'barcodesheet',
|
|
465
|
+
paperSize: { width: 210, height: 297, footerHeight: 10 },
|
|
466
|
+
objects: [
|
|
467
|
+
{
|
|
468
|
+
type: 'container',
|
|
469
|
+
name: 'items',
|
|
470
|
+
source: 'items',
|
|
471
|
+
x: 100,
|
|
472
|
+
y: 100,
|
|
473
|
+
width: 1900,
|
|
474
|
+
height: 220,
|
|
475
|
+
active: true,
|
|
476
|
+
repeatContainer: 'vertical',
|
|
477
|
+
minHeightBeforeBreak: 220,
|
|
478
|
+
children: [
|
|
479
|
+
{
|
|
480
|
+
type: 'barcode',
|
|
481
|
+
name: 'barcode',
|
|
482
|
+
source: 'barcode',
|
|
483
|
+
x: 0,
|
|
484
|
+
y: 20,
|
|
485
|
+
width: 900,
|
|
486
|
+
height: 160,
|
|
487
|
+
active: true,
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
type: 'field',
|
|
491
|
+
name: 'barcodeText',
|
|
492
|
+
source: 'barcode',
|
|
493
|
+
x: 0,
|
|
494
|
+
y: 185,
|
|
495
|
+
width: 900,
|
|
496
|
+
height: 30,
|
|
497
|
+
active: true,
|
|
498
|
+
fontFamily: 'Helvetica',
|
|
499
|
+
fontSize: 20,
|
|
500
|
+
fontStyle: 0,
|
|
501
|
+
textAlign: 2,
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
},
|
|
505
|
+
],
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function barcodeMarksOnPage(doc: Awaited<ReturnType<typeof genPDF>>, page: number): string[] {
|
|
510
|
+
const pageOps = (doc as any).internal.pages[page] as string[] | undefined
|
|
511
|
+
if (!pageOps) return []
|
|
512
|
+
return [...pageOps.join('\n').matchAll(/\(BC-\d{3}\)/g)].map((m) => m[0].slice(1, -1))
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
it('keeps barcode rows that fit under minHeightBeforeBreak on a single page', async () => {
|
|
516
|
+
const items = Array.from({ length: 12 }, (_, i) => ({ barcode: `BC-${String(i).padStart(3, '0')}` }))
|
|
517
|
+
const doc = await genPDF(barcodeSheetLayout(), { items })
|
|
518
|
+
expect(doc.getNumberOfPages()).toBe(1)
|
|
519
|
+
expect(barcodeMarksOnPage(doc, 1)).toEqual(items.map((item) => item.barcode))
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
it('breaks barcode rows before drawing when minHeightBeforeBreak would overflow', async () => {
|
|
523
|
+
const items = Array.from({ length: 15 }, (_, i) => ({ barcode: `BC-${String(i).padStart(3, '0')}` }))
|
|
524
|
+
const doc = await genPDF(barcodeSheetLayout(), { items })
|
|
525
|
+
|
|
526
|
+
expect(doc.getNumberOfPages()).toBe(2)
|
|
527
|
+
expect(barcodeMarksOnPage(doc, 1)).toEqual(items.slice(0, 12).map((item) => item.barcode))
|
|
528
|
+
expect(barcodeMarksOnPage(doc, 2)).toEqual(items.slice(12).map((item) => item.barcode))
|
|
529
|
+
|
|
530
|
+
const pdfText = Buffer.from(doc.output('arraybuffer')).toString('latin1')
|
|
531
|
+
for (const item of items) {
|
|
532
|
+
const matches = pdfText.match(new RegExp(`\\(${item.barcode}\\)`, 'g'))
|
|
533
|
+
expect(matches, `${item.barcode} should appear exactly once`).toHaveLength(1)
|
|
534
|
+
}
|
|
535
|
+
})
|
|
458
536
|
})
|