@polotno/pdf-export 0.1.41 → 0.2.0

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 CHANGED
@@ -1,46 +1,63 @@
1
1
  # Polotno to Vector PDF
2
2
 
3
- Convert polotno JSON into vector PDF file from NodeJS with optional PDF/X-1a print-ready export.
3
+ Convert Polotno JSON into a vector PDF, in **Node.js** or directly in the **browser**. Optional PDF/X-1a print-ready export (Node only).
4
4
 
5
5
  ```bash
6
6
  npm install @polotno/pdf-export
7
7
  ```
8
8
 
9
- ## Basic Usage
9
+ ## Node usage
10
10
 
11
11
  ```js
12
- import fs from 'fs';
12
+ import fs from 'node:fs';
13
13
  import { jsonToPDF } from '@polotno/pdf-export';
14
14
 
15
- async function run() {
16
- const json = JSON.parse(fs.readFileSync('./polotno.json'));
15
+ const json = JSON.parse(fs.readFileSync('./polotno.json', 'utf8'));
16
+ await jsonToPDF(json, './output.pdf');
17
+ ```
17
18
 
18
- // Standard PDF export
19
- await jsonToPDF(json, './output.pdf');
20
- }
19
+ `jsonToPDFBytes(json, attrs)` is also exported on the Node entry — useful when you want raw bytes (to upload, attach, etc.) without writing to disk.
21
20
 
22
- run();
21
+ ## Browser usage
22
+
23
+ ```js
24
+ import { jsonToPDFBlob } from '@polotno/pdf-export/browser';
25
+
26
+ async function exportToPDF(store) {
27
+ const blob = await jsonToPDFBlob(store.toJSON());
28
+ const url = URL.createObjectURL(blob);
29
+ const a = document.createElement('a');
30
+ a.href = url;
31
+ a.download = 'design.pdf';
32
+ document.body.appendChild(a);
33
+ a.click();
34
+ a.remove();
35
+ URL.revokeObjectURL(url);
36
+ }
23
37
  ```
24
38
 
25
- ## PDF/X-1a Print-Ready Export
39
+ Also exported: `jsonToPDFBytes(json, attrs): Promise<Uint8Array>` — for cases where you want raw bytes (to push to IndexedDB, send via WebTransport, share via `navigator.share`, etc.).
40
+
41
+ The browser entry **does not** support `attrs.pdfx1a` / `attrs.validate` — those rely on Ghostscript, which has no browser-side equivalent. Pass them and you'll get a clear runtime error pointing at the Node entry.
42
+
43
+ ### CORS
44
+
45
+ When polotno designs reference remote images, fonts, or SVG sources, the browser fetches those assets at export time. They must be reachable with CORS:
26
46
 
27
- For professional printing, use the PDF/X-1a option which ensures:
47
+ - **Google Fonts** (`fonts.googleapis.com` and `fonts.gstatic.com`) work out of the box.
48
+ - **Custom asset URLs** must be served with `Access-Control-Allow-Origin: *` (or your origin), or pre-fetched and passed as `data:` URLs in the JSON.
28
49
 
29
- - CMYK color space conversion
30
- - Transparency flattening
31
- - Font embedding/outlining
32
- - Print industry compliance
50
+ ## PDF/X-1a print-ready export (Node only)
51
+
52
+ For professional printing:
33
53
 
34
54
  ```js
35
- // PDF/X-1a export
36
- await jsonToPDF(json, './print-ready.pdf', {
37
- pdfx1a: true,
38
- });
55
+ await jsonToPDF(json, './print-ready.pdf', { pdfx1a: true });
39
56
 
40
- // PDF/X-1a with custom metadata
57
+ // With custom metadata and validation
41
58
  await jsonToPDF(json, './book-cover.pdf', {
42
59
  pdfx1a: true,
43
- validate: true, // Optional validation
60
+ validate: true,
44
61
  metadata: {
45
62
  title: 'My Book Cover',
46
63
  author: 'Author Name',
@@ -49,20 +66,25 @@ await jsonToPDF(json, './book-cover.pdf', {
49
66
  });
50
67
  ```
51
68
 
