@pdfme/schemas 6.1.13-dev.25 → 6.1.13-dev.26
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/graphics/orientation.d.ts +31 -0
- package/dist/index.js +209 -74
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type ImageFormat = 'jpeg' | 'png' | 'other';
|
|
2
|
+
export declare const detectImageFormat: (bytes: Uint8Array) => ImageFormat;
|
|
3
|
+
/** TIFF/IFD0: returns 1..8 or undefined. Never throws. */
|
|
4
|
+
export declare const parseTiffOrientation: (tiff: Uint8Array) => number | undefined;
|
|
5
|
+
/**
|
|
6
|
+
* JPEG: scan APP1 'Exif\0\0' segments; stop at SOS/EOI.
|
|
7
|
+
* Skips 0xFF fill/padding and standalone markers. If an Exif APP1 has no
|
|
8
|
+
* valid Orientation, keep scanning for a later one.
|
|
9
|
+
*/
|
|
10
|
+
export declare const getJpegOrientation: (bytes: Uint8Array) => number | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* PNG: scan chunks for eXIf (raw TIFF payload). Skip IDAT and other
|
|
13
|
+
* ancillary chunks; eXIf may appear after image data. Stop at IEND.
|
|
14
|
+
*/
|
|
15
|
+
export declare const getPngOrientation: (bytes: Uint8Array) => number | undefined;
|
|
16
|
+
export declare const orientationSwapsDimensions: (orientation: number) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Map source (x, y) into destination (dx, dy) for EXIF orientations 2–8.
|
|
19
|
+
* Orientations 5–8 swap width and height.
|
|
20
|
+
*/
|
|
21
|
+
export declare const transformRgba: (src: Uint8Array, width: number, height: number, orientation: number) => {
|
|
22
|
+
data: Uint8Array;
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* One normalize path for JPEG and PNG. Tag absent/1 is a byte-identical
|
|
28
|
+
* passthrough. Tag 2–8 decodes, transforms pixels, and re-encodes without
|
|
29
|
+
* an orientation tag. May throw on decode/encode failure — callers fall back.
|
|
30
|
+
*/
|
|
31
|
+
export declare const normalizeImageOrientation: (bytes: Uint8Array) => Uint8Array;
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,8 @@ import { DEFAULT_FONT_NAME, ZOOM, b64toUint8Array, getDefaultFont, getFallbackFo
|
|
|
11
11
|
import { Buffer as Buffer$1 } from "buffer";
|
|
12
12
|
import { toRadians } from "@pdfme/pdf-lib";
|
|
13
13
|
import { Barcode, Calendar, CalendarClock, ChevronDown, Circle, CircleDot, Clock, Image, List, Minus, QrCode, Route, Square, SquareCheck, Table, Type } from "lucide";
|
|
14
|
+
import jpegJsModule from "jpeg-js";
|
|
15
|
+
import UPNGModule from "@pdf-lib/upng";
|
|
14
16
|
import DOMPurify from "dompurify";
|
|
15
17
|
import bwipjs from "bwip-js";
|
|
16
18
|
import AirDatepicker from "air-datepicker";
|
|
@@ -1406,88 +1408,208 @@ var listSchema = {
|
|
|
1406
1408
|
icon: createSvgStr(List)
|
|
1407
1409
|
};
|
|
1408
1410
|
//#endregion
|
|
1409
|
-
//#region src/graphics/
|
|
1410
|
-
var
|
|
1411
|
-
var
|
|
1412
|
-
var
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1411
|
+
//#region src/graphics/orientation.ts
|
|
1412
|
+
var isJpegJsApi = (value) => !!value && typeof value === "object" && typeof value.decode === "function" && typeof value.encode === "function";
|
|
1413
|
+
var isUPNGApi = (value) => !!value && typeof value === "object" && typeof value.decode === "function" && typeof value.toRGBA8 === "function" && typeof value.encode === "function";
|
|
1414
|
+
var resolveExport = (value, guard, label) => {
|
|
1415
|
+
let current = value;
|
|
1416
|
+
while (current && typeof current === "object") {
|
|
1417
|
+
if (guard(current)) return current;
|
|
1418
|
+
current = current.default;
|
|
1419
|
+
}
|
|
1420
|
+
throw new TypeError(`Failed to resolve ${label} exports`);
|
|
1421
|
+
};
|
|
1422
|
+
var jpegJs = resolveExport(jpegJsModule, isJpegJsApi, "jpeg-js");
|
|
1423
|
+
var UPNG = resolveExport(UPNGModule, isUPNGApi, "@pdf-lib/upng");
|
|
1424
|
+
var JPEG_ENCODE_QUALITY = 90;
|
|
1425
|
+
var JPEG_MAX_MEMORY_MB = 512;
|
|
1426
|
+
var JPEG_SOI_0 = 255;
|
|
1427
|
+
var JPEG_SOI_1 = 216;
|
|
1428
|
+
var PNG_SIGNATURE = new Uint8Array([
|
|
1429
|
+
137,
|
|
1430
|
+
80,
|
|
1431
|
+
78,
|
|
1432
|
+
71,
|
|
1433
|
+
13,
|
|
1434
|
+
10,
|
|
1435
|
+
26,
|
|
1436
|
+
10
|
|
1437
|
+
]);
|
|
1438
|
+
var toArrayBuffer = (bytes) => {
|
|
1439
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
1440
|
+
copy.set(bytes);
|
|
1441
|
+
return copy.buffer;
|
|
1442
|
+
};
|
|
1443
|
+
var isPngSignature = (bytes) => {
|
|
1444
|
+
if (bytes.length < PNG_SIGNATURE.length) return false;
|
|
1445
|
+
for (let i = 0; i < PNG_SIGNATURE.length; i++) if (bytes[i] !== PNG_SIGNATURE[i]) return false;
|
|
1446
|
+
return true;
|
|
1420
1447
|
};
|
|
1421
|
-
var
|
|
1422
|
-
if (
|
|
1423
|
-
if (
|
|
1448
|
+
var detectImageFormat = (bytes) => {
|
|
1449
|
+
if (bytes.length >= 2 && bytes[0] === JPEG_SOI_0 && bytes[1] === JPEG_SOI_1) return "jpeg";
|
|
1450
|
+
if (isPngSignature(bytes)) return "png";
|
|
1451
|
+
return "other";
|
|
1424
1452
|
};
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1453
|
+
/** TIFF/IFD0: returns 1..8 or undefined. Never throws. */
|
|
1454
|
+
var parseTiffOrientation = (tiff) => {
|
|
1455
|
+
try {
|
|
1456
|
+
if (tiff.length < 8) return void 0;
|
|
1457
|
+
const le = tiff[0] === 73 && tiff[1] === 73;
|
|
1458
|
+
if (!le && !(tiff[0] === 77 && tiff[1] === 77)) return void 0;
|
|
1459
|
+
const view = new DataView(tiff.buffer, tiff.byteOffset, tiff.byteLength);
|
|
1460
|
+
if (view.getUint16(2, le) !== 42) return void 0;
|
|
1461
|
+
const ifd = view.getUint32(4, le);
|
|
1462
|
+
if (ifd + 2 > tiff.length) return void 0;
|
|
1463
|
+
const n = view.getUint16(ifd, le);
|
|
1464
|
+
for (let i = 0; i < n; i++) {
|
|
1465
|
+
const entry = ifd + 2 + i * 12;
|
|
1466
|
+
if (entry + 12 > tiff.length) return void 0;
|
|
1467
|
+
if (view.getUint16(entry, le) !== 274) continue;
|
|
1468
|
+
if (view.getUint16(entry + 2, le) !== 3) return void 0;
|
|
1469
|
+
if (view.getUint32(entry + 4, le) !== 1) return void 0;
|
|
1470
|
+
const val = view.getUint16(entry + 8, le);
|
|
1471
|
+
return val >= 1 && val <= 8 ? val : void 0;
|
|
1436
1472
|
}
|
|
1437
|
-
|
|
1473
|
+
return;
|
|
1474
|
+
} catch {
|
|
1475
|
+
return;
|
|
1438
1476
|
}
|
|
1439
1477
|
};
|
|
1440
|
-
var
|
|
1441
|
-
|
|
1442
|
-
var
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1478
|
+
var isExifApp1Payload = (segment) => segment.length >= 6 && segment[0] === 69 && segment[1] === 120 && segment[2] === 105 && segment[3] === 102 && segment[4] === 0 && segment[5] === 0;
|
|
1479
|
+
/** Standalone JPEG markers have no length field (TEM, RST0–7, SOI). */
|
|
1480
|
+
var isStandaloneJpegMarker = (marker) => marker === 1 || marker >= 208 && marker <= 216;
|
|
1481
|
+
/**
|
|
1482
|
+
* JPEG: scan APP1 'Exif\0\0' segments; stop at SOS/EOI.
|
|
1483
|
+
* Skips 0xFF fill/padding and standalone markers. If an Exif APP1 has no
|
|
1484
|
+
* valid Orientation, keep scanning for a later one.
|
|
1485
|
+
*/
|
|
1486
|
+
var getJpegOrientation = (bytes) => {
|
|
1487
|
+
if (bytes.length < 2 || bytes[0] !== JPEG_SOI_0 || bytes[1] !== JPEG_SOI_1) return void 0;
|
|
1488
|
+
let pos = 2;
|
|
1489
|
+
while (pos < bytes.length) {
|
|
1490
|
+
if (bytes[pos] !== 255) return void 0;
|
|
1491
|
+
while (pos < bytes.length && bytes[pos] === 255) pos++;
|
|
1492
|
+
if (pos >= bytes.length) return void 0;
|
|
1493
|
+
const marker = bytes[pos++];
|
|
1494
|
+
if (isStandaloneJpegMarker(marker)) continue;
|
|
1495
|
+
if (marker === 218 || marker === 217) return void 0;
|
|
1496
|
+
if (pos + 2 > bytes.length) return void 0;
|
|
1497
|
+
const size = bytes[pos] << 8 | bytes[pos + 1];
|
|
1498
|
+
if (size < 2 || pos + size > bytes.length) return void 0;
|
|
1499
|
+
if (marker === 225 && size >= 8) {
|
|
1500
|
+
const segment = bytes.subarray(pos + 2, pos + size);
|
|
1501
|
+
if (isExifApp1Payload(segment)) {
|
|
1502
|
+
const orientation = parseTiffOrientation(segment.subarray(6));
|
|
1503
|
+
if (orientation !== void 0) return orientation;
|
|
1452
1504
|
}
|
|
1453
|
-
return false;
|
|
1454
|
-
},
|
|
1455
|
-
calculate(input) {
|
|
1456
|
-
if (toUTF8String(input, 12, 16) === pngFriedChunkName) return {
|
|
1457
|
-
height: readUInt32BE(input, 36),
|
|
1458
|
-
width: readUInt32BE(input, 32)
|
|
1459
|
-
};
|
|
1460
|
-
return {
|
|
1461
|
-
height: readUInt32BE(input, 20),
|
|
1462
|
-
width: readUInt32BE(input, 16)
|
|
1463
|
-
};
|
|
1464
1505
|
}
|
|
1506
|
+
pos += size;
|
|
1465
1507
|
}
|
|
1466
1508
|
};
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1509
|
+
/**
|
|
1510
|
+
* PNG: scan chunks for eXIf (raw TIFF payload). Skip IDAT and other
|
|
1511
|
+
* ancillary chunks; eXIf may appear after image data. Stop at IEND.
|
|
1512
|
+
*/
|
|
1513
|
+
var getPngOrientation = (bytes) => {
|
|
1514
|
+
if (!isPngSignature(bytes)) return void 0;
|
|
1515
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
1516
|
+
let pos = 8;
|
|
1517
|
+
while (pos + 12 <= bytes.length) {
|
|
1518
|
+
const length = view.getUint32(pos);
|
|
1519
|
+
if (pos + 12 + length > bytes.length) return void 0;
|
|
1520
|
+
const type = String.fromCharCode(bytes[pos + 4], bytes[pos + 5], bytes[pos + 6], bytes[pos + 7]);
|
|
1521
|
+
if (type === "eXIf") return parseTiffOrientation(bytes.subarray(pos + 8, pos + 8 + length));
|
|
1522
|
+
if (type === "IEND") return void 0;
|
|
1523
|
+
pos += 12 + length;
|
|
1476
1524
|
}
|
|
1477
|
-
return Object.keys(typeHandlers).find((key) => typeHandlers[key].validate(input));
|
|
1478
|
-
}
|
|
1479
|
-
var getImageDimension = (value) => {
|
|
1480
|
-
const idx = value.indexOf(";base64,");
|
|
1481
|
-
const imgBase64 = value.substring(idx + 8, value.length);
|
|
1482
|
-
return imageSize(Buffer$1.from(imgBase64, "base64"));
|
|
1483
1525
|
};
|
|
1484
|
-
var
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1526
|
+
var orientationSwapsDimensions = (orientation) => orientation >= 5;
|
|
1527
|
+
/**
|
|
1528
|
+
* Map source (x, y) into destination (dx, dy) for EXIF orientations 2–8.
|
|
1529
|
+
* Orientations 5–8 swap width and height.
|
|
1530
|
+
*/
|
|
1531
|
+
var transformRgba = (src, width, height, orientation) => {
|
|
1532
|
+
const swap = orientationSwapsDimensions(orientation);
|
|
1533
|
+
const destWidth = swap ? height : width;
|
|
1534
|
+
const destHeight = swap ? width : height;
|
|
1535
|
+
const dest = new Uint8Array(destWidth * destHeight * 4);
|
|
1536
|
+
for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
|
|
1537
|
+
let dx = x;
|
|
1538
|
+
let dy = y;
|
|
1539
|
+
switch (orientation) {
|
|
1540
|
+
case 2:
|
|
1541
|
+
dx = width - 1 - x;
|
|
1542
|
+
dy = y;
|
|
1543
|
+
break;
|
|
1544
|
+
case 3:
|
|
1545
|
+
dx = width - 1 - x;
|
|
1546
|
+
dy = height - 1 - y;
|
|
1547
|
+
break;
|
|
1548
|
+
case 4:
|
|
1549
|
+
dx = x;
|
|
1550
|
+
dy = height - 1 - y;
|
|
1551
|
+
break;
|
|
1552
|
+
case 5:
|
|
1553
|
+
dx = y;
|
|
1554
|
+
dy = x;
|
|
1555
|
+
break;
|
|
1556
|
+
case 6:
|
|
1557
|
+
dx = height - 1 - y;
|
|
1558
|
+
dy = x;
|
|
1559
|
+
break;
|
|
1560
|
+
case 7:
|
|
1561
|
+
dx = height - 1 - y;
|
|
1562
|
+
dy = width - 1 - x;
|
|
1563
|
+
break;
|
|
1564
|
+
case 8:
|
|
1565
|
+
dx = y;
|
|
1566
|
+
dy = width - 1 - x;
|
|
1567
|
+
}
|
|
1568
|
+
const srcIndex = (y * width + x) * 4;
|
|
1569
|
+
const destIndex = (dy * destWidth + dx) * 4;
|
|
1570
|
+
dest[destIndex] = src[srcIndex];
|
|
1571
|
+
dest[destIndex + 1] = src[srcIndex + 1];
|
|
1572
|
+
dest[destIndex + 2] = src[srcIndex + 2];
|
|
1573
|
+
dest[destIndex + 3] = src[srcIndex + 3];
|
|
1489
1574
|
}
|
|
1490
|
-
|
|
1575
|
+
return {
|
|
1576
|
+
data: dest,
|
|
1577
|
+
width: destWidth,
|
|
1578
|
+
height: destHeight
|
|
1579
|
+
};
|
|
1580
|
+
};
|
|
1581
|
+
var bakeJpegOrientation = (bytes) => {
|
|
1582
|
+
const orientation = getJpegOrientation(bytes);
|
|
1583
|
+
if (!orientation || orientation === 1) return bytes;
|
|
1584
|
+
const raw = jpegJs.decode(bytes, {
|
|
1585
|
+
useTArray: true,
|
|
1586
|
+
formatAsRGBA: true,
|
|
1587
|
+
maxMemoryUsageInMB: JPEG_MAX_MEMORY_MB
|
|
1588
|
+
});
|
|
1589
|
+
const transformed = transformRgba(raw.data, raw.width, raw.height, orientation);
|
|
1590
|
+
return new Uint8Array(jpegJs.encode({
|
|
1591
|
+
data: transformed.data,
|
|
1592
|
+
width: transformed.width,
|
|
1593
|
+
height: transformed.height
|
|
1594
|
+
}, JPEG_ENCODE_QUALITY).data);
|
|
1595
|
+
};
|
|
1596
|
+
var bakePngOrientation = (bytes) => {
|
|
1597
|
+
const orientation = getPngOrientation(bytes);
|
|
1598
|
+
if (!orientation || orientation === 1) return bytes;
|
|
1599
|
+
const img = UPNG.decode(toArrayBuffer(bytes));
|
|
1600
|
+
const transformed = transformRgba(new Uint8Array(UPNG.toRGBA8(img)[0]), img.width, img.height, orientation);
|
|
1601
|
+
return new Uint8Array(UPNG.encode([toArrayBuffer(transformed.data)], transformed.width, transformed.height, 0));
|
|
1602
|
+
};
|
|
1603
|
+
/**
|
|
1604
|
+
* One normalize path for JPEG and PNG. Tag absent/1 is a byte-identical
|
|
1605
|
+
* passthrough. Tag 2–8 decodes, transforms pixels, and re-encodes without
|
|
1606
|
+
* an orientation tag. May throw on decode/encode failure — callers fall back.
|
|
1607
|
+
*/
|
|
1608
|
+
var normalizeImageOrientation = (bytes) => {
|
|
1609
|
+
const kind = detectImageFormat(bytes);
|
|
1610
|
+
if (kind === "jpeg") return bakeJpegOrientation(bytes);
|
|
1611
|
+
if (kind === "png") return bakePngOrientation(bytes);
|
|
1612
|
+
return bytes;
|
|
1491
1613
|
};
|
|
1492
1614
|
//#endregion
|
|
1493
1615
|
//#region src/graphics/image.ts
|
|
@@ -1515,6 +1637,11 @@ var getCacheKey = (schema, input) => {
|
|
|
1515
1637
|
const hex = (hash >>> 0).toString(16).padStart(8, "0");
|
|
1516
1638
|
return `${schema.type}:${input.length}:${hex}`;
|
|
1517
1639
|
};
|
|
1640
|
+
var dataUrlToBytes = (value) => {
|
|
1641
|
+
const idx = value.indexOf(";base64,");
|
|
1642
|
+
const base64 = idx >= 0 ? value.slice(idx + 8) : value;
|
|
1643
|
+
return new Uint8Array(Buffer$1.from(base64, "base64"));
|
|
1644
|
+
};
|
|
1518
1645
|
var fullSize$1 = {
|
|
1519
1646
|
width: "100%",
|
|
1520
1647
|
height: "100%"
|
|
@@ -1527,16 +1654,24 @@ var imageSchema = {
|
|
|
1527
1654
|
const inputImageCacheKey = getCacheKey(schema, value);
|
|
1528
1655
|
let image = _cache.get(inputImageCacheKey);
|
|
1529
1656
|
if (!image) {
|
|
1530
|
-
|
|
1657
|
+
const originalBytes = dataUrlToBytes(value);
|
|
1658
|
+
const kind = detectImageFormat(originalBytes);
|
|
1659
|
+
let bytes = originalBytes;
|
|
1660
|
+
try {
|
|
1661
|
+
if (kind === "jpeg" || kind === "png") bytes = normalizeImageOrientation(originalBytes);
|
|
1662
|
+
} catch (error) {
|
|
1663
|
+
console.warn("[@pdfme/schemas] EXIF orientation bake failed; embedding original bytes:", error);
|
|
1664
|
+
bytes = originalBytes;
|
|
1665
|
+
}
|
|
1666
|
+
image = await (kind === "png" ? pdfDoc.embedPng(bytes) : pdfDoc.embedJpg(bytes));
|
|
1531
1667
|
_cache.set(inputImageCacheKey, image);
|
|
1532
1668
|
}
|
|
1533
1669
|
const _schema = {
|
|
1534
1670
|
...schema,
|
|
1535
1671
|
position: { ...schema.position }
|
|
1536
1672
|
};
|
|
1537
|
-
const
|
|
1538
|
-
const
|
|
1539
|
-
const imageHeight = px2mm(dimension.height);
|
|
1673
|
+
const imageWidth = px2mm(image.width);
|
|
1674
|
+
const imageHeight = px2mm(image.height);
|
|
1540
1675
|
const boxWidth = _schema.width;
|
|
1541
1676
|
const boxHeight = _schema.height;
|
|
1542
1677
|
const imageRatio = imageWidth / imageHeight;
|