pdf-catalog-generator 3.1.3 → 3.1.4
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/README.md +11 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +139 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +140 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -175,6 +175,7 @@ Generates a PDF catalog from the provided configuration.
|
|
|
175
175
|
- `companyName` (string): Your company name
|
|
176
176
|
- `companyLogo` (string | null, optional): Logo URL or base64 string
|
|
177
177
|
- `template` (TemplateType, optional): Template to use (default: 'template1')
|
|
178
|
+
- `imageOptimization` (ImageOptimizationOptions, optional): Resize/compress supported remote image URLs before rendering. Enabled by default.
|
|
178
179
|
|
|
179
180
|
**Returns:** `Promise<Uint8Array>` - PDF file as Uint8Array (works in both Node.js and browser)
|
|
180
181
|
|
|
@@ -288,6 +289,16 @@ This library is **completely schema-free** - no fields are mandatory! You can us
|
|
|
288
289
|
interface ProductData {
|
|
289
290
|
[key: string]: string | number | boolean | null | undefined;
|
|
290
291
|
}
|
|
292
|
+
|
|
293
|
+
interface ImageOptimizationOptions {
|
|
294
|
+
enabled?: boolean;
|
|
295
|
+
maxWidth?: number;
|
|
296
|
+
maxHeight?: number;
|
|
297
|
+
quality?: number;
|
|
298
|
+
logoMaxWidth?: number;
|
|
299
|
+
logoMaxHeight?: number;
|
|
300
|
+
logoQuality?: number;
|
|
301
|
+
}
|
|
291
302
|
```
|
|
292
303
|
|
|
293
304
|
**Dynamic Field Rendering:**
|
package/dist/index.d.mts
CHANGED
|
@@ -5,11 +5,21 @@ interface ProductData {
|
|
|
5
5
|
[key: string]: string | number | boolean | null | undefined;
|
|
6
6
|
}
|
|
7
7
|
type TemplateType = 'template1' | 'template2' | 'template3' | 'template4' | 'template5' | 'template6';
|
|
8
|
+
interface ImageOptimizationOptions {
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
maxWidth?: number;
|
|
11
|
+
maxHeight?: number;
|
|
12
|
+
quality?: number;
|
|
13
|
+
logoMaxWidth?: number;
|
|
14
|
+
logoMaxHeight?: number;
|
|
15
|
+
logoQuality?: number;
|
|
16
|
+
}
|
|
8
17
|
interface CatalogConfig {
|
|
9
18
|
products: ProductData[];
|
|
10
19
|
companyLogo?: string | null;
|
|
11
20
|
companyName: string;
|
|
12
21
|
template?: TemplateType;
|
|
22
|
+
imageOptimization?: ImageOptimizationOptions;
|
|
13
23
|
}
|
|
14
24
|
interface TemplateProps {
|
|
15
25
|
companyLogo?: string | null;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,11 +5,21 @@ interface ProductData {
|
|
|
5
5
|
[key: string]: string | number | boolean | null | undefined;
|
|
6
6
|
}
|
|
7
7
|
type TemplateType = 'template1' | 'template2' | 'template3' | 'template4' | 'template5' | 'template6';
|
|
8
|
+
interface ImageOptimizationOptions {
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
maxWidth?: number;
|
|
11
|
+
maxHeight?: number;
|
|
12
|
+
quality?: number;
|
|
13
|
+
logoMaxWidth?: number;
|
|
14
|
+
logoMaxHeight?: number;
|
|
15
|
+
logoQuality?: number;
|
|
16
|
+
}
|
|
8
17
|
interface CatalogConfig {
|
|
9
18
|
products: ProductData[];
|
|
10
19
|
companyLogo?: string | null;
|
|
11
20
|
companyName: string;
|
|
12
21
|
template?: TemplateType;
|
|
22
|
+
imageOptimization?: ImageOptimizationOptions;
|
|
13
23
|
}
|
|
14
24
|
interface TemplateProps {
|
|
15
25
|
companyLogo?: string | null;
|
package/dist/index.js
CHANGED
|
@@ -1005,8 +1005,137 @@ renderer.Font.registerEmojiSource({
|
|
|
1005
1005
|
format: "png",
|
|
1006
1006
|
url: "https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/"
|
|
1007
1007
|
});
|
|
1008
|
+
function normalizeImageSource(imageSource) {
|
|
1009
|
+
const trimmedImageSource = imageSource.trim();
|
|
1010
|
+
if (trimmedImageSource.startsWith("data:")) {
|
|
1011
|
+
return trimmedImageSource;
|
|
1012
|
+
}
|
|
1013
|
+
if (trimmedImageSource.startsWith("http://") || trimmedImageSource.startsWith("https://")) {
|
|
1014
|
+
return trimmedImageSource;
|
|
1015
|
+
}
|
|
1016
|
+
let mimeType = "image/png";
|
|
1017
|
+
const base64Start = trimmedImageSource.substring(0, 20).toLowerCase();
|
|
1018
|
+
if (base64Start.includes("ivborw0kggo") || trimmedImageSource.startsWith("iVBORw0KGgo")) {
|
|
1019
|
+
mimeType = "image/png";
|
|
1020
|
+
} else if (base64Start.includes("/9j/4aaq") || trimmedImageSource.startsWith("/9j/4AAQ")) {
|
|
1021
|
+
mimeType = "image/jpeg";
|
|
1022
|
+
} else if (base64Start.includes("r0lgodlh") || trimmedImageSource.startsWith("R0lGODlh")) {
|
|
1023
|
+
mimeType = "image/gif";
|
|
1024
|
+
} else if (base64Start.includes("uklgr") || trimmedImageSource.startsWith("UklGR")) {
|
|
1025
|
+
mimeType = "image/webp";
|
|
1026
|
+
}
|
|
1027
|
+
return `data:${mimeType};base64,${trimmedImageSource}`;
|
|
1028
|
+
}
|
|
1029
|
+
var IMAGE_FIELD_NAMES_LOWER = /* @__PURE__ */ new Set([
|
|
1030
|
+
"image",
|
|
1031
|
+
"imageurl",
|
|
1032
|
+
"img",
|
|
1033
|
+
"photo",
|
|
1034
|
+
"picture",
|
|
1035
|
+
"image2",
|
|
1036
|
+
"imageurl2",
|
|
1037
|
+
"img2",
|
|
1038
|
+
"photo2",
|
|
1039
|
+
"picture2"
|
|
1040
|
+
]);
|
|
1041
|
+
var DEFAULT_IMAGE_OPTIMIZATION = {
|
|
1042
|
+
enabled: true,
|
|
1043
|
+
maxWidth: 900,
|
|
1044
|
+
maxHeight: 900,
|
|
1045
|
+
quality: 70,
|
|
1046
|
+
logoMaxWidth: 400,
|
|
1047
|
+
logoMaxHeight: 400,
|
|
1048
|
+
logoQuality: 70
|
|
1049
|
+
};
|
|
1050
|
+
function getImageOptimizationOptions(options) {
|
|
1051
|
+
return {
|
|
1052
|
+
...DEFAULT_IMAGE_OPTIMIZATION,
|
|
1053
|
+
...options
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
function getNumericSearchParam(url, key) {
|
|
1057
|
+
const rawValue = url.searchParams.get(key);
|
|
1058
|
+
if (rawValue === null || rawValue.trim() === "") {
|
|
1059
|
+
return null;
|
|
1060
|
+
}
|
|
1061
|
+
const numericValue = Number(rawValue);
|
|
1062
|
+
return Number.isFinite(numericValue) ? numericValue : null;
|
|
1063
|
+
}
|
|
1064
|
+
function optimizeImageUrl(imageSource, options, imageKind) {
|
|
1065
|
+
if (!options.enabled) {
|
|
1066
|
+
return normalizeImageSource(imageSource);
|
|
1067
|
+
}
|
|
1068
|
+
const normalizedSource = normalizeImageSource(imageSource);
|
|
1069
|
+
if (!normalizedSource.startsWith("http://") && !normalizedSource.startsWith("https://")) {
|
|
1070
|
+
return normalizedSource;
|
|
1071
|
+
}
|
|
1072
|
+
let url;
|
|
1073
|
+
try {
|
|
1074
|
+
url = new URL(normalizedSource);
|
|
1075
|
+
} catch {
|
|
1076
|
+
return normalizedSource;
|
|
1077
|
+
}
|
|
1078
|
+
const hostname = url.hostname.toLowerCase();
|
|
1079
|
+
const isImgixStyleHost = hostname.includes("unsplash.com") || hostname.includes("imgix.net") || hostname.includes("builder.io") || hostname.includes("sanity.io");
|
|
1080
|
+
if (!isImgixStyleHost) {
|
|
1081
|
+
return normalizedSource;
|
|
1082
|
+
}
|
|
1083
|
+
const maxWidth = imageKind === "logo" ? options.logoMaxWidth : options.maxWidth;
|
|
1084
|
+
const maxHeight = imageKind === "logo" ? options.logoMaxHeight : options.maxHeight;
|
|
1085
|
+
const quality = imageKind === "logo" ? options.logoQuality : options.quality;
|
|
1086
|
+
url.searchParams.set("auto", "format,compress");
|
|
1087
|
+
url.searchParams.set("fit", "max");
|
|
1088
|
+
const currentWidth = getNumericSearchParam(url, "w");
|
|
1089
|
+
if (currentWidth === null || currentWidth > maxWidth) {
|
|
1090
|
+
url.searchParams.set("w", String(maxWidth));
|
|
1091
|
+
}
|
|
1092
|
+
const currentHeight = getNumericSearchParam(url, "h");
|
|
1093
|
+
if (currentHeight === null || currentHeight > maxHeight) {
|
|
1094
|
+
url.searchParams.set("h", String(maxHeight));
|
|
1095
|
+
}
|
|
1096
|
+
const currentQuality = getNumericSearchParam(url, "q");
|
|
1097
|
+
if (currentQuality === null || currentQuality > quality) {
|
|
1098
|
+
url.searchParams.set("q", String(quality));
|
|
1099
|
+
}
|
|
1100
|
+
return url.toString();
|
|
1101
|
+
}
|
|
1102
|
+
function normalizeProductImages(products, options) {
|
|
1103
|
+
const imageCache = /* @__PURE__ */ new Map();
|
|
1104
|
+
const normalizeCachedImage = (value, imageKind) => {
|
|
1105
|
+
const cacheKey = `${imageKind}:${value}`;
|
|
1106
|
+
const cachedValue = imageCache.get(cacheKey);
|
|
1107
|
+
if (cachedValue) {
|
|
1108
|
+
return cachedValue;
|
|
1109
|
+
}
|
|
1110
|
+
const normalizedValue = optimizeImageUrl(value, options, imageKind);
|
|
1111
|
+
imageCache.set(cacheKey, normalizedValue);
|
|
1112
|
+
return normalizedValue;
|
|
1113
|
+
};
|
|
1114
|
+
return products.map((product) => {
|
|
1115
|
+
const normalizedProduct = { ...product };
|
|
1116
|
+
for (const [key, value] of Object.entries(product)) {
|
|
1117
|
+
if (typeof value === "string" && value.trim() !== "" && IMAGE_FIELD_NAMES_LOWER.has(key.toLowerCase())) {
|
|
1118
|
+
normalizedProduct[key] = normalizeCachedImage(value, "product");
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return normalizedProduct;
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
async function renderPdfToUint8Array(doc) {
|
|
1125
|
+
const isNodeRuntime = typeof process !== "undefined" && typeof process.versions?.node === "string";
|
|
1126
|
+
if (isNodeRuntime && typeof renderer.renderToBuffer === "function") {
|
|
1127
|
+
const buffer = await renderer.renderToBuffer(doc);
|
|
1128
|
+
return buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
1129
|
+
}
|
|
1130
|
+
const blob = await renderer.pdf(doc).toBlob();
|
|
1131
|
+
const arrayBuffer = await blob.arrayBuffer();
|
|
1132
|
+
return new Uint8Array(arrayBuffer);
|
|
1133
|
+
}
|
|
1008
1134
|
async function generateProductCatalog(config) {
|
|
1009
|
-
const { products, companyLogo, companyName, template = "template1" } = config;
|
|
1135
|
+
const { products, companyLogo, companyName, template = "template1", imageOptimization } = config;
|
|
1136
|
+
const optimizationOptions = getImageOptimizationOptions(imageOptimization);
|
|
1137
|
+
const normalizedProducts = normalizeProductImages(products, optimizationOptions);
|
|
1138
|
+
const normalizedCompanyLogo = companyLogo ? optimizeImageUrl(companyLogo, optimizationOptions, "logo") : companyLogo;
|
|
1010
1139
|
let TemplateComponent;
|
|
1011
1140
|
switch (template) {
|
|
1012
1141
|
case "template1":
|
|
@@ -1034,39 +1163,33 @@ async function generateProductCatalog(config) {
|
|
|
1034
1163
|
const doc2 = /* @__PURE__ */ jsxRuntime.jsx(renderer.Document, { children: /* @__PURE__ */ jsxRuntime.jsx(renderer.Page, { size: "A4", style: styles7.page, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1035
1164
|
Template4_default,
|
|
1036
1165
|
{
|
|
1037
|
-
products,
|
|
1038
|
-
companyLogo,
|
|
1166
|
+
products: normalizedProducts,
|
|
1167
|
+
companyLogo: normalizedCompanyLogo,
|
|
1039
1168
|
companyName
|
|
1040
1169
|
}
|
|
1041
1170
|
) }) });
|
|
1042
|
-
|
|
1043
|
-
const arrayBuffer2 = await blob2.arrayBuffer();
|
|
1044
|
-
return new Uint8Array(arrayBuffer2);
|
|
1171
|
+
return renderPdfToUint8Array(doc2);
|
|
1045
1172
|
}
|
|
1046
1173
|
if (template === "template5" || template === "template6") {
|
|
1047
1174
|
const Template = template === "template5" ? Template5_default : Template6_default;
|
|
1048
1175
|
const doc2 = /* @__PURE__ */ jsxRuntime.jsx(renderer.Document, { children: /* @__PURE__ */ jsxRuntime.jsx(renderer.Page, { size: "A4", orientation: "landscape", style: styles7.page, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1049
1176
|
Template,
|
|
1050
1177
|
{
|
|
1051
|
-
products,
|
|
1052
|
-
companyLogo,
|
|
1178
|
+
products: normalizedProducts,
|
|
1179
|
+
companyLogo: normalizedCompanyLogo,
|
|
1053
1180
|
companyName
|
|
1054
1181
|
}
|
|
1055
1182
|
) }) });
|
|
1056
|
-
|
|
1057
|
-
const arrayBuffer2 = await blob2.arrayBuffer();
|
|
1058
|
-
return new Uint8Array(arrayBuffer2);
|
|
1183
|
+
return renderPdfToUint8Array(doc2);
|
|
1059
1184
|
}
|
|
1060
1185
|
const doc = /* @__PURE__ */ jsxRuntime.jsx(renderer.Document, { children: /* @__PURE__ */ jsxRuntime.jsxs(renderer.Page, { size: "A4", style: styles7.page, children: [
|
|
1061
1186
|
/* @__PURE__ */ jsxRuntime.jsxs(renderer.View, { style: styles7.header, children: [
|
|
1062
|
-
|
|
1187
|
+
normalizedCompanyLogo && /* @__PURE__ */ jsxRuntime.jsx(renderer.View, { style: styles7.logoSection, children: /* @__PURE__ */ jsxRuntime.jsx(renderer.Image, { style: styles7.logo, src: normalizedCompanyLogo }) }),
|
|
1063
1188
|
/* @__PURE__ */ jsxRuntime.jsx(renderer.View, { style: styles7.storeNameSection, children: /* @__PURE__ */ jsxRuntime.jsx(renderer.Text, { style: styles7.storeName, children: companyName }) })
|
|
1064
1189
|
] }),
|
|
1065
|
-
/* @__PURE__ */ jsxRuntime.jsx(TemplateComponent, { products })
|
|
1190
|
+
/* @__PURE__ */ jsxRuntime.jsx(TemplateComponent, { products: normalizedProducts })
|
|
1066
1191
|
] }) });
|
|
1067
|
-
|
|
1068
|
-
const arrayBuffer = await blob.arrayBuffer();
|
|
1069
|
-
return new Uint8Array(arrayBuffer);
|
|
1192
|
+
return renderPdfToUint8Array(doc);
|
|
1070
1193
|
}
|
|
1071
1194
|
function parseExcelFile(buffer) {
|
|
1072
1195
|
const workbook = XLSX__namespace.read(buffer, {
|