52
- ## Spot Color / Foil Support
69
+ **Requires Ghostscript:**
70
+
71
+ - macOS: `brew install ghostscript`
72
+ - Ubuntu: `apt-get install ghostscript`
73
+ - Windows: download from [ghostscript.com](https://www.ghostscript.com/)
74
+
75
+ ## Spot color / foil support
53
76
 
54
- For professional printing with special inks like metallic foils, Pantone colors, or other spot colors:
77
+ For professional printing with special inks like metallic foils or Pantone colors:
55
78
 
56
79
  ```js
57
80
  await jsonToPDF(json, './output.pdf', {
58
81
  pdfx1a: true,
59
82
  spotColors: {
60
- // Map any color to a spot color
61
83
  'rgba(255,215,0,1)': {
62
84
  name: 'Gold Foil',
63
- pantoneCode: 'Pantone 871 C', // Optional reference
64
- cmyk: [0, 0.15, 0.5, 0], // CMYK fallback (0-1 range)
65
- overprint: true, // Recommended for foils — see below
85
+ pantoneCode: 'Pantone 871 C', // optional reference
86
+ cmyk: [0, 0.15, 0.5, 0], // CMYK fallback (01 range)
87
+ overprint: true, // recommended for foils — see below
66
88
  },
67
89
  '#C0C0C0': {
68
90
  name: 'Silver Foil',
@@ -73,44 +95,24 @@ await jsonToPDF(json, './output.pdf', {
73
95
  });
74
96
  ```
75
97
 
76
- **How it works:**
98
+ Any element with a matching fill or stroke color is automatically converted. Color matching is flexible — `'#FFD700'`, `'#ffd700'`, `'rgb(255,215,0)'`, and `'rgba(255,215,0,1)'` all match. CMYK fallback values are used by viewers that don't support spot colors.
77
99
 
78
- - Any element with a matching fill or stroke color is automatically converted to use the spot color
79
- - Colors are matched flexibly — `'#FFD700'`, `'#ffd700'`, and `'rgba(255,215,0,1)'` all match
80
- - Spot colors are preserved as Separation color spaces in both standard and PDF/X-1a output
81
- - CMYK fallback values are used when viewers don't support spot colors
82
- - Supported on text, lines, figures (rect, circle, star, blobs, and other subTypes), and SVG elements
83
-
84
- **Color Format Support:**
85
-
86
- - Hex: `'#FFD700'` or `'#ffd700'`
87
- - RGB: `'rgb(255,215,0)'`
88
- - RGBA: `'rgba(255,215,0,1)'`
100
+ Supported on text, lines, figures, and SVG elements.
89
101
 
90
102
  ### Overprint
91
103
 
92
- `overprint: true` emits the PDF graphics state `/OP true /op true /OPM 1` whenever the spot color is painted. This tells the press to **add** the spot ink on top of any underlying CMYK rather than knocking it out:
93
-
94
- - **Without overprint** (default): the spot region knocks out CMYK underneath. If the foil die is misaligned by even a fraction of a millimetre, you get a visible white halo at the edge.
95
- - **With overprint**: CMYK prints continuously through the spot region; the foil is stamped over it. No halo at edges, and the underlying artwork stays intact.
96
-
97
- Overprint is the standard choice for foil stamps and varnishes. Acrobat's *Output Preview → Simulate Overprinting* shows how the press will composite the layers.
98
-
99
- **Tips:**
104
+ `overprint: true` emits the PDF graphics state `/OP true /op true /OPM 1` whenever the spot color is painted, telling the press to **add** the spot ink on top of any underlying CMYK rather than knocking it out:
100
105
 
101
- - Use professional color references (like Pantone codes) to communicate exact requirements to printers
102
- - Provide accurate CMYK fallback values for preview and proof prints values closer to the real ink (e.g. `[0, 0, 0, 0.4]` for silver) make digital proofs more representative
103
- - Verify spot colors in Adobe Acrobat Pro: **Tools → Print Production → Output Preview → Separations**
106
+ - **Without overprint** (default): the spot region knocks out CMYK underneath. A misaligned foil die produces a visible white halo at the edge.
107
+ - **With overprint**: CMYK prints continuously through the spot region; the foil is stamped over it. No halo, underlying artwork stays intact.
104
108
 
105
- **Caveats:**
109
+ Acrobat's *Output Preview → Simulate Overprinting* shows how the press will composite the layers. Verify spot colors in **Tools → Print Production → Output Preview → Separations**.
106
110
 
107
- - Spot color preservation through PDF/X-1a relies on Ghostscript not flattening transparency in the spot region. Plain figures, lines, text, and simple SVGs all work. SVGs that internally use `<clipPath>` or `<mask>` may have their spot color flattened to CMYK during pdfx1a conversion (a Ghostscript limitation around transparency groups). For foil regions, prefer plain shapes or simple SVG paths over masked artwork.
111
+ For foil regions, prefer plain shapes or simple SVG paths SVGs that internally use `<clipPath>` or `<mask>` may have their spot color flattened to CMYK during PDF/X-1a conversion.
108
112
 
109
- ## Bleed and Crop Marks (print-ready output)
113
+ ## Bleed and crop marks
110
114
 
111
- For print production you usually want extra paper around the trim edge so
112
- that ink covers the cut line, plus crop marks indicating where the print
113
- shop should trim:
115
+ For print production you usually want extra paper around the trim edge so that ink covers the cut line, plus crop marks for the print shop:
114
116
 
115
117
  ```js
116
118
  await jsonToPDF(json, './print-ready.pdf', {
@@ -120,78 +122,40 @@ await jsonToPDF(json, './print-ready.pdf', {
120
122
  });
121
123
  ```
122
124
 
123
- **Source data** — set `bleed` in pixels on each page in the JSON:
125
+ Set `bleed` in pixels on each page in the JSON:
124
126
 
125
127
  ```js
126
- {
127
- width: 1080,
128
- height: 1080,
129
- pages: [{ background: '...', bleed: 36, children: [...] }]
130
- }
128
+ { width: 1080, height: 1080, pages: [{ background: '...', bleed: 36, children: [...] }] }
131
129
  ```
132
130
 
133
- **Page-box metadata.** When `includeBleed` or `cropMarkSize` is set the
134
- generated PDF has correct PDF/X page boxes:
135
-
136
- - `MediaBox` — full sheet (trim + bleed + crop-mark margin)
137
- - `BleedBox` — trim + bleed (where ink may cover)
138
- - `TrimBox` — final cut size
139
- - `ArtBox` — same as `TrimBox`
131
+ Element coordinates stay **relative to the trim corner** `(0, 0)` still lands at the top-left of the trim. Backgrounds automatically extend into the bleed strip.
140
132
 
141
- `TrimBox BleedBox MediaBox` per PDF/X spec, which lets RIPs and proofers
142
- identify the live area and the bleed strip without guessing.
133
+ The output PDF gets correct PDF/X page boxes (`MediaBox` ⊇ `BleedBox` ⊇ `TrimBox` = `ArtBox`) so RIPs and proofers can identify the live area without guessing.
143
134
 
144
- **How content is positioned.** Element coordinates in the JSON stay
145
- **relative to the trim corner** — the renderer translates origin so a
146
- `(0, 0)` element still lands at the top-left of the trim. Backgrounds
147
- automatically extend into the bleed strip; the crop-mark margin remains
148
- unprinted.
135
+ Works on both Node and browser entries no Ghostscript required if you skip `pdfx1a`.
149
136
 
150
- ## DPI Handling
137
+ ## DPI handling
151
138
 
152
- The library automatically handles DPI conversion to ensure correct physical dimensions in the output PDF. By default, it uses the `dpi` value from your JSON file (or 72 DPI if not specified).
139
+ The library converts pixel coordinates to PDF points using the `dpi` value from your JSON (default 72):
153
140
 
154
141
  ```js
155
- // JSON with dpi specified
156
- const json = {
157
- width: 1920,
158
- height: 1080,
159
- dpi: 300, // 300 DPI input
160
- // ... rest of JSON
161
- };
162
-
163
- // Use JSON dpi automatically
142
+ const json = { width: 1920, height: 1080, dpi: 300, /* ... */ };
143
+
144
+ // Uses JSON dpi automatically
164
145
  await jsonToPDF(json, './output.pdf');
165
146
 
166
- // Override DPI via attrs (takes precedence over JSON dpi)
167
- await jsonToPDF(json, './output.pdf', {
168
- dpi: 150, // Override to 150 DPI
169
- });
147
+ // Or override
148
+ await jsonToPDF(json, './output.pdf', { dpi: 150 });
170
149
  ```
171
150
 
172
- **How it works:**
173
-
174
- - Input JSON coordinates are in **pixels** at the specified DPI
175
- - PDF uses **points** (1 point = 1/72 inch)
176
- - The library converts: `points = pixels × (72 / dpi)`
177
- - This ensures the PDF has correct physical dimensions for printing
178
- - All element positions, sizes, and coordinates are automatically scaled
179
-
180
- **Example:**
181
-
182
- - A 1920×1080 pixel canvas at 300 DPI = 6.4" × 3.6" in the PDF
183
- - The same canvas at 72 DPI = 26.67" × 15" in the PDF
184
-
185
- ## Requirements
186
-
187
- - **GhostScript** must be installed for PDF/X-1a conversion
188
- - macOS: `brew install ghostscript`
189
- - Ubuntu: `apt-get install ghostscript`
190
- - Windows: Download from [ghostscript.com](https://www.ghostscript.com/)
151
+ A 1920×1080 canvas at 300 DPI is 6.4″ × 3.6″ in the PDF; at 72 DPI it's 26.67″ × 15″.
191
152
 
192
153
  ## Development
193
154
 
194
155
  ```bash
195
- npm run build # Build the library
196
- npm test # Run tests
156
+ npm install # postinstall applies patches/ to node_modules
157
+ npm run build # builds the Node and browser bundles
158
+ npm test # Node-side fixture tests
159
+ npm run test:browser # real-Chromium smoke suite (Vitest browser mode)
160
+ npm run dev # demo Polotno editor for manual export testing
197
161
  ```
@@ -0,0 +1,102 @@
1
+ interface SpotColorDefinition {
2
+ name?: string;
3
+ cmyk?: number[];
4
+ /**
5
+ * If true, the spot color is painted with overprint enabled
6
+ * (PDF graphics state /OP true /op true /OPM 1). This prevents
7
+ * underlying inks from being knocked out, so the spot ink mixes
8
+ * with the colors beneath it on press. Typically used for
9
+ * varnishes, foils and Pantone overlays.
10
+ */
11
+ overprint?: boolean;
12
+ }
13
+ interface SpotColorConfig {
14
+ [color: string]: SpotColorDefinition;
15
+ }
16
+
17
+ /**
18
+ * One CSS @font-face style variant for a custom font. Mirrors Polotno's
19
+ * `FontStyleVariant` (`node_modules/polotno/model/schema.d.ts`): `src` is
20
+ * the CSS-style source string (e.g. `url("https://example.com/foo.woff2")`),
21
+ * and `fontStyle`/`fontWeight` qualify which (style, weight) the variant
22
+ * is for. Missing means normal/400.
23
+ */
24
+ interface FontStyleVariant {
25
+ src: string;
26
+ fontStyle?: string;
27
+ fontWeight?: string;
28
+ }
29
+ /**
30
+ * Custom font definition. Three valid shapes (matching polotno):
31
+ * 1. `{ fontFamily }` alone → fetched from Google Fonts per variant
32
+ * 2. `{ fontFamily, url }` → one URL applied to all variants (regular
33
+ * file used for bold/italic; not ideal but matches polotno fallback)
34
+ * 3. `{ fontFamily, styles: [...] }` → per-variant URLs (preferred)
35
+ */
36
+ interface FontDefinition {
37
+ fontFamily: string;
38
+ url?: string;
39
+ styles?: FontStyleVariant[];
40
+ }
41
+ interface PolotnoJSON {
42
+ width: number;
43
+ height: number;
44
+ fonts: FontDefinition[];
45
+ pages: Array<{
46
+ background?: string;
47
+ children: any[];
48
+ }>;
49
+ dpi?: number;
50
+ }
51
+ interface RenderAttrs {
52
+ pdfx1a?: boolean;
53
+ validate?: boolean;
54
+ metadata?: {
55
+ title?: string;
56
+ author?: string;
57
+ application?: string;
58
+ producer?: string;
59
+ };
60
+ spotColors?: SpotColorConfig;
61
+ /**
62
+ * @deprecated No-op. Text always shrinks to fit `element.height` and
63
+ * `verticalAlign` is always honored — both are properties of the polotno
64
+ * element model, not render-time options. This field is kept for
65
+ * backwards compatibility with callers that used to pass it.
66
+ */
67
+ textVerticalResizeEnabled?: boolean;
68
+ dpi?: number;
69
+ /**
70
+ * When true, expand each page's PDF dimensions to include the per-page
71
+ * `bleed` value (read from `page.bleed` in the JSON, in pixels). Background
72
+ * and content drawn outside the trim area are preserved into the bleed
73
+ * strip — exactly matching what print shops expect.
74
+ */
75
+ includeBleed?: boolean;
76
+ /**
77
+ * Reserve `cropMarkSize` pixels of additional margin around each page and
78
+ * draw 8 corner crop marks at the trim line (matching polotno's bitmap
79
+ * PDF export). 0 (default) = no crop marks. Independent of `includeBleed`
80
+ * — a print-ready PDF typically wants both.
81
+ */
82
+ cropMarkSize?: number;
83
+ }
84
+ /**
85
+ * Render `json` and return the resulting PDF as raw bytes. Works on both
86
+ * Node and browser (the browser entry re-exports this with the browser
87
+ * platform shim swapped in by the bundler). PDF/X-1a is rejected — that
88
+ * conversion needs Ghostscript and a real file path.
89
+ */
90
+ declare function jsonToPDFBytes(json: PolotnoJSON, attrs?: RenderAttrs): Promise<Uint8Array>;
91
+
92
+ /**
93
+ * Render `json` into a PDF and return it as a `Blob` ready for download
94
+ * (`URL.createObjectURL`), upload, or `navigator.share`.
95
+ *
96
+ * The `Blob`'s MIME type is `application/pdf`. PDF/X-1a (`attrs.pdfx1a`)
97
+ * is unsupported in browsers — pass it and you'll get a clear runtime
98
+ * error pointing you at the Node entry.
99
+ */
100
+ declare function jsonToPDFBlob(json: PolotnoJSON, attrs?: RenderAttrs): Promise<Blob>;
101
+
102
+ export { type FontDefinition, type FontStyleVariant, type PolotnoJSON, type RenderAttrs, type SpotColorConfig, jsonToPDFBlob, jsonToPDFBytes };