@pdfme/schemas 6.2.4-dev.1 → 6.2.4-dev.3

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.
@@ -2,3 +2,4 @@ export declare const BARCODE_TYPES: readonly ['qrcode', 'japanpost', 'ean13', 'e
2
2
  export declare const DEFAULT_BARCODE_BG_COLOR = "#ffffff";
3
3
  export declare const DEFAULT_BARCODE_COLOR = "#000000";
4
4
  export declare const DEFAULT_BARCODE_INCLUDETEXT = true;
5
+ export declare const ASPECT_RATIO_LOCKED_BARCODE_TYPES: readonly ['qrcode', 'gs1datamatrix', 'pdf417'];
@@ -1,5 +1,5 @@
1
1
  import { Buffer } from 'buffer';
2
- import { BarcodeTypes } from './types.js';
2
+ import { BarcodeSchema, BarcodeTypes } from './types.js';
3
3
  export declare const validateBarcodeInput: (type: BarcodeTypes, input: string) => boolean;
4
4
  /**
5
5
  * The bwip.js lib has a different name for nw7 type barcodes
@@ -25,16 +25,47 @@ export type BarcodeRenderRuntime = 'document-canvas' | 'offscreencanvas' | 'node
25
25
  * OffscreenCanvas without `toCanvas()` (Node bwip-js export) falls back to `toBuffer()`.
26
26
  */
27
27
  export declare const resolveBarcodeRenderRuntime: () => BarcodeRenderRuntime;
28
+ export declare const isAspectRatioLockedBarcodeType: (type: string) => boolean;
29
+ export declare const isSquareBarcodeType: (type: string) => boolean;
30
+ /** Root `viewBox` width/height, then `width`/`height` attributes. `undefined` when neither parses. */
31
+ export declare const getSvgViewBoxSize: (svg: string) => {
32
+ width: number;
33
+ height: number;
34
+ } | undefined;
35
+ /** Centered contain rect for a natural size; the full box when the natural size is unknown. */
36
+ export declare const getBarcodeFitLayout: ({ boxWidth, boxHeight, naturalWidth, naturalHeight, }: {
37
+ boxWidth: number;
38
+ boxHeight: number;
39
+ naturalWidth?: number;
40
+ naturalHeight?: number;
41
+ }) => {
42
+ width: number;
43
+ height: number;
44
+ offsetX: number;
45
+ offsetY: number;
46
+ };
28
47
  type CreateBarCodeArg = {
29
48
  type: BarcodeTypes;
30
49
  input: string;
31
- width: number;
32
- height: number;
50
+ /** Per-axis size targets in mm. Omit both to render the natural symbol. */
51
+ width?: number;
52
+ height?: number;
33
53
  backgroundColor?: string;
34
54
  barColor?: string;
35
55
  textColor?: string;
36
56
  includetext?: boolean;
37
57
  };
38
58
  export declare const createBarCodeSvg: (arg: CreateBarCodeArg) => string;
59
+ /** Natural (unscaled) symbol size from bwip-js, in SVG user units. */
60
+ export declare const getNaturalBarcodeSize: (schema: BarcodeSchema, input: string) => {
61
+ width: number;
62
+ height: number;
63
+ } | undefined;
64
+ /**
65
+ * Natural width / height of a locked symbology; undefined for 1D types. Square types
66
+ * return 1 even for empty content. The Designer calls this on every width/height/content
67
+ * commit, so content-dependent ratios are memoized.
68
+ */
69
+ export declare const getBarcodeAspectRatio: (schema: BarcodeSchema) => number | undefined;
39
70
  export declare const createBarCode: (arg: CreateBarCodeArg) => Promise<Buffer>;
40
71
  export {};
package/dist/index.js CHANGED
@@ -2194,6 +2194,11 @@ var BARCODE_TYPES = [
2194
2194
  ];
2195
2195
  var DEFAULT_BARCODE_BG_COLOR = "#ffffff";
2196
2196
  var DEFAULT_BARCODE_COLOR = "#000000";
2197
+ var ASPECT_RATIO_LOCKED_BARCODE_TYPES = [
2198
+ "qrcode",
2199
+ "gs1datamatrix",
2200
+ "pdf417"
2201
+ ];
2197
2202
  //#endregion
2198
2203
  //#region src/barcodes/helper.ts
2199
2204
  var validateCheckDigit = (input, checkDigitPos) => {
@@ -2296,17 +2301,67 @@ var renderBarcodeToNodeBuffer = async (options) => {
2296
2301
  if (typeof toBuffer !== "function") throw new Error("[@pdfme/schemas] bwip-js toBuffer() is not available in this environment.");
2297
2302
  return toBuffer(options);
2298
2303
  };
2304
+ var aspectRatioLockedTypes = ASPECT_RATIO_LOCKED_BARCODE_TYPES;
2305
+ var isAspectRatioLockedBarcodeType = (type) => aspectRatioLockedTypes.includes(type);
2306
+ var SQUARE_BARCODE_TYPES = ["qrcode", "gs1datamatrix"];
2307
+ var isSquareBarcodeType = (type) => SQUARE_BARCODE_TYPES.includes(type);
2308
+ var SVG_VIEW_BOX_PATTERN = /viewBox="\s*(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s*"/;
2309
+ var positiveSize = (value) => {
2310
+ const size = Number(value);
2311
+ return Number.isFinite(size) && size > 0 ? size : void 0;
2312
+ };
2313
+ /** Root `viewBox` width/height, then `width`/`height` attributes. `undefined` when neither parses. */
2314
+ var getSvgViewBoxSize = (svg) => {
2315
+ const viewBox = svg.match(SVG_VIEW_BOX_PATTERN);
2316
+ if (viewBox) {
2317
+ const width = positiveSize(viewBox[3]);
2318
+ const height = positiveSize(viewBox[4]);
2319
+ if (width !== void 0 && height !== void 0) return {
2320
+ width,
2321
+ height
2322
+ };
2323
+ }
2324
+ const openTag = svg.match(/<svg\b[^>]*>/)?.[0];
2325
+ if (!openTag) return void 0;
2326
+ const width = positiveSize(openTag.match(/\bwidth="([^"]+)"/)?.[1]);
2327
+ const height = positiveSize(openTag.match(/\bheight="([^"]+)"/)?.[1]);
2328
+ if (width === void 0 || height === void 0) return void 0;
2329
+ return {
2330
+ width,
2331
+ height
2332
+ };
2333
+ };
2334
+ /** Centered contain rect for a natural size; the full box when the natural size is unknown. */
2335
+ var getBarcodeFitLayout = ({ boxWidth, boxHeight, naturalWidth, naturalHeight }) => {
2336
+ const stretch = {
2337
+ width: boxWidth,
2338
+ height: boxHeight,
2339
+ offsetX: 0,
2340
+ offsetY: 0
2341
+ };
2342
+ if (naturalWidth === void 0 || naturalHeight === void 0 || naturalWidth <= 0 || naturalHeight <= 0) return stretch;
2343
+ const ratio = naturalWidth / naturalHeight;
2344
+ const widthLimited = boxWidth / boxHeight <= ratio;
2345
+ const width = widthLimited ? boxWidth : boxHeight * ratio;
2346
+ const height = widthLimited ? boxWidth / ratio : boxHeight;
2347
+ return {
2348
+ width,
2349
+ height,
2350
+ offsetX: (boxWidth - width) / 2,
2351
+ offsetY: (boxHeight - height) / 2
2352
+ };
2353
+ };
2299
2354
  var createBwipJsRenderOptions = (arg) => {
2300
2355
  const { type, input, width, height, backgroundColor, barColor, textColor, includetext = true } = arg;
2301
2356
  const bwipjsArg = {
2302
2357
  bcid: barCodeType2Bcid(type),
2303
2358
  text: input,
2304
- width,
2305
- height,
2306
2359
  scale: 5,
2307
2360
  includetext,
2308
2361
  textxalign: "center"
2309
2362
  };
2363
+ if (width !== void 0) bwipjsArg.width = width;
2364
+ if (height !== void 0) bwipjsArg.height = height;
2310
2365
  if (backgroundColor) bwipjsArg.backgroundcolor = mapHexColorForBwipJsLib(backgroundColor);
2311
2366
  if (barColor) bwipjsArg.barcolor = mapHexColorForBwipJsLib(barColor);
2312
2367
  if (textColor) bwipjsArg.textcolor = mapHexColorForBwipJsLib(textColor);
@@ -2320,6 +2375,39 @@ var createBarCodeSvg = (arg) => {
2320
2375
  if (/<image\b/i.test(svg)) throw new Error(`[@pdfme/schemas] bwip-js emitted an embedded image for ${arg.type}; vector barcode PDF rendering requires path-based SVG.`);
2321
2376
  return svg;
2322
2377
  };
2378
+ /** Natural (unscaled) symbol size from bwip-js, in SVG user units. */
2379
+ var getNaturalBarcodeSize = (schema, input) => getSvgViewBoxSize(createBarCodeSvg({
2380
+ ...schema,
2381
+ width: void 0,
2382
+ height: void 0,
2383
+ backgroundColor: void 0,
2384
+ input
2385
+ }));
2386
+ var contentAspectRatioMemo = /* @__PURE__ */ new Map();
2387
+ var CONTENT_ASPECT_RATIO_MEMO_LIMIT = 200;
2388
+ /**
2389
+ * Natural width / height of a locked symbology; undefined for 1D types. Square types
2390
+ * return 1 even for empty content. The Designer calls this on every width/height/content
2391
+ * commit, so content-dependent ratios are memoized.
2392
+ */
2393
+ var getBarcodeAspectRatio = (schema) => {
2394
+ if (!isAspectRatioLockedBarcodeType(schema.type)) return void 0;
2395
+ if (isSquareBarcodeType(schema.type)) return 1;
2396
+ const input = schema.content ?? "";
2397
+ if (!validateBarcodeInput(schema.type, input)) return void 0;
2398
+ const key = `${schema.type}:${input}`;
2399
+ if (contentAspectRatioMemo.has(key)) return contentAspectRatioMemo.get(key);
2400
+ let ratio;
2401
+ try {
2402
+ const size = getNaturalBarcodeSize(schema, input);
2403
+ ratio = size ? size.width / size.height : void 0;
2404
+ } catch {
2405
+ ratio = void 0;
2406
+ }
2407
+ if (contentAspectRatioMemo.size >= CONTENT_ASPECT_RATIO_MEMO_LIMIT) contentAspectRatioMemo.clear();
2408
+ contentAspectRatioMemo.set(key, ratio);
2409
+ return ratio;
2410
+ };
2323
2411
  var createBarCode = async (arg) => {
2324
2412
  const bwipjsArg = createBwipJsRenderOptions(arg);
2325
2413
  const runtime = resolveBarcodeRenderRuntime();
@@ -2329,8 +2417,20 @@ var createBarCode = async (arg) => {
2329
2417
  };
2330
2418
  //#endregion
2331
2419
  //#region src/barcodes/pdfRender.ts
2332
- var getBarcodeCacheKey = (schema, value) => {
2333
- return `svg:${schema.type}:${schema.width}:${schema.height}:${schema.barColor}:${schema.textColor}:${schema.includetext}:${value}`;
2420
+ var getBarcodeCacheKey = (schema, size, value) => {
2421
+ return `svg:${schema.type}:${size.width}:${size.height}:${schema.barColor}:${schema.textColor}:${schema.includetext}:${value}`;
2422
+ };
2423
+ var getNaturalSize = (schema, value, cache) => {
2424
+ if (!isAspectRatioLockedBarcodeType(schema.type)) return void 0;
2425
+ if (isSquareBarcodeType(schema.type)) return {
2426
+ width: 1,
2427
+ height: 1
2428
+ };
2429
+ const key = `svg-natural:${schema.type}:${value}`;
2430
+ if (cache.has(key)) return cache.get(key);
2431
+ const size = getNaturalBarcodeSize(schema, value);
2432
+ cache.set(key, size);
2433
+ return size;
2334
2434
  };
2335
2435
  var addSvgOpacity = (svg, opacity) => {
2336
2436
  return opacity === void 0 ? svg : svg.replace(/<svg\b/, `<svg opacity="${opacity}"`);
@@ -2344,11 +2444,20 @@ var getSvgColorMapper = (colorType = "") => {
2344
2444
  var pdfRender$1 = async (arg) => {
2345
2445
  const { value, schema, page, options, _cache } = arg;
2346
2446
  if (!validateBarcodeInput(schema.type, value)) return;
2347
- const inputBarcodeCacheKey = getBarcodeCacheKey(schema, value);
2447
+ const natural = getNaturalSize(schema, value, _cache);
2448
+ const fitted = getBarcodeFitLayout({
2449
+ boxWidth: schema.width,
2450
+ boxHeight: schema.height,
2451
+ naturalWidth: natural?.width,
2452
+ naturalHeight: natural?.height
2453
+ });
2454
+ const inputBarcodeCacheKey = getBarcodeCacheKey(schema, fitted, value);
2348
2455
  let svg = _cache.get(inputBarcodeCacheKey);
2349
2456
  if (!svg) {
2350
2457
  svg = createBarCodeSvg({
2351
2458
  ...schema,
2459
+ width: fitted.width,
2460
+ height: fitted.height,
2352
2461
  backgroundColor: void 0,
2353
2462
  type: schema.type,
2354
2463
  input: value
@@ -2382,11 +2491,17 @@ var pdfRender$1 = async (arg) => {
2382
2491
  opacity: applyAlphaToOpacity(opacity, alpha)
2383
2492
  });
2384
2493
  }
2494
+ const layout = getBarcodeFitLayout({
2495
+ boxWidth: width,
2496
+ boxHeight: height,
2497
+ naturalWidth: natural?.width,
2498
+ naturalHeight: natural?.height
2499
+ });
2385
2500
  await page.drawSvg(addSvgOpacity(svg, opacity), {
2386
- x,
2387
- y: y + height,
2388
- width,
2389
- height,
2501
+ x: x + layout.offsetX,
2502
+ y: y + height - layout.offsetY,
2503
+ width: layout.width,
2504
+ height: layout.height,
2390
2505
  mapColor: getSvgColorMapper(options.colorType)
2391
2506
  });
2392
2507
  } finally {
@@ -2550,7 +2665,7 @@ var barcodeDefaults = [
2550
2665
  position,
2551
2666
  ...defaultColors,
2552
2667
  width: 40,
2553
- height: 16,
2668
+ height: 11.65,
2554
2669
  rotate: 0,
2555
2670
  opacity: 1
2556
2671
  } }
@@ -2622,9 +2737,25 @@ var createBarcodeImage = async (schema, value) => {
2622
2737
  return await blobToDataURL(new Blob([new Uint8Array(imageBuf)], { type: "image/png" }));
2623
2738
  };
2624
2739
  var createBarcodeImageElm = async (schema, value) => {
2625
- const barcodeDataURL = await createBarcodeImage(schema, value);
2626
2740
  const img = document.createElement("img");
2627
- img.src = barcodeDataURL;
2741
+ if (isAspectRatioLockedBarcodeType(schema.type)) {
2742
+ const svg = createBarCodeSvg({
2743
+ ...schema,
2744
+ width: void 0,
2745
+ height: void 0,
2746
+ backgroundColor: void 0,
2747
+ input: value
2748
+ });
2749
+ img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
2750
+ const imgStyle = {
2751
+ ...fullSize,
2752
+ objectFit: "contain",
2753
+ borderRadius: 0
2754
+ };
2755
+ Object.assign(img.style, imgStyle);
2756
+ return img;
2757
+ }
2758
+ img.src = await createBarcodeImage(schema, value);
2628
2759
  const imgStyle = {
2629
2760
  ...fullSize,
2630
2761
  borderRadius: 0
@@ -2695,7 +2826,8 @@ var barcodes = BARCODE_TYPES.reduce((acc, type) => Object.assign(acc, { [type]:
2695
2826
  pdf: pdfRender$1,
2696
2827
  ui: uiRender$1,
2697
2828
  propPanel: getPropPanelByBarcodeType(type),
2698
- icon: createSvgStr(type == "qrcode" ? QrCode : Barcode)
2829
+ icon: createSvgStr(type == "qrcode" ? QrCode : Barcode),
2830
+ ...isAspectRatioLockedBarcodeType(type) ? { getAspectRatio: getBarcodeAspectRatio } : {}
2699
2831
  } }), {});
2700
2832
  //#endregion
2701
2833
  //#region src/shapes/line.ts