@jsonpdf/plugins 0.1.0-alpha.1

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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/barcode/barcode-generator.d.ts +18 -0
  4. package/dist/barcode/barcode-generator.d.ts.map +1 -0
  5. package/dist/barcode/barcode-generator.js +94 -0
  6. package/dist/barcode/barcode-generator.js.map +1 -0
  7. package/dist/barcode/barcode-plugin.d.ts +4 -0
  8. package/dist/barcode/barcode-plugin.d.ts.map +1 -0
  9. package/dist/barcode/barcode-plugin.js +70 -0
  10. package/dist/barcode/barcode-plugin.js.map +1 -0
  11. package/dist/barcode/barcode-types.d.ts +29 -0
  12. package/dist/barcode/barcode-types.d.ts.map +1 -0
  13. package/dist/barcode/barcode-types.js +60 -0
  14. package/dist/barcode/barcode-types.js.map +1 -0
  15. package/dist/chart/chart-generator.d.ts +17 -0
  16. package/dist/chart/chart-generator.d.ts.map +1 -0
  17. package/dist/chart/chart-generator.js +77 -0
  18. package/dist/chart/chart-generator.js.map +1 -0
  19. package/dist/chart/chart-plugin.d.ts +4 -0
  20. package/dist/chart/chart-plugin.d.ts.map +1 -0
  21. package/dist/chart/chart-plugin.js +65 -0
  22. package/dist/chart/chart-plugin.js.map +1 -0
  23. package/dist/chart/chart-types.d.ts +18 -0
  24. package/dist/chart/chart-types.d.ts.map +1 -0
  25. package/dist/chart/chart-types.js +18 -0
  26. package/dist/chart/chart-types.js.map +1 -0
  27. package/dist/container/container-plugin.d.ts +15 -0
  28. package/dist/container/container-plugin.d.ts.map +1 -0
  29. package/dist/container/container-plugin.js +190 -0
  30. package/dist/container/container-plugin.js.map +1 -0
  31. package/dist/frame/frame-plugin.d.ts +4 -0
  32. package/dist/frame/frame-plugin.d.ts.map +1 -0
  33. package/dist/frame/frame-plugin.js +52 -0
  34. package/dist/frame/frame-plugin.js.map +1 -0
  35. package/dist/frame/frame-types.d.ts +8 -0
  36. package/dist/frame/frame-types.d.ts.map +1 -0
  37. package/dist/frame/frame-types.js +17 -0
  38. package/dist/frame/frame-types.js.map +1 -0
  39. package/dist/image/image-loader.d.ts +13 -0
  40. package/dist/image/image-loader.d.ts.map +1 -0
  41. package/dist/image/image-loader.js +73 -0
  42. package/dist/image/image-loader.js.map +1 -0
  43. package/dist/image/image-plugin.d.ts +19 -0
  44. package/dist/image/image-plugin.d.ts.map +1 -0
  45. package/dist/image/image-plugin.js +112 -0
  46. package/dist/image/image-plugin.js.map +1 -0
  47. package/dist/image/svg-rasterizer.d.ts +30 -0
  48. package/dist/image/svg-rasterizer.d.ts.map +1 -0
  49. package/dist/image/svg-rasterizer.js +97 -0
  50. package/dist/image/svg-rasterizer.js.map +1 -0
  51. package/dist/index.d.ts +32 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +38 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/line/line-plugin.d.ts +11 -0
  56. package/dist/line/line-plugin.d.ts.map +1 -0
  57. package/dist/line/line-plugin.js +68 -0
  58. package/dist/line/line-plugin.js.map +1 -0
  59. package/dist/list/list-plugin.d.ts +13 -0
  60. package/dist/list/list-plugin.d.ts.map +1 -0
  61. package/dist/list/list-plugin.js +194 -0
  62. package/dist/list/list-plugin.js.map +1 -0
  63. package/dist/list/list-types.d.ts +9 -0
  64. package/dist/list/list-types.d.ts.map +1 -0
  65. package/dist/list/list-types.js +9 -0
  66. package/dist/list/list-types.js.map +1 -0
  67. package/dist/platform/base64.d.ts +7 -0
  68. package/dist/platform/base64.d.ts.map +1 -0
  69. package/dist/platform/base64.js +17 -0
  70. package/dist/platform/base64.js.map +1 -0
  71. package/dist/platform/font-store.d.ts +3 -0
  72. package/dist/platform/font-store.d.ts.map +1 -0
  73. package/dist/platform/font-store.js +8 -0
  74. package/dist/platform/font-store.js.map +1 -0
  75. package/dist/platform/fs.browser.d.ts +6 -0
  76. package/dist/platform/fs.browser.d.ts.map +1 -0
  77. package/dist/platform/fs.browser.js +10 -0
  78. package/dist/platform/fs.browser.js.map +1 -0
  79. package/dist/platform/fs.d.ts +5 -0
  80. package/dist/platform/fs.d.ts.map +1 -0
  81. package/dist/platform/fs.js +10 -0
  82. package/dist/platform/fs.js.map +1 -0
  83. package/dist/platform/init.browser.d.ts +10 -0
  84. package/dist/platform/init.browser.d.ts.map +1 -0
  85. package/dist/platform/init.browser.js +24 -0
  86. package/dist/platform/init.browser.js.map +1 -0
  87. package/dist/platform/init.d.ts +6 -0
  88. package/dist/platform/init.d.ts.map +1 -0
  89. package/dist/platform/init.js +12 -0
  90. package/dist/platform/init.js.map +1 -0
  91. package/dist/platform/svg-rasterizer.browser.d.ts +2 -0
  92. package/dist/platform/svg-rasterizer.browser.d.ts.map +1 -0
  93. package/dist/platform/svg-rasterizer.browser.js +2 -0
  94. package/dist/platform/svg-rasterizer.browser.js.map +1 -0
  95. package/dist/platform/svg-rasterizer.d.ts +2 -0
  96. package/dist/platform/svg-rasterizer.d.ts.map +1 -0
  97. package/dist/platform/svg-rasterizer.js +2 -0
  98. package/dist/platform/svg-rasterizer.js.map +1 -0
  99. package/dist/registry.d.ts +32 -0
  100. package/dist/registry.d.ts.map +1 -0
  101. package/dist/registry.js +67 -0
  102. package/dist/registry.js.map +1 -0
  103. package/dist/shape/shape-plugin.d.ts +21 -0
  104. package/dist/shape/shape-plugin.d.ts.map +1 -0
  105. package/dist/shape/shape-plugin.js +148 -0
  106. package/dist/shape/shape-plugin.js.map +1 -0
  107. package/dist/table/table-plugin.d.ts +4 -0
  108. package/dist/table/table-plugin.d.ts.map +1 -0
  109. package/dist/table/table-plugin.js +232 -0
  110. package/dist/table/table-plugin.js.map +1 -0
  111. package/dist/table/table-types.d.ts +50 -0
  112. package/dist/table/table-types.d.ts.map +1 -0
  113. package/dist/table/table-types.js +129 -0
  114. package/dist/table/table-types.js.map +1 -0
  115. package/dist/text/text-decoration.d.ts +22 -0
  116. package/dist/text/text-decoration.d.ts.map +1 -0
  117. package/dist/text/text-decoration.js +43 -0
  118. package/dist/text/text-decoration.js.map +1 -0
  119. package/dist/text/text-plugin.d.ts +9 -0
  120. package/dist/text/text-plugin.d.ts.map +1 -0
  121. package/dist/text/text-plugin.js +496 -0
  122. package/dist/text/text-plugin.js.map +1 -0
  123. package/dist/text/word-wrap.d.ts +41 -0
  124. package/dist/text/word-wrap.d.ts.map +1 -0
  125. package/dist/text/word-wrap.js +149 -0
  126. package/dist/text/word-wrap.js.map +1 -0
  127. package/dist/types.d.ts +96 -0
  128. package/dist/types.d.ts.map +1 -0
  129. package/dist/types.js +5 -0
  130. package/dist/types.js.map +1 -0
  131. package/dist/utils.d.ts +8 -0
  132. package/dist/utils.d.ts.map +1 -0
  133. package/dist/utils.js +27 -0
  134. package/dist/utils.js.map +1 -0
  135. package/package.json +62 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 JsonPDF
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @jsonpdf/plugins
2
+
3
+ Element type implementations for the jsonpdf rendering pipeline. Each plugin provides `measure()` and `render()` methods for a specific visual element type.
4
+
5
+ Plugins receive resolved values and never touch LiquidJS — expression resolution is handled upstream by the renderer.
6
+
7
+ ## Plugins
8
+
9
+ | Plugin | Description |
10
+ | ----------- | --------------------------------------------------------------------------- |
11
+ | `text` | Text rendering with word wrapping and decoration (underline, strikethrough) |
12
+ | `line` | Line drawing with configurable dash patterns |
13
+ | `list` | Bullet, numbered, and lettered lists |
14
+ | `shape` | Rectangle, circle, ellipse, and rounded rectangle |
15
+ | `image` | Image rendering with fit modes (contain, cover, stretch, none) |
16
+ | `container` | Horizontal, vertical, absolute, and grid layouts |
17
+ | `table` | Table rendering with automatic column width calculation |
18
+ | `barcode` | Barcode and QR code generation via bwip-js |
19
+ | `chart` | Chart rendering via Vega-Lite |
20
+ | `frame` | Nested band container for sub-layouts |
21
+
22
+ ## Platform abstraction
23
+
24
+ The package uses Node.js subpath imports (`#platform/*`) to swap implementations between Node and browser environments:
25
+
26
+ | Module | Node | Browser |
27
+ | -------------------------- | -------------------------- | -------------------------------------------------- |
28
+ | `#platform/fs` | Node `fs` for file reading | Throws (files must be inlined) |
29
+ | `#platform/svg-rasterizer` | `@resvg/resvg-js` (native) | `@resvg/resvg-wasm` |
30
+ | `#platform/init` | No-op | Requires `initBrowser(resvgWasm)` before rendering |
31
+
32
+ Browser consumers must call `initBrowser(resvgWasm)` once before rendering.
33
+
34
+ ## Usage
35
+
36
+ ```ts
37
+ import { textPlugin, imagePlugin, PluginRegistry } from '@jsonpdf/plugins';
38
+
39
+ // Plugins are registered automatically when imported
40
+ // Use the registry to look up plugins by element type
41
+ const plugin = PluginRegistry.get('text');
42
+ ```
43
+
44
+ ## Exports
45
+
46
+ | Category | Exports |
47
+ | --------- | --------------------------------------------------------------------------------------------- |
48
+ | Registry | `PluginRegistry`, `registerPlugin`, `getPlugin`, `hasPlugin`, `getAllPlugins`, `clearPlugins` |
49
+ | Text | `textPlugin`, `wrapText`, `measureTextWidth` |
50
+ | Line | `linePlugin` |
51
+ | List | `listPlugin`, `ListItem`, `isListItem`, `toListItem` |
52
+ | Shape | `shapePlugin`, `roundedRectPath` |
53
+ | Image | `imagePlugin`, `computeFitDimensions`, `createImageCache`, `loadImageBytes`, `rasterizeSvg` |
54
+ | Container | `containerPlugin` |
55
+ | Table | `tablePlugin`, `TableColumn`, `computeColumnWidths` |
56
+ | Barcode | `barcodePlugin`, `generateBarcode`, `createBarcodeCache`, `BarcodeFormat` |
57
+ | Chart | `chartPlugin`, `generateChart`, `createChartCache`, `buildFinalSpec` |
58
+ | Frame | `framePlugin` |
59
+ | Platform | `readFileBytes`, `uint8ArrayToBase64`, `initBrowser` |
@@ -0,0 +1,18 @@
1
+ import type { BarcodeProps } from './barcode-types.js';
2
+ /** Cache for generated barcode data URIs, keyed by deterministic option string. */
3
+ export type BarcodeCache = Map<string, Promise<string>>;
4
+ /** Create a new barcode cache instance. */
5
+ export declare function createBarcodeCache(): BarcodeCache;
6
+ /**
7
+ * Strip '#' from a hex color for bwip-js (expects hex without prefix).
8
+ * Handles both '#RGB' shorthand and '#RRGGBB'.
9
+ */
10
+ export declare function toBwipColor(hex: string): string;
11
+ /**
12
+ * Generate a barcode PNG and return it as a data URI string.
13
+ *
14
+ * Uses the provided cache to deduplicate calls with identical props
15
+ * (important because the two-pass renderer calls measure+render twice).
16
+ */
17
+ export declare function generateBarcode(props: BarcodeProps, cache: BarcodeCache): Promise<string>;
18
+ //# sourceMappingURL=barcode-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"barcode-generator.d.ts","sourceRoot":"","sources":["../../src/barcode/barcode-generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAKvD,mFAAmF;AACnF,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAExD,2CAA2C;AAC3C,wBAAgB,kBAAkB,IAAI,YAAY,CAEjD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM/C;AAiBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBzF"}
@@ -0,0 +1,94 @@
1
+ import BwipJs from 'bwip-js';
2
+ import { TWO_D_FORMATS } from './barcode-types.js';
3
+ import { rasterizeSvg } from '../image/svg-rasterizer.js';
4
+ import { uint8ArrayToBase64 } from '../platform/base64.js';
5
+ /** Create a new barcode cache instance. */
6
+ export function createBarcodeCache() {
7
+ return new Map();
8
+ }
9
+ /**
10
+ * Strip '#' from a hex color for bwip-js (expects hex without prefix).
11
+ * Handles both '#RGB' shorthand and '#RRGGBB'.
12
+ */
13
+ export function toBwipColor(hex) {
14
+ const raw = hex.startsWith('#') ? hex.slice(1) : hex;
15
+ if (raw.length === 3) {
16
+ return raw[0] + raw[0] + raw[1] + raw[1] + raw[2] + raw[2];
17
+ }
18
+ return raw;
19
+ }
20
+ /** Build a deterministic cache key from barcode props. */
21
+ function cacheKey(props) {
22
+ return JSON.stringify({
23
+ v: props.value,
24
+ f: props.format,
25
+ bc: props.barColor ?? '#000000',
26
+ bg: props.backgroundColor ?? '#FFFFFF',
27
+ it: props.includeText ?? false,
28
+ ts: props.textSize,
29
+ s: props.scale ?? 3,
30
+ mh: props.moduleHeight ?? 10,
31
+ p: props.padding ?? 2,
32
+ });
33
+ }
34
+ /**
35
+ * Generate a barcode PNG and return it as a data URI string.
36
+ *
37
+ * Uses the provided cache to deduplicate calls with identical props
38
+ * (important because the two-pass renderer calls measure+render twice).
39
+ */
40
+ export function generateBarcode(props, cache) {
41
+ const key = cacheKey(props);
42
+ const existing = cache.get(key);
43
+ if (existing) {
44
+ return existing;
45
+ }
46
+ // Wrap in a Promise constructor so synchronous throws become rejections
47
+ // without creating a transiently unhandled rejected promise.
48
+ const promise = new Promise((resolve, reject) => {
49
+ try {
50
+ resolve(generateBarcodeUncached(props));
51
+ }
52
+ catch (err) {
53
+ reject(err instanceof Error ? err : new Error(String(err)));
54
+ }
55
+ });
56
+ // Remove from cache on failure so retries can succeed
57
+ promise.catch(() => cache.delete(key));
58
+ cache.set(key, promise);
59
+ return promise;
60
+ }
61
+ function generateBarcodeUncached(props) {
62
+ const options = {
63
+ bcid: props.format,
64
+ text: props.value,
65
+ scale: props.scale ?? 3,
66
+ paddingwidth: props.padding ?? 2,
67
+ paddingheight: props.padding ?? 2,
68
+ backgroundcolor: toBwipColor(props.backgroundColor ?? '#FFFFFF'),
69
+ barcolor: toBwipColor(props.barColor ?? '#000000'),
70
+ };
71
+ // Only set height for linear barcodes (not 2D codes)
72
+ if (!TWO_D_FORMATS.has(props.format)) {
73
+ options.height = props.moduleHeight ?? 10;
74
+ }
75
+ if (props.includeText) {
76
+ options.includetext = true;
77
+ options.textxalign = 'center';
78
+ if (props.textSize !== undefined) {
79
+ options.textsize = props.textSize;
80
+ }
81
+ }
82
+ let svgString;
83
+ try {
84
+ svgString = BwipJs.toSVG(options);
85
+ }
86
+ catch (err) {
87
+ const message = err instanceof Error ? err.message : String(err);
88
+ throw new Error(`Barcode generation failed for format "${props.format}" with value "${props.value}": ${message}`);
89
+ }
90
+ const { pngBytes } = rasterizeSvg(svgString, 1);
91
+ const base64 = uint8ArrayToBase64(pngBytes);
92
+ return `data:image/png;base64,${base64}`;
93
+ }
94
+ //# sourceMappingURL=barcode-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"barcode-generator.js","sourceRoot":"","sources":["../../src/barcode/barcode-generator.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAK3D,2CAA2C;AAC3C,MAAM,UAAU,kBAAkB;IAChC,OAAO,IAAI,GAAG,EAAE,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACrD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0DAA0D;AAC1D,SAAS,QAAQ,CAAC,KAAmB;IACnC,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,CAAC,EAAE,KAAK,CAAC,KAAK;QACd,CAAC,EAAE,KAAK,CAAC,MAAM;QACf,EAAE,EAAE,KAAK,CAAC,QAAQ,IAAI,SAAS;QAC/B,EAAE,EAAE,KAAK,CAAC,eAAe,IAAI,SAAS;QACtC,EAAE,EAAE,KAAK,CAAC,WAAW,IAAI,KAAK;QAC9B,EAAE,EAAE,KAAK,CAAC,QAAQ;QAClB,CAAC,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC;QACnB,EAAE,EAAE,KAAK,CAAC,YAAY,IAAI,EAAE;QAC5B,CAAC,EAAE,KAAK,CAAC,OAAO,IAAI,CAAC;KACtB,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAmB,EAAE,KAAmB;IACtE,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE5B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,wEAAwE;IACxE,6DAA6D;IAC7D,MAAM,OAAO,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtD,IAAI,CAAC;YACH,OAAO,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CAAC,CAAC;IACH,sDAAsD;IACtD,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACxB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAmB;IAClD,MAAM,OAAO,GAAyB;QACpC,IAAI,EAAE,KAAK,CAAC,MAAM;QAClB,IAAI,EAAE,KAAK,CAAC,KAAK;QACjB,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC;QACvB,YAAY,EAAE,KAAK,CAAC,OAAO,IAAI,CAAC;QAChC,aAAa,EAAE,KAAK,CAAC,OAAO,IAAI,CAAC;QACjC,eAAe,EAAE,WAAW,CAAC,KAAK,CAAC,eAAe,IAAI,SAAS,CAAC;QAChE,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC,QAAQ,IAAI,SAAS,CAAC;KACnD,CAAC;IAEF,qDAAqD;IACrD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC;IAC5C,CAAC;IAED,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;QACtB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;QAC3B,OAAO,CAAC,UAAU,GAAG,QAAQ,CAAC;QAC9B,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACjC,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QACpC,CAAC;IACH,CAAC;IAED,IAAI,SAAiB,CAAC;IACtB,IAAI,CAAC;QACH,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,KAAK,CACb,yCAAyC,KAAK,CAAC,MAAM,iBAAiB,KAAK,CAAC,KAAK,MAAM,OAAO,EAAE,CACjG,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC5C,OAAO,yBAAyB,MAAM,EAAE,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from '../types.js';
2
+ import { type BarcodeProps } from './barcode-types.js';
3
+ export declare const barcodePlugin: Plugin<BarcodeProps>;
4
+ //# sourceMappingURL=barcode-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"barcode-plugin.d.ts","sourceRoot":"","sources":["../../src/barcode/barcode-plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAgD,MAAM,aAAa,CAAC;AAExF,OAAO,EACL,KAAK,YAAY,EAIlB,MAAM,oBAAoB,CAAC;AAY5B,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,YAAY,CAoF9C,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { computeFitDimensions } from '../image/image-plugin.js';
2
+ import { barcodePropsSchema, BARCODE_DEFAULTS, SUPPORTED_FORMATS, } from './barcode-types.js';
3
+ import { generateBarcode, createBarcodeCache } from './barcode-generator.js';
4
+ /** Module-level barcode cache shared across measure and render passes. */
5
+ const barcodeCache = createBarcodeCache();
6
+ /** Get the barcode as an embedded PDF image. */
7
+ async function getBarcode(props, ctx) {
8
+ const dataUri = await generateBarcode(props, barcodeCache);
9
+ return ctx.imageCache.getOrEmbed(dataUri, ctx.pdfDoc);
10
+ }
11
+ export const barcodePlugin = {
12
+ type: 'barcode',
13
+ propsSchema: barcodePropsSchema,
14
+ defaultProps: BARCODE_DEFAULTS,
15
+ resolveProps(raw) {
16
+ return { ...BARCODE_DEFAULTS, ...raw };
17
+ },
18
+ validate(props) {
19
+ const errors = [];
20
+ if (!props.value || typeof props.value !== 'string' || props.value.trim().length === 0) {
21
+ errors.push({ path: '/value', message: 'value is required and must be a non-empty string' });
22
+ }
23
+ if (!SUPPORTED_FORMATS.includes(props.format)) {
24
+ errors.push({
25
+ path: '/format',
26
+ message: `format must be one of: ${SUPPORTED_FORMATS.join(', ')}`,
27
+ });
28
+ }
29
+ if (props.barColor !== undefined &&
30
+ typeof props.barColor === 'string' &&
31
+ !props.barColor.startsWith('#')) {
32
+ errors.push({ path: '/barColor', message: 'barColor must be a hex string starting with #' });
33
+ }
34
+ if (props.backgroundColor !== undefined &&
35
+ typeof props.backgroundColor === 'string' &&
36
+ !props.backgroundColor.startsWith('#')) {
37
+ errors.push({
38
+ path: '/backgroundColor',
39
+ message: 'backgroundColor must be a hex string starting with #',
40
+ });
41
+ }
42
+ return errors;
43
+ },
44
+ async measure(props, ctx) {
45
+ if (!props.value || props.value.trim().length === 0) {
46
+ return { width: 0, height: 0 };
47
+ }
48
+ await getBarcode(props, ctx);
49
+ return { width: ctx.availableWidth, height: ctx.availableHeight };
50
+ },
51
+ async render(props, ctx) {
52
+ if (!props.value || props.value.trim().length === 0) {
53
+ return;
54
+ }
55
+ const embedded = await getBarcode(props, ctx);
56
+ const { drawWidth, drawHeight, offsetX, offsetY } = computeFitDimensions(embedded.width, embedded.height, ctx.width, ctx.height, 'contain');
57
+ // pdf-lib drawImage: x,y is the bottom-left corner.
58
+ // ctx.x, ctx.y is the top-left of content area in pdf-lib coords (y up).
59
+ const imgX = ctx.x + offsetX;
60
+ const imgY = ctx.y - offsetY - drawHeight;
61
+ ctx.page.drawImage(embedded.image, {
62
+ x: imgX,
63
+ y: imgY,
64
+ width: drawWidth,
65
+ height: drawHeight,
66
+ opacity: ctx.opacity,
67
+ });
68
+ },
69
+ };
70
+ //# sourceMappingURL=barcode-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"barcode-plugin.js","sourceRoot":"","sources":["../../src/barcode/barcode-plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { JSONSchema } from '@jsonpdf/core';
2
+ /** Supported barcode/QR format identifiers (bwip-js bcid values). */
3
+ export declare const SUPPORTED_FORMATS: readonly ["qrcode", "datamatrix", "pdf417", "azteccode", "azteccodecompact", "maxicode", "code128", "code39", "ean13", "ean8", "upca", "upce", "itf14", "codabar", "interleaved2of5", "code93", "isbn", "issn", "gs1-128"];
4
+ export type BarcodeFormat = (typeof SUPPORTED_FORMATS)[number];
5
+ export interface BarcodeProps {
6
+ /** Data to encode (required). */
7
+ value: string;
8
+ /** Barcode type — bwip-js bcid (required). */
9
+ format: BarcodeFormat;
10
+ /** Bar/module color as hex string. Default '#000000'. */
11
+ barColor?: string;
12
+ /** Background color as hex string. Default '#FFFFFF'. */
13
+ backgroundColor?: string;
14
+ /** Show human-readable text below linear barcodes. Default false. */
15
+ includeText?: boolean;
16
+ /** Font size for the human-readable text in points. */
17
+ textSize?: number;
18
+ /** Module scale factor. Default 3. Higher = larger PNG before fitting. */
19
+ scale?: number;
20
+ /** Bar height in mm for linear barcodes. Default 10. Ignored for 2D codes. */
21
+ moduleHeight?: number;
22
+ /** Quiet zone padding in modules. Default 2. */
23
+ padding?: number;
24
+ }
25
+ export declare const barcodePropsSchema: JSONSchema;
26
+ export declare const BARCODE_DEFAULTS: BarcodeProps;
27
+ /** Set of 2D barcode formats where moduleHeight does not apply. */
28
+ export declare const TWO_D_FORMATS: ReadonlySet<string>;
29
+ //# sourceMappingURL=barcode-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"barcode-types.d.ts","sourceRoot":"","sources":["../../src/barcode/barcode-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,4NAsBpB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,MAAM,EAAE,aAAa,CAAC;IACtB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,kBAAkB,EAAE,UAehC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,YAS9B,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,CAO5C,CAAC"}
@@ -0,0 +1,60 @@
1
+ /** Supported barcode/QR format identifiers (bwip-js bcid values). */
2
+ export const SUPPORTED_FORMATS = [
3
+ // 2D codes
4
+ 'qrcode',
5
+ 'datamatrix',
6
+ 'pdf417',
7
+ 'azteccode',
8
+ 'azteccodecompact',
9
+ 'maxicode',
10
+ // Linear codes
11
+ 'code128',
12
+ 'code39',
13
+ 'ean13',
14
+ 'ean8',
15
+ 'upca',
16
+ 'upce',
17
+ 'itf14',
18
+ 'codabar',
19
+ 'interleaved2of5',
20
+ 'code93',
21
+ 'isbn',
22
+ 'issn',
23
+ 'gs1-128',
24
+ ];
25
+ export const barcodePropsSchema = {
26
+ type: 'object',
27
+ required: ['value', 'format'],
28
+ additionalProperties: false,
29
+ properties: {
30
+ value: { type: 'string', minLength: 1 },
31
+ format: { type: 'string', enum: [...SUPPORTED_FORMATS] },
32
+ barColor: { type: 'string' },
33
+ backgroundColor: { type: 'string' },
34
+ includeText: { type: 'boolean' },
35
+ textSize: { type: 'number', minimum: 1 },
36
+ scale: { type: 'number', minimum: 1 },
37
+ moduleHeight: { type: 'number', minimum: 1 },
38
+ padding: { type: 'number', minimum: 0 },
39
+ },
40
+ };
41
+ export const BARCODE_DEFAULTS = {
42
+ value: '',
43
+ format: 'qrcode',
44
+ barColor: '#000000',
45
+ backgroundColor: '#FFFFFF',
46
+ includeText: false,
47
+ scale: 3,
48
+ moduleHeight: 10,
49
+ padding: 2,
50
+ };
51
+ /** Set of 2D barcode formats where moduleHeight does not apply. */
52
+ export const TWO_D_FORMATS = new Set([
53
+ 'qrcode',
54
+ 'datamatrix',
55
+ 'pdf417',
56
+ 'azteccode',
57
+ 'azteccodecompact',
58
+ 'maxicode',
59
+ ]);
60
+ //# sourceMappingURL=barcode-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"barcode-types.js","sourceRoot":"","sources":["../../src/barcode/barcode-types.ts"],"names":[],"mappings":"AAEA,qEAAqE;AACrE,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,WAAW;IACX,QAAQ;IACR,YAAY;IACZ,QAAQ;IACR,WAAW;IACX,kBAAkB;IAClB,UAAU;IACV,eAAe;IACf,SAAS;IACT,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,SAAS;IACT,iBAAiB;IACjB,QAAQ;IACR,MAAM;IACN,MAAM;IACN,SAAS;CACD,CAAC;AAyBX,MAAM,CAAC,MAAM,kBAAkB,GAAe;IAC5C,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC;IAC7B,oBAAoB,EAAE,KAAK;IAC3B,UAAU,EAAE;QACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE;QACvC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,iBAAiB,CAAC,EAAE;QACxD,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC5B,eAAe,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACnC,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;QAChC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;QACxC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;QACrC,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;QAC5C,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;KACxC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAiB;IAC5C,KAAK,EAAE,EAAE;IACT,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,SAAS;IACnB,eAAe,EAAE,SAAS;IAC1B,WAAW,EAAE,KAAK;IAClB,KAAK,EAAE,CAAC;IACR,YAAY,EAAE,EAAE;IAChB,OAAO,EAAE,CAAC;CACX,CAAC;AAEF,mEAAmE;AACnE,MAAM,CAAC,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC;IACxD,QAAQ;IACR,YAAY;IACZ,QAAQ;IACR,WAAW;IACX,kBAAkB;IAClB,UAAU;CACX,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { ChartProps } from './chart-types.js';
2
+ /** Cache for generated chart PNG data URIs. */
3
+ export type ChartCache = Map<string, Promise<string>>;
4
+ /** Create a new chart cache instance. */
5
+ export declare function createChartCache(): ChartCache;
6
+ /**
7
+ * Build the final Vega-Lite spec by merging dataSource and background.
8
+ */
9
+ export declare function buildFinalSpec(props: ChartProps): Record<string, unknown>;
10
+ /**
11
+ * Generate a chart PNG data URI from a resolved Vega-Lite spec.
12
+ *
13
+ * Uses the provided cache to deduplicate calls with identical specs
14
+ * (important because the two-pass renderer calls measure+render twice).
15
+ */
16
+ export declare function generateChart(props: ChartProps, cache: ChartCache): Promise<string>;
17
+ //# sourceMappingURL=chart-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-generator.d.ts","sourceRoot":"","sources":["../../src/chart/chart-generator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,+CAA+C;AAC/C,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAEtD,yCAAyC;AACzC,wBAAgB,gBAAgB,IAAI,UAAU,CAE7C;AAOD;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYzE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAYnF"}
@@ -0,0 +1,77 @@
1
+ import * as vl from 'vega-lite';
2
+ import * as vega from 'vega';
3
+ import { rasterizeSvg } from '../image/svg-rasterizer.js';
4
+ import { uint8ArrayToBase64 } from '../platform/base64.js';
5
+ /** Create a new chart cache instance. */
6
+ export function createChartCache() {
7
+ return new Map();
8
+ }
9
+ /** Build a deterministic cache key from resolved spec + scale. */
10
+ function cacheKey(spec, scale) {
11
+ return JSON.stringify({ s: spec, sc: scale });
12
+ }
13
+ /**
14
+ * Build the final Vega-Lite spec by merging dataSource and background.
15
+ */
16
+ export function buildFinalSpec(props) {
17
+ let spec = { ...props.spec };
18
+ if (props.dataSource !== undefined) {
19
+ spec = { ...spec, data: { values: props.dataSource } };
20
+ }
21
+ if (props.background !== undefined) {
22
+ spec = { ...spec, background: props.background };
23
+ }
24
+ return spec;
25
+ }
26
+ /**
27
+ * Generate a chart PNG data URI from a resolved Vega-Lite spec.
28
+ *
29
+ * Uses the provided cache to deduplicate calls with identical specs
30
+ * (important because the two-pass renderer calls measure+render twice).
31
+ */
32
+ export function generateChart(props, cache) {
33
+ const finalSpec = buildFinalSpec(props);
34
+ const scale = props.scale ?? 2;
35
+ const key = cacheKey(finalSpec, scale);
36
+ const existing = cache.get(key);
37
+ if (existing)
38
+ return existing;
39
+ const promise = generateChartUncached(finalSpec, scale);
40
+ promise.catch(() => cache.delete(key));
41
+ cache.set(key, promise);
42
+ return promise;
43
+ }
44
+ async function generateChartUncached(spec, scale) {
45
+ // Step 1: Compile Vega-Lite spec to Vega spec
46
+ let vegaSpec;
47
+ try {
48
+ const result = vl.compile(spec);
49
+ vegaSpec = result.spec;
50
+ }
51
+ catch (err) {
52
+ const message = err instanceof Error ? err.message : String(err);
53
+ throw new Error(`Vega-Lite compilation failed: ${message}`);
54
+ }
55
+ // Step 2: Render Vega spec to SVG
56
+ let svgString;
57
+ const runtime = vega.parse(vegaSpec);
58
+ const view = new vega.View(runtime, { renderer: 'none' });
59
+ try {
60
+ view.initialize();
61
+ await view.runAsync();
62
+ svgString = await view.toSVG();
63
+ }
64
+ catch (err) {
65
+ const message = err instanceof Error ? err.message : String(err);
66
+ throw new Error(`Vega chart rendering failed: ${message}`);
67
+ }
68
+ finally {
69
+ view.finalize();
70
+ }
71
+ // Step 3: Rasterize SVG to PNG
72
+ const { pngBytes } = rasterizeSvg(svgString, scale);
73
+ // Step 4: Convert to data URI
74
+ const base64 = uint8ArrayToBase64(pngBytes);
75
+ return `data:image/png;base64,${base64}`;
76
+ }
77
+ //# sourceMappingURL=chart-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-generator.js","sourceRoot":"","sources":["../../src/chart/chart-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from '../types.js';
2
+ import { type ChartProps } from './chart-types.js';
3
+ export declare const chartPlugin: Plugin<ChartProps>;
4
+ //# sourceMappingURL=chart-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-plugin.d.ts","sourceRoot":"","sources":["../../src/chart/chart-plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAgD,MAAM,aAAa,CAAC;AAExF,OAAO,EAAE,KAAK,UAAU,EAAoC,MAAM,kBAAkB,CAAC;AAYrF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,UAAU,CA0E1C,CAAC"}
@@ -0,0 +1,65 @@
1
+ import { computeFitDimensions } from '../image/image-plugin.js';
2
+ import { chartPropsSchema, CHART_DEFAULTS } from './chart-types.js';
3
+ import { generateChart, createChartCache } from './chart-generator.js';
4
+ /** Module-level chart cache shared across measure and render passes. */
5
+ const chartCache = createChartCache();
6
+ /** Get the chart as an embedded PDF image. */
7
+ async function getChart(props, ctx) {
8
+ const dataUri = await generateChart(props, chartCache);
9
+ return ctx.imageCache.getOrEmbed(dataUri, ctx.pdfDoc);
10
+ }
11
+ export const chartPlugin = {
12
+ type: 'chart',
13
+ propsSchema: chartPropsSchema,
14
+ defaultProps: CHART_DEFAULTS,
15
+ resolveProps(raw) {
16
+ return { ...CHART_DEFAULTS, ...raw };
17
+ },
18
+ validate(props) {
19
+ const errors = [];
20
+ const spec = props.spec;
21
+ if (!spec || typeof spec !== 'object' || Array.isArray(spec)) {
22
+ errors.push({ path: '/spec', message: 'spec is required and must be a non-empty object' });
23
+ }
24
+ else if (Object.keys(props.spec).length === 0) {
25
+ errors.push({ path: '/spec', message: 'spec must not be empty' });
26
+ }
27
+ if (props.dataSource !== undefined && !Array.isArray(props.dataSource)) {
28
+ errors.push({ path: '/dataSource', message: 'dataSource must be an array' });
29
+ }
30
+ if (props.fit !== undefined && !['contain', 'cover', 'fill', 'none'].includes(props.fit)) {
31
+ errors.push({ path: '/fit', message: 'fit must be contain, cover, fill, or none' });
32
+ }
33
+ if (props.scale !== undefined && (typeof props.scale !== 'number' || props.scale < 0.5)) {
34
+ errors.push({ path: '/scale', message: 'scale must be a number >= 0.5' });
35
+ }
36
+ return errors;
37
+ },
38
+ async measure(props, ctx) {
39
+ if (Object.keys(props.spec).length === 0) {
40
+ return { width: 0, height: 0 };
41
+ }
42
+ await getChart(props, ctx);
43
+ return { width: ctx.availableWidth, height: ctx.availableHeight };
44
+ },
45
+ async render(props, ctx) {
46
+ if (Object.keys(props.spec).length === 0) {
47
+ return;
48
+ }
49
+ const embedded = await getChart(props, ctx);
50
+ const fit = props.fit ?? 'contain';
51
+ const { drawWidth, drawHeight, offsetX, offsetY } = computeFitDimensions(embedded.width, embedded.height, ctx.width, ctx.height, fit);
52
+ // pdf-lib drawImage: x,y is the bottom-left corner.
53
+ // ctx.x, ctx.y is the top-left of content area in pdf-lib coords (y up).
54
+ const imgX = ctx.x + offsetX;
55
+ const imgY = ctx.y - offsetY - drawHeight;
56
+ ctx.page.drawImage(embedded.image, {
57
+ x: imgX,
58
+ y: imgY,
59
+ width: drawWidth,
60
+ height: drawHeight,
61
+ opacity: ctx.opacity,
62
+ });
63
+ },
64
+ };
65
+ //# sourceMappingURL=chart-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-plugin.js","sourceRoot":"","sources":["../../src/chart/chart-plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAmB,gBAAgB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAmB,MAAM,sBAAsB,CAAC;AAExF,wEAAwE;AACxE,MAAM,UAAU,GAAe,gBAAgB,EAAE,CAAC;AAElD,8CAA8C;AAC9C,KAAK,UAAU,QAAQ,CAAC,KAAiB,EAAE,GAAmB;IAC5D,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IACvD,OAAO,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAuB;IAC7C,IAAI,EAAE,OAAO;IACb,WAAW,EAAE,gBAAgB;IAC7B,YAAY,EAAE,cAAc;IAE5B,YAAY,CAAC,GAA4B;QACvC,OAAO,EAAE,GAAG,cAAc,EAAE,GAAG,GAAG,EAAgB,CAAC;IACrD,CAAC;IAED,QAAQ,CAAC,KAAiB;QACxB,MAAM,MAAM,GAAsB,EAAE,CAAC;QAErC,MAAM,IAAI,GAAY,KAAK,CAAC,IAAI,CAAC;QACjC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,iDAAiD,EAAE,CAAC,CAAC;QAC7F,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,wBAAwB,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;YACvE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,6BAA6B,EAAE,CAAC,CAAC;QAC/E,CAAC;QAED,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACzF,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,2CAA2C,EAAE,CAAC,CAAC;QACtF,CAAC;QAED,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,EAAE,CAAC;YACxF,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,+BAA+B,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,OAAO,CACX,KAAiB,EACjB,GAAmB;QAEnB,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzC,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACjC,CAAC;QAED,MAAM,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC3B,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,CAAC,eAAe,EAAE,CAAC;IACpE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAiB,EAAE,GAAkB;QAChD,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzC,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC5C,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,SAAS,CAAC;QACnC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,oBAAoB,CACtE,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,MAAM,EACf,GAAG,CAAC,KAAK,EACT,GAAG,CAAC,MAAM,EACV,GAAG,CACJ,CAAC;QAEF,oDAAoD;QACpD,yEAAyE;QACzE,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC;QAC7B,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,GAAG,OAAO,GAAG,UAAU,CAAC;QAE1C,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE;YACjC,CAAC,EAAE,IAAI;YACP,CAAC,EAAE,IAAI;YACP,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { JSONSchema } from '@jsonpdf/core';
2
+ /** Fit mode for chart image within element bounds. */
3
+ export type ChartFit = 'contain' | 'cover' | 'fill' | 'none';
4
+ export interface ChartProps {
5
+ /** Vega-Lite specification object (fully resolved by renderer). */
6
+ spec: Record<string, unknown>;
7
+ /** Optional data array to inject as spec.data.values. */
8
+ dataSource?: unknown[];
9
+ /** How the chart image fits within the element bounds. Default 'contain'. */
10
+ fit?: ChartFit;
11
+ /** Scale factor for SVG rasterization. Default 2 (HiDPI). */
12
+ scale?: number;
13
+ /** Background color for the chart. If set, overrides spec.background. */
14
+ background?: string;
15
+ }
16
+ export declare const chartPropsSchema: JSONSchema;
17
+ export declare const CHART_DEFAULTS: ChartProps;
18
+ //# sourceMappingURL=chart-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-types.d.ts","sourceRoot":"","sources":["../../src/chart/chart-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,sDAAsD;AACtD,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7D,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,yDAAyD;IACzD,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;IACvB,6EAA6E;IAC7E,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,gBAAgB,EAAE,UAW9B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,UAI5B,CAAC"}
@@ -0,0 +1,18 @@
1
+ export const chartPropsSchema = {
2
+ type: 'object',
3
+ required: ['spec'],
4
+ additionalProperties: false,
5
+ properties: {
6
+ spec: { type: 'object' },
7
+ dataSource: { type: 'array' },
8
+ fit: { type: 'string', enum: ['contain', 'cover', 'fill', 'none'] },
9
+ scale: { type: 'number', minimum: 0.5 },
10
+ background: { type: 'string' },
11
+ },
12
+ };
13
+ export const CHART_DEFAULTS = {
14
+ spec: {},
15
+ fit: 'contain',
16
+ scale: 2,
17
+ };
18
+ //# sourceMappingURL=chart-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-types.js","sourceRoot":"","sources":["../../src/chart/chart-types.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,MAAM,gBAAgB,GAAe;IAC1C,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,MAAM,CAAC;IAClB,oBAAoB,EAAE,KAAK;IAC3B,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACxB,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;QAC7B,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE;QACnE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE;QACvC,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KAC/B;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC,IAAI,EAAE,EAAE;IACR,GAAG,EAAE,SAAS;IACd,KAAK,EAAE,CAAC;CACT,CAAC"}