@softwear/latestcollectioncore 1.0.194 → 1.0.195
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.d.ts +7 -0
- package/dist/reports.js +22 -16
- package/package.json +1 -1
- package/src/reports.ts +35 -13
- package/test/reports.spec.ts +13 -1
package/dist/reports.d.ts
CHANGED
|
@@ -36,6 +36,13 @@ export interface GenPdfOptions {
|
|
|
36
36
|
loadFont?: (url: string) => Promise<string>;
|
|
37
37
|
loadImage?: (url: string) => Promise<PdfImageAsset | undefined>;
|
|
38
38
|
}
|
|
39
|
+
/** Fit image in layout box preserving aspect ratio (like CSS object-fit: contain). Stretch only when objectFit is fill/stretch. */
|
|
40
|
+
export declare function computeImageDrawRect(x: number, y: number, boxWidth: number, boxHeight: number, imageWidth: number, imageHeight: number, objectFit?: string): {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
};
|
|
39
46
|
export declare function genPDF(layout: Layout, printBuffer: any, options?: GenPdfOptions): Promise<jsPDF>;
|
|
40
47
|
declare const _default: {
|
|
41
48
|
genPDF: typeof genPDF;
|
package/dist/reports.js
CHANGED
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.genPDF = void 0;
|
|
15
|
+
exports.genPDF = exports.computeImageDrawRect = void 0;
|
|
16
16
|
const jspdf_1 = require("jspdf");
|
|
17
17
|
const date_fns_1 = require("date-fns");
|
|
18
18
|
const deepCopy_1 = __importDefault(require("./deepCopy"));
|
|
@@ -152,6 +152,23 @@ function getImageFormat(url, contentType, bytes) {
|
|
|
152
152
|
function getImageMimeType(format) {
|
|
153
153
|
return format === 'PNG' ? 'image/png' : 'image/jpeg';
|
|
154
154
|
}
|
|
155
|
+
/** Fit image in layout box preserving aspect ratio (like CSS object-fit: contain). Stretch only when objectFit is fill/stretch. */
|
|
156
|
+
function computeImageDrawRect(x, y, boxWidth, boxHeight, imageWidth, imageHeight, objectFit) {
|
|
157
|
+
let drawX = x;
|
|
158
|
+
let drawY = y;
|
|
159
|
+
let drawWidth = boxWidth;
|
|
160
|
+
let drawHeight = boxHeight;
|
|
161
|
+
const stretch = objectFit === 'fill' || objectFit === 'stretch';
|
|
162
|
+
if (!stretch && imageWidth > 0 && imageHeight > 0) {
|
|
163
|
+
const scale = Math.min(boxWidth / imageWidth, boxHeight / imageHeight);
|
|
164
|
+
drawWidth = imageWidth * scale;
|
|
165
|
+
drawHeight = imageHeight * scale;
|
|
166
|
+
drawX = x + (boxWidth - drawWidth) / 2;
|
|
167
|
+
drawY = y + (boxHeight - drawHeight) / 2;
|
|
168
|
+
}
|
|
169
|
+
return { x: drawX, y: drawY, width: drawWidth, height: drawHeight };
|
|
170
|
+
}
|
|
171
|
+
exports.computeImageDrawRect = computeImageDrawRect;
|
|
155
172
|
function getImageDimensions(bytes, format) {
|
|
156
173
|
if (format === 'PNG' && bytes.length >= 24 && isPng(bytes)) {
|
|
157
174
|
return { width: readUint32BE(bytes, 16), height: readUint32BE(bytes, 20) };
|
|
@@ -270,7 +287,7 @@ function truncateTextToFitWidth(doc, text, maxWidth) {
|
|
|
270
287
|
return truncatedText + ELLIPSIS;
|
|
271
288
|
}
|
|
272
289
|
function drawSimpleObject(x, y, doc, object, printBuffer, width, height, options, context) {
|
|
273
|
-
var _a, _b;
|
|
290
|
+
var _a, _b, _c, _d;
|
|
274
291
|
let text = ''; // Will hold text to display for either 'text' or 'field'
|
|
275
292
|
// Get text from object for text objects
|
|
276
293
|
if (object.type == 'text') {
|
|
@@ -351,18 +368,7 @@ function drawSimpleObject(x, y, doc, object, printBuffer, width, height, options
|
|
|
351
368
|
const urlLower = String(imgUrl).toLowerCase();
|
|
352
369
|
const imageAsset = (_a = context.imageAssets) === null || _a === void 0 ? void 0 : _a.get(String(imgUrl));
|
|
353
370
|
const format = (imageAsset === null || imageAsset === void 0 ? void 0 : imageAsset.format) || (urlLower.endsWith('.png') ? 'PNG' : urlLower.endsWith('.jpg') || urlLower.endsWith('.jpeg') ? 'JPEG' : 'JPEG');
|
|
354
|
-
|
|
355
|
-
let drawY = y;
|
|
356
|
-
let drawWidth = width;
|
|
357
|
-
let drawHeight = height;
|
|
358
|
-
// Preserve aspect ratio when objectFit is 'contain' and we have dimensions
|
|
359
|
-
if (imgObj.objectFit === 'contain' && (imageAsset === null || imageAsset === void 0 ? void 0 : imageAsset.width) && (imageAsset === null || imageAsset === void 0 ? void 0 : imageAsset.height)) {
|
|
360
|
-
const scale = Math.min(width / imageAsset.width, height / imageAsset.height);
|
|
361
|
-
drawWidth = imageAsset.width * scale;
|
|
362
|
-
drawHeight = imageAsset.height * scale;
|
|
363
|
-
drawX = x + (width - drawWidth) / 2;
|
|
364
|
-
drawY = y + (height - drawHeight) / 2;
|
|
365
|
-
}
|
|
371
|
+
const { x: drawX, y: drawY, width: drawWidth, height: drawHeight } = computeImageDrawRect(x, y, width, height, (_b = imageAsset === null || imageAsset === void 0 ? void 0 : imageAsset.width) !== null && _b !== void 0 ? _b : 0, (_c = imageAsset === null || imageAsset === void 0 ? void 0 : imageAsset.height) !== null && _c !== void 0 ? _c : 0, imgObj.objectFit);
|
|
366
372
|
try {
|
|
367
373
|
doc.addImage((imageAsset === null || imageAsset === void 0 ? void 0 : imageAsset.dataUrl) || imgUrl, format, drawX, drawY, drawWidth, drawHeight);
|
|
368
374
|
}
|
|
@@ -388,7 +394,7 @@ function drawSimpleObject(x, y, doc, object, printBuffer, width, height, options
|
|
|
388
394
|
return;
|
|
389
395
|
const fontStyleStr = FONT_STYLES[object.fontStyle & 3] || 'normal';
|
|
390
396
|
// Use object font if it's a custom font; otherwise layout default overrides standard fonts (Helvetica etc.)
|
|
391
|
-
const fontFamily = object.fontFamily && ((
|
|
397
|
+
const fontFamily = object.fontFamily && ((_d = options.isCustomPdfFont) === null || _d === void 0 ? void 0 : _d.call(options, object.fontFamily)) ? object.fontFamily : context.defaultFontFamily || object.fontFamily || 'Helvetica';
|
|
392
398
|
doc.setFont(fontFamily, fontStyleStr);
|
|
393
399
|
doc.setFontSize(object.fontSize / 4);
|
|
394
400
|
doc.text(truncateTextToFitWidth(doc, text, width), x, y, { align: textAlign, baseline: 'hanging' });
|
|
@@ -686,7 +692,7 @@ function genPDF(layout, printBuffer, options = {}) {
|
|
|
686
692
|
object.url = logoUrl;
|
|
687
693
|
}
|
|
688
694
|
});
|
|
689
|
-
// Preload image bytes for Node.js and dimensions for
|
|
695
|
+
// Preload image bytes for Node.js and pixel dimensions for aspect-ratio fitting
|
|
690
696
|
const imageAssets = new Map();
|
|
691
697
|
const loadImage = options.loadImage || loadImageForPdf;
|
|
692
698
|
const urls = collectImageUrlsFromLayout(layout, printBuffer);
|
package/package.json
CHANGED
package/src/reports.ts
CHANGED
|
@@ -219,6 +219,31 @@ function getImageMimeType(format: 'PNG' | 'JPEG'): string {
|
|
|
219
219
|
return format === 'PNG' ? 'image/png' : 'image/jpeg'
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
/** Fit image in layout box preserving aspect ratio (like CSS object-fit: contain). Stretch only when objectFit is fill/stretch. */
|
|
223
|
+
export function computeImageDrawRect(
|
|
224
|
+
x: number,
|
|
225
|
+
y: number,
|
|
226
|
+
boxWidth: number,
|
|
227
|
+
boxHeight: number,
|
|
228
|
+
imageWidth: number,
|
|
229
|
+
imageHeight: number,
|
|
230
|
+
objectFit?: string
|
|
231
|
+
): { x: number; y: number; width: number; height: number } {
|
|
232
|
+
let drawX = x
|
|
233
|
+
let drawY = y
|
|
234
|
+
let drawWidth = boxWidth
|
|
235
|
+
let drawHeight = boxHeight
|
|
236
|
+
const stretch = objectFit === 'fill' || objectFit === 'stretch'
|
|
237
|
+
if (!stretch && imageWidth > 0 && imageHeight > 0) {
|
|
238
|
+
const scale = Math.min(boxWidth / imageWidth, boxHeight / imageHeight)
|
|
239
|
+
drawWidth = imageWidth * scale
|
|
240
|
+
drawHeight = imageHeight * scale
|
|
241
|
+
drawX = x + (boxWidth - drawWidth) / 2
|
|
242
|
+
drawY = y + (boxHeight - drawHeight) / 2
|
|
243
|
+
}
|
|
244
|
+
return { x: drawX, y: drawY, width: drawWidth, height: drawHeight }
|
|
245
|
+
}
|
|
246
|
+
|
|
222
247
|
function getImageDimensions(bytes: Uint8Array, format: 'PNG' | 'JPEG'): { width?: number; height?: number } {
|
|
223
248
|
if (format === 'PNG' && bytes.length >= 24 && isPng(bytes)) {
|
|
224
249
|
return { width: readUint32BE(bytes, 16), height: readUint32BE(bytes, 20) }
|
|
@@ -411,18 +436,15 @@ function drawSimpleObject(x: number, y: number, doc: jsPDF, object: LayoutObject
|
|
|
411
436
|
const urlLower = String(imgUrl).toLowerCase()
|
|
412
437
|
const imageAsset = context.imageAssets?.get(String(imgUrl))
|
|
413
438
|
const format = imageAsset?.format || (urlLower.endsWith('.png') ? 'PNG' : urlLower.endsWith('.jpg') || urlLower.endsWith('.jpeg') ? 'JPEG' : 'JPEG')
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
drawX = x + (width - drawWidth) / 2
|
|
424
|
-
drawY = y + (height - drawHeight) / 2
|
|
425
|
-
}
|
|
439
|
+
const { x: drawX, y: drawY, width: drawWidth, height: drawHeight } = computeImageDrawRect(
|
|
440
|
+
x,
|
|
441
|
+
y,
|
|
442
|
+
width,
|
|
443
|
+
height,
|
|
444
|
+
imageAsset?.width ?? 0,
|
|
445
|
+
imageAsset?.height ?? 0,
|
|
446
|
+
imgObj.objectFit
|
|
447
|
+
)
|
|
426
448
|
try {
|
|
427
449
|
doc.addImage(imageAsset?.dataUrl || imgUrl, format, drawX, drawY, drawWidth, drawHeight)
|
|
428
450
|
} catch (_e) {
|
|
@@ -773,7 +795,7 @@ export async function genPDF(layout: Layout, printBuffer: any, options: GenPdfOp
|
|
|
773
795
|
if (logoUrl) object.url = logoUrl
|
|
774
796
|
}
|
|
775
797
|
})
|
|
776
|
-
// Preload image bytes for Node.js and dimensions for
|
|
798
|
+
// Preload image bytes for Node.js and pixel dimensions for aspect-ratio fitting
|
|
777
799
|
const imageAssets = new Map<string, PdfImageAsset>()
|
|
778
800
|
const loadImage = options.loadImage || loadImageForPdf
|
|
779
801
|
const urls = collectImageUrlsFromLayout(layout, printBuffer)
|
package/test/reports.spec.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { genPDF } from '../src/reports'
|
|
2
|
+
import { computeImageDrawRect, genPDF } from '../src/reports'
|
|
3
3
|
import type { Layout } from '../src/pdf'
|
|
4
4
|
|
|
5
|
+
describe('computeImageDrawRect', () => {
|
|
6
|
+
it('preserves aspect ratio by default (object-fit: contain)', () => {
|
|
7
|
+
const rect = computeImageDrawRect(10, 20, 40, 30, 500, 500)
|
|
8
|
+
expect(rect).toEqual({ x: 15, y: 20, width: 30, height: 30 })
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('stretches when objectFit is fill', () => {
|
|
12
|
+
const rect = computeImageDrawRect(10, 20, 40, 30, 500, 375, 'fill')
|
|
13
|
+
expect(rect).toEqual({ x: 10, y: 20, width: 40, height: 30 })
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
|
|
5
17
|
/** Tiny valid JPEG as data URL — avoids flaky network-dependent image fetch in CI/sandbox. */
|
|
6
18
|
function offlineJpegFixture(): string {
|
|
7
19
|
const b64 =
|