@polotno/pdf-export 0.1.37 → 0.1.39
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 +61 -8
- package/lib/index.d.ts +66 -8
- package/lib/index.js +25 -145
- package/package.json +17 -18
- package/lib/browser-entry.d.ts +0 -7
- package/lib/browser-entry.js +0 -11
- package/lib/compare-render.d.ts +0 -1
- package/lib/compare-render.js +0 -185
- package/lib/core/index.d.ts +0 -26
- package/lib/core/index.js +0 -87
- package/lib/figure.d.ts +0 -10
- package/lib/figure.js +0 -54
- package/lib/filters.d.ts +0 -2
- package/lib/filters.js +0 -163
- package/lib/ghostscript.d.ts +0 -21
- package/lib/ghostscript.js +0 -132
- package/lib/group.d.ts +0 -5
- package/lib/group.js +0 -5
- package/lib/image.d.ts +0 -38
- package/lib/image.js +0 -279
- package/lib/line.d.ts +0 -10
- package/lib/line.js +0 -66
- package/lib/platform/adapter.d.ts +0 -37
- package/lib/platform/adapter.js +0 -13
- package/lib/platform/browser-polyfill.d.ts +0 -1
- package/lib/platform/browser-polyfill.js +0 -5
- package/lib/platform/browser.d.ts +0 -7
- package/lib/platform/browser.js +0 -145
- package/lib/platform/node.d.ts +0 -7
- package/lib/platform/node.js +0 -142
- package/lib/spot-colors.d.ts +0 -38
- package/lib/spot-colors.js +0 -141
- package/lib/svg-render.d.ts +0 -9
- package/lib/svg-render.js +0 -63
- package/lib/svg.d.ts +0 -12
- package/lib/svg.js +0 -224
- package/lib/text/fonts.d.ts +0 -16
- package/lib/text/fonts.js +0 -82
- package/lib/text/index.d.ts +0 -8
- package/lib/text/index.js +0 -42
- package/lib/text/layout.d.ts +0 -22
- package/lib/text/layout.js +0 -522
- package/lib/text/parser.d.ts +0 -46
- package/lib/text/parser.js +0 -415
- package/lib/text/render.d.ts +0 -8
- package/lib/text/render.js +0 -237
- package/lib/text/types.d.ts +0 -91
- package/lib/text/types.js +0 -1
- package/lib/text.d.ts +0 -49
- package/lib/text.js +0 -1277
- package/lib/utils.d.ts +0 -16
- package/lib/utils.js +0 -124
package/lib/group.d.ts
DELETED
package/lib/group.js
DELETED
package/lib/image.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { ImageCache } from './utils.js';
|
|
2
|
-
export interface ImageElement {
|
|
3
|
-
src: string;
|
|
4
|
-
width: number;
|
|
5
|
-
height: number;
|
|
6
|
-
cropX: number;
|
|
7
|
-
cropY: number;
|
|
8
|
-
cropWidth: number;
|
|
9
|
-
cropHeight: number;
|
|
10
|
-
clipSrc?: string;
|
|
11
|
-
type?: string;
|
|
12
|
-
opacity?: number;
|
|
13
|
-
flipX?: boolean;
|
|
14
|
-
flipY?: boolean;
|
|
15
|
-
borderSize?: number;
|
|
16
|
-
borderColor?: string;
|
|
17
|
-
brightnessEnabled: boolean;
|
|
18
|
-
brightness: number;
|
|
19
|
-
grayscaleEnabled: boolean;
|
|
20
|
-
sepiaEnabled: boolean;
|
|
21
|
-
blurEnabled: boolean;
|
|
22
|
-
blurRadius: number;
|
|
23
|
-
filters: Record<string, ShapeFilter>;
|
|
24
|
-
shadowEnabled?: boolean;
|
|
25
|
-
shadowBlur?: number;
|
|
26
|
-
shadowOffsetX?: number;
|
|
27
|
-
shadowOffsetY?: number;
|
|
28
|
-
shadowColor?: string;
|
|
29
|
-
shadowOpacity?: number;
|
|
30
|
-
}
|
|
31
|
-
type ShapeFilter = {
|
|
32
|
-
intensity: number;
|
|
33
|
-
};
|
|
34
|
-
export declare function getProcessedImageKey(element: ImageElement): string;
|
|
35
|
-
export declare function cropImage(src: string, element: ImageElement, cache?: ImageCache | null): Promise<string>;
|
|
36
|
-
export declare function clipImage(src: string, element: ImageElement, cache?: ImageCache | null): Promise<string>;
|
|
37
|
-
export declare function renderImage(doc: PDFKit.PDFDocument, element: ImageElement, cache?: ImageCache | null): Promise<void>;
|
|
38
|
-
export {};
|
package/lib/image.js
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
import { loadImage, PIXEL_RATIO, srcToBuffer, parseColor, } from './utils.js';
|
|
2
|
-
import Canvas from 'canvas';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import os from 'os';
|
|
6
|
-
import crypto from 'crypto';
|
|
7
|
-
import Konva from 'konva';
|
|
8
|
-
import { elementFilterToKonva } from './filters.js';
|
|
9
|
-
async function applyFlip(image, element) {
|
|
10
|
-
const { flipX, flipY } = element;
|
|
11
|
-
if (!flipX && !flipY) {
|
|
12
|
-
return image;
|
|
13
|
-
}
|
|
14
|
-
if (!image || !image.width || !image.height) {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
const canvas = new Canvas.Canvas(image.width, image.height);
|
|
18
|
-
const ctx = canvas.getContext('2d');
|
|
19
|
-
let x = flipX ? -canvas.width : 0;
|
|
20
|
-
let y = flipY ? -canvas.height : 0;
|
|
21
|
-
ctx.scale(flipX ? -1 : 1, flipY ? -1 : 1);
|
|
22
|
-
ctx.drawImage(image, x, y, canvas.width, canvas.height);
|
|
23
|
-
return canvas;
|
|
24
|
-
}
|
|
25
|
-
// Helper to create cache key for processed images
|
|
26
|
-
export function getProcessedImageKey(element) {
|
|
27
|
-
return JSON.stringify({
|
|
28
|
-
src: element.src,
|
|
29
|
-
width: element.width,
|
|
30
|
-
height: element.height,
|
|
31
|
-
cropX: element.cropX,
|
|
32
|
-
cropY: element.cropY,
|
|
33
|
-
cropWidth: element.cropWidth,
|
|
34
|
-
cropHeight: element.cropHeight,
|
|
35
|
-
clipSrc: element.clipSrc,
|
|
36
|
-
brightnessEnabled: element.brightnessEnabled,
|
|
37
|
-
brightness: element.brightness,
|
|
38
|
-
grayscaleEnabled: element.grayscaleEnabled,
|
|
39
|
-
sepiaEnabled: element.sepiaEnabled,
|
|
40
|
-
blurEnabled: element.blurEnabled,
|
|
41
|
-
blurRadius: element.blurRadius,
|
|
42
|
-
filters: element.filters,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
export async function cropImage(src, element, cache = null) {
|
|
46
|
-
let image = await loadImage(src);
|
|
47
|
-
// Apply flip transformations first
|
|
48
|
-
image = await applyFlip(image, element);
|
|
49
|
-
if (!image) {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
const canvas = Canvas.createCanvas(element.width * PIXEL_RATIO, element.height * PIXEL_RATIO);
|
|
53
|
-
const ctx = canvas.getContext('2d');
|
|
54
|
-
let { cropX, cropY } = element;
|
|
55
|
-
const availableWidth = image.width * element.cropWidth;
|
|
56
|
-
const availableHeight = image.height * element.cropHeight;
|
|
57
|
-
const aspectRatio = element.width / element.height;
|
|
58
|
-
let cropAbsoluteWidth;
|
|
59
|
-
let cropAbsoluteHeight;
|
|
60
|
-
const imageRatio = availableWidth / availableHeight;
|
|
61
|
-
const allowScale = element.type === 'svg';
|
|
62
|
-
if (allowScale) {
|
|
63
|
-
cropAbsoluteWidth = availableWidth;
|
|
64
|
-
cropAbsoluteHeight = availableHeight;
|
|
65
|
-
}
|
|
66
|
-
else if (aspectRatio >= imageRatio) {
|
|
67
|
-
cropAbsoluteWidth = availableWidth;
|
|
68
|
-
cropAbsoluteHeight = availableWidth / aspectRatio;
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
cropAbsoluteWidth = availableHeight * aspectRatio;
|
|
72
|
-
cropAbsoluteHeight = availableHeight;
|
|
73
|
-
}
|
|
74
|
-
ctx.drawImage(image, cropX * image.width, cropY * image.height, cropAbsoluteWidth, cropAbsoluteHeight, 0, 0, canvas.width, canvas.height);
|
|
75
|
-
return canvas.toDataURL('image/png');
|
|
76
|
-
}
|
|
77
|
-
export async function clipImage(src, element, cache = null) {
|
|
78
|
-
const image = await loadImage(src, cache);
|
|
79
|
-
const clipImage = await loadImage(element.clipSrc, cache);
|
|
80
|
-
const canvas = Canvas.createCanvas(element.width, element.height);
|
|
81
|
-
const ctx = canvas.getContext('2d');
|
|
82
|
-
ctx.drawImage(image, 0, 0, element.width, element.height);
|
|
83
|
-
const clipCanvas = Canvas.createCanvas(element.width, element.height);
|
|
84
|
-
const clipCtx = clipCanvas.getContext('2d');
|
|
85
|
-
clipCtx.drawImage(clipImage, 0, 0, element.width, element.height);
|
|
86
|
-
ctx.globalCompositeOperation = 'destination-in';
|
|
87
|
-
ctx.drawImage(clipCanvas, 0, 0);
|
|
88
|
-
ctx.globalCompositeOperation = 'source-over';
|
|
89
|
-
return canvas.toDataURL('image/png');
|
|
90
|
-
}
|
|
91
|
-
async function applyFilter(src, element, cache = null) {
|
|
92
|
-
const image = await loadImage(src, cache);
|
|
93
|
-
const canvas = Canvas.createCanvas(element.width * PIXEL_RATIO, element.height * PIXEL_RATIO);
|
|
94
|
-
const ctx = canvas.getContext('2d');
|
|
95
|
-
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
96
|
-
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
97
|
-
const data = imageData.data;
|
|
98
|
-
if (element.brightnessEnabled) {
|
|
99
|
-
Konva.Filters.Brighten.bind({
|
|
100
|
-
brightness: () => element.brightness,
|
|
101
|
-
})({ data });
|
|
102
|
-
}
|
|
103
|
-
// grayscale
|
|
104
|
-
if (element.grayscaleEnabled) {
|
|
105
|
-
Konva.Filters.Grayscale.bind({})({ data });
|
|
106
|
-
}
|
|
107
|
-
// sepia
|
|
108
|
-
if (element.sepiaEnabled) {
|
|
109
|
-
Konva.Filters.Sepia.bind({})({ data });
|
|
110
|
-
}
|
|
111
|
-
// blur
|
|
112
|
-
if (element.blurEnabled) {
|
|
113
|
-
Konva.Filters.Blur.bind({
|
|
114
|
-
blurRadius: () => element.blurRadius,
|
|
115
|
-
})({ data });
|
|
116
|
-
}
|
|
117
|
-
// filters
|
|
118
|
-
if (element.filters) {
|
|
119
|
-
Object.entries(element.filters).forEach(([type, effect]) => {
|
|
120
|
-
const filter = elementFilterToKonva[type];
|
|
121
|
-
if (filter) {
|
|
122
|
-
filter(effect.intensity).bind({})({ data });
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
ctx.putImageData(imageData, 0, 0);
|
|
127
|
-
return canvas.toDataURL('image/png');
|
|
128
|
-
}
|
|
129
|
-
function applyBorder(doc, element) {
|
|
130
|
-
if (element.borderSize > 0) {
|
|
131
|
-
const borderColor = parseColor(element.borderColor).keyword || 'black';
|
|
132
|
-
doc
|
|
133
|
-
.rect(element.borderSize / 2, element.borderSize / 2, element.width - element.borderSize, element.height - element.borderSize)
|
|
134
|
-
.lineWidth(element.borderSize)
|
|
135
|
-
.strokeColor(borderColor)
|
|
136
|
-
.stroke();
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
function saveToTempFile(buffer, key, cache) {
|
|
140
|
-
if (cache) {
|
|
141
|
-
if (!cache.imageFiles) {
|
|
142
|
-
cache.imageFiles = new Map();
|
|
143
|
-
}
|
|
144
|
-
if (!cache.tempDir) {
|
|
145
|
-
cache.tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pdfkit-images-'));
|
|
146
|
-
}
|
|
147
|
-
// Create a unique filename based on cache key hash
|
|
148
|
-
const hash = crypto.createHash('md5').update(key).digest('hex');
|
|
149
|
-
const filePath = path.join(cache.tempDir, `${hash}.png`);
|
|
150
|
-
// Write buffer to file
|
|
151
|
-
fs.writeFileSync(filePath, buffer);
|
|
152
|
-
cache.imageFiles.set(key, filePath);
|
|
153
|
-
return filePath;
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
return buffer;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
async function getShadowImage(src, element, cache) {
|
|
160
|
-
const image = await loadImage(src, cache);
|
|
161
|
-
const { shadowBlur, shadowColor, shadowOpacity } = element;
|
|
162
|
-
// Shadow blur in Konva is standard. In Canvas it is standard.
|
|
163
|
-
// We need to scale it by PIXEL_RATIO as our canvas is scaled.
|
|
164
|
-
const ratio = PIXEL_RATIO;
|
|
165
|
-
const blur = (shadowBlur || 0) * ratio;
|
|
166
|
-
// Padding.
|
|
167
|
-
const padding = blur * 4 + 20; // Sufficient padding
|
|
168
|
-
const width = image.width + padding * 2;
|
|
169
|
-
const height = image.height + padding * 2;
|
|
170
|
-
const canvas = Canvas.createCanvas(width, height);
|
|
171
|
-
const ctx = canvas.getContext('2d');
|
|
172
|
-
// Parse color
|
|
173
|
-
const parsed = parseColor(shadowColor || 'black');
|
|
174
|
-
const opacity = shadowOpacity !== undefined ? shadowOpacity : 1;
|
|
175
|
-
const r = parsed.rgb[0];
|
|
176
|
-
const g = parsed.rgb[1];
|
|
177
|
-
const b = parsed.rgb[2];
|
|
178
|
-
const a = opacity;
|
|
179
|
-
const colorString = `rgba(${r},${g},${b},${a})`;
|
|
180
|
-
ctx.shadowColor = colorString;
|
|
181
|
-
ctx.shadowBlur = blur;
|
|
182
|
-
// We want the shadow to appear at (padding, padding) relative to canvas.
|
|
183
|
-
// We draw the image at (padding - OFFSET, padding - OFFSET)
|
|
184
|
-
// And set shadowOffset to (OFFSET, OFFSET).
|
|
185
|
-
// OFFSET should be large enough to move image out of view.
|
|
186
|
-
const OFFSET = 10000;
|
|
187
|
-
ctx.shadowOffsetX = OFFSET;
|
|
188
|
-
ctx.shadowOffsetY = OFFSET;
|
|
189
|
-
// Draw image
|
|
190
|
-
ctx.drawImage(image, padding - OFFSET, padding - OFFSET, image.width, image.height);
|
|
191
|
-
return {
|
|
192
|
-
src: canvas.toDataURL('image/png'),
|
|
193
|
-
padding: padding / ratio, // return padding in original units (points)
|
|
194
|
-
width: width / ratio,
|
|
195
|
-
height: height / ratio,
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
export async function renderImage(doc, element, cache = null) {
|
|
199
|
-
// Check if we have a cached processed version
|
|
200
|
-
const cacheKey = getProcessedImageKey(element);
|
|
201
|
-
let src = null;
|
|
202
|
-
const hasCachedFile = cache && cache.imageFiles && cache.imageFiles.has(cacheKey);
|
|
203
|
-
if (hasCachedFile) {
|
|
204
|
-
src = cache.imageFiles.get(cacheKey);
|
|
205
|
-
}
|
|
206
|
-
else if (cache && cache.processedImages.has(cacheKey)) {
|
|
207
|
-
src = cache.processedImages.get(cacheKey);
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
src = await cropImage(element.src, element, cache);
|
|
211
|
-
if (element.clipSrc) {
|
|
212
|
-
src = await clipImage(src, element, cache);
|
|
213
|
-
}
|
|
214
|
-
src = await applyFilter(src, element, cache);
|
|
215
|
-
// Cache the processed result
|
|
216
|
-
if (cache && src) {
|
|
217
|
-
cache.processedImages.set(cacheKey, src);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
if (element.shadowEnabled && src) {
|
|
221
|
-
const shadowKey = cacheKey +
|
|
222
|
-
'_shadow_' +
|
|
223
|
-
JSON.stringify({
|
|
224
|
-
blur: element.shadowBlur,
|
|
225
|
-
color: element.shadowColor,
|
|
226
|
-
opacity: element.shadowOpacity,
|
|
227
|
-
});
|
|
228
|
-
let shadowPadding = 0;
|
|
229
|
-
let shadowW = 0;
|
|
230
|
-
let shadowH = 0;
|
|
231
|
-
if (cache && cache.imageFiles && cache.imageFiles.has(shadowKey)) {
|
|
232
|
-
const filePath = cache.imageFiles.get(shadowKey);
|
|
233
|
-
// Recalculate padding/dimensions as we don't cache them
|
|
234
|
-
const ratio = PIXEL_RATIO;
|
|
235
|
-
const blur = (element.shadowBlur || 0) * ratio;
|
|
236
|
-
shadowPadding = (blur * 4 + 20) / ratio;
|
|
237
|
-
shadowW = element.width + shadowPadding * 2;
|
|
238
|
-
shadowH = element.height + shadowPadding * 2;
|
|
239
|
-
console.log('✓ Using cached shadow file:', path.basename(filePath));
|
|
240
|
-
doc.image(filePath, (element.shadowOffsetX || 0) - shadowPadding, (element.shadowOffsetY || 0) - shadowPadding, {
|
|
241
|
-
width: shadowW,
|
|
242
|
-
height: shadowH,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
const shadowResult = await getShadowImage(src, element, cache);
|
|
247
|
-
const shadowSrc = shadowResult.src;
|
|
248
|
-
shadowPadding = shadowResult.padding;
|
|
249
|
-
shadowW = shadowResult.width;
|
|
250
|
-
shadowH = shadowResult.height;
|
|
251
|
-
if (shadowSrc) {
|
|
252
|
-
const buffer = await srcToBuffer(shadowSrc, cache);
|
|
253
|
-
const filePath = saveToTempFile(buffer, shadowKey, cache);
|
|
254
|
-
doc.image(filePath, (element.shadowOffsetX || 0) - shadowPadding, (element.shadowOffsetY || 0) - shadowPadding, {
|
|
255
|
-
width: shadowW,
|
|
256
|
-
height: shadowH,
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
if (src) {
|
|
262
|
-
if (hasCachedFile) {
|
|
263
|
-
console.log('✓ Using cached image file:', path.basename(src));
|
|
264
|
-
doc.image(src, 0, 0, {
|
|
265
|
-
width: element.width,
|
|
266
|
-
height: element.height,
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
else {
|
|
270
|
-
const buffer = await srcToBuffer(src, cache);
|
|
271
|
-
const filePath = saveToTempFile(buffer, cacheKey, cache);
|
|
272
|
-
doc.image(filePath, 0, 0, {
|
|
273
|
-
width: element.width,
|
|
274
|
-
height: element.height,
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
applyBorder(doc, element);
|
|
278
|
-
}
|
|
279
|
-
}
|
package/lib/line.d.ts
DELETED
package/lib/line.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { Util } from 'konva/lib/Util.js';
|
|
2
|
-
function rgbToHex({ r, g, b }) {
|
|
3
|
-
// Ensure each value is within the valid range
|
|
4
|
-
r = Math.max(0, Math.min(255, r));
|
|
5
|
-
g = Math.max(0, Math.min(255, g));
|
|
6
|
-
b = Math.max(0, Math.min(255, b));
|
|
7
|
-
// Convert each value to a 2-digit hexadecimal string
|
|
8
|
-
const hexR = r.toString(16).padStart(2, '0');
|
|
9
|
-
const hexG = g.toString(16).padStart(2, '0');
|
|
10
|
-
const hexB = b.toString(16).padStart(2, '0');
|
|
11
|
-
// Return the concatenated hex string
|
|
12
|
-
return `#${hexR}${hexG}${hexB}`;
|
|
13
|
-
}
|
|
14
|
-
function getLineHead({ element, type, doc }) {
|
|
15
|
-
doc.lineWidth(element.height);
|
|
16
|
-
doc.lineCap('round');
|
|
17
|
-
doc.lineJoin('round');
|
|
18
|
-
doc.opacity(element.opacity);
|
|
19
|
-
const rgba = Util.colorToRGBA(element.color);
|
|
20
|
-
const fillColor = rgbToHex(rgba);
|
|
21
|
-
if (type === 'arrow') {
|
|
22
|
-
doc
|
|
23
|
-
.moveTo(element.height * 3, -element.height * 2)
|
|
24
|
-
.lineTo(0, 0)
|
|
25
|
-
.lineTo(element.height * 3, element.height * 2);
|
|
26
|
-
doc.stroke();
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
else if (type === 'triangle') {
|
|
30
|
-
doc.polygon([element.height * 3, -element.height * 2], [0, 0], [element.height * 3, element.height * 2]);
|
|
31
|
-
}
|
|
32
|
-
else if (type === 'bar') {
|
|
33
|
-
doc.polygon([0, -element.height * 2], [0, 0], [0, element.height * 2]);
|
|
34
|
-
}
|
|
35
|
-
else if (type === 'circle') {
|
|
36
|
-
doc.circle(element.height * 2, 0, element.height * 2);
|
|
37
|
-
}
|
|
38
|
-
else if (type === 'square') {
|
|
39
|
-
doc.rect(0, -element.height * 2, element.height * 4, element.height * 4);
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
doc.fillAndStroke(fillColor, fillColor);
|
|
45
|
-
}
|
|
46
|
-
export function lineToPDF(doc, element) {
|
|
47
|
-
doc.translate(0, element.height / 2);
|
|
48
|
-
doc.lineWidth(element.height);
|
|
49
|
-
doc.moveTo(0, 0);
|
|
50
|
-
doc.lineTo(element.width, 0);
|
|
51
|
-
if (element.dash && element.dash.length > 0) {
|
|
52
|
-
doc.dash(element.dash.map((dash) => dash * element.height));
|
|
53
|
-
}
|
|
54
|
-
const rgba = Util.colorToRGBA(element.color);
|
|
55
|
-
doc.strokeColor(rgbToHex(rgba));
|
|
56
|
-
doc.stroke();
|
|
57
|
-
if (element.dash && element.dash.length > 0) {
|
|
58
|
-
doc.undash();
|
|
59
|
-
}
|
|
60
|
-
getLineHead({ element, doc: doc, type: element.startHead });
|
|
61
|
-
getLineHead({
|
|
62
|
-
element,
|
|
63
|
-
doc: doc.translate(element.width, 0).rotate(180),
|
|
64
|
-
type: element.endHead,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export interface FetchResponse {
|
|
2
|
-
ok: boolean;
|
|
3
|
-
status: number;
|
|
4
|
-
statusText: string;
|
|
5
|
-
headers: Headers;
|
|
6
|
-
arrayBuffer(): Promise<ArrayBuffer>;
|
|
7
|
-
text(): Promise<string>;
|
|
8
|
-
json(): Promise<any>;
|
|
9
|
-
}
|
|
10
|
-
export interface CanvasLike {
|
|
11
|
-
width: number;
|
|
12
|
-
height: number;
|
|
13
|
-
getContext(type: '2d'): any;
|
|
14
|
-
toDataURL(type?: string, quality?: any): string;
|
|
15
|
-
}
|
|
16
|
-
export interface ImageLike {
|
|
17
|
-
width: number;
|
|
18
|
-
height: number;
|
|
19
|
-
[key: string]: any;
|
|
20
|
-
}
|
|
21
|
-
export interface PDFContext<TOutput = unknown, TFinalizeOptions = unknown> {
|
|
22
|
-
doc: any;
|
|
23
|
-
finalize(options?: TFinalizeOptions): Promise<TOutput>;
|
|
24
|
-
}
|
|
25
|
-
export interface PlatformAdapter<TOutput = unknown, TFinalizeOptions = unknown> {
|
|
26
|
-
fetch(input: string, init?: RequestInit): Promise<FetchResponse>;
|
|
27
|
-
loadImage(src: string): Promise<ImageLike>;
|
|
28
|
-
createCanvas(width: number, height: number): CanvasLike;
|
|
29
|
-
createPDFContext(options: any): PDFContext<TOutput, TFinalizeOptions>;
|
|
30
|
-
registerFont(doc: any, name: string, data: Uint8Array): void | Promise<void>;
|
|
31
|
-
encodeBase64(data: Uint8Array): string;
|
|
32
|
-
decodeBase64(value: string): Uint8Array;
|
|
33
|
-
embedImage(doc: any, data: Uint8Array | string, x: number, y: number, options: any, mimeType?: string): void | Promise<void>;
|
|
34
|
-
}
|
|
35
|
-
export declare function setPlatformAdapter(adapter: PlatformAdapter): void;
|
|
36
|
-
export declare function hasPlatformAdapter(): boolean;
|
|
37
|
-
export declare function getPlatformAdapter(): PlatformAdapter;
|
package/lib/platform/adapter.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
let currentAdapter = null;
|
|
2
|
-
export function setPlatformAdapter(adapter) {
|
|
3
|
-
currentAdapter = adapter;
|
|
4
|
-
}
|
|
5
|
-
export function hasPlatformAdapter() {
|
|
6
|
-
return currentAdapter !== null;
|
|
7
|
-
}
|
|
8
|
-
export function getPlatformAdapter() {
|
|
9
|
-
if (!currentAdapter) {
|
|
10
|
-
throw new Error('Platform adapter is not configured.');
|
|
11
|
-
}
|
|
12
|
-
return currentAdapter;
|
|
13
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/lib/platform/browser.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import PDFDocument from 'pdfkit/js/pdfkit.standalone.js';
|
|
2
|
-
import SVGtoPDF from 'svg-to-pdfkit';
|
|
3
|
-
import { Buffer } from 'buffer';
|
|
4
|
-
function attachAddSVG(doc) {
|
|
5
|
-
const existing = doc.addSVG;
|
|
6
|
-
if (!existing) {
|
|
7
|
-
doc.addSVG = function (svg, x, y, options) {
|
|
8
|
-
SVGtoPDF(this, svg, x, y, options);
|
|
9
|
-
return this;
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
function arrayBufferToBase64(buffer) {
|
|
14
|
-
let binary = '';
|
|
15
|
-
const chunkSize = 8192;
|
|
16
|
-
for (let i = 0; i < buffer.length; i += chunkSize) {
|
|
17
|
-
const chunk = buffer.subarray(i, i + chunkSize);
|
|
18
|
-
binary += String.fromCharCode(...chunk);
|
|
19
|
-
}
|
|
20
|
-
return btoa(binary);
|
|
21
|
-
}
|
|
22
|
-
function base64ToUint8Array(value) {
|
|
23
|
-
const binary = atob(value);
|
|
24
|
-
const bytes = new Uint8Array(binary.length);
|
|
25
|
-
for (let i = 0; i < binary.length; i++) {
|
|
26
|
-
bytes[i] = binary.charCodeAt(i);
|
|
27
|
-
}
|
|
28
|
-
return bytes;
|
|
29
|
-
}
|
|
30
|
-
function loadImageElement(src) {
|
|
31
|
-
return new Promise((resolve, reject) => {
|
|
32
|
-
const img = new Image();
|
|
33
|
-
if (!src.startsWith('data:')) {
|
|
34
|
-
img.crossOrigin = 'anonymous';
|
|
35
|
-
}
|
|
36
|
-
img.onload = () => resolve(img);
|
|
37
|
-
img.onerror = (error) => reject(error);
|
|
38
|
-
img.src = src;
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
async function loadImageFromSource(src) {
|
|
42
|
-
if (src.startsWith('data:')) {
|
|
43
|
-
return loadImageElement(src);
|
|
44
|
-
}
|
|
45
|
-
const fetchImpl = globalThis.fetch?.bind(globalThis);
|
|
46
|
-
if (!fetchImpl) {
|
|
47
|
-
throw new Error('Global fetch is not available in this environment.');
|
|
48
|
-
}
|
|
49
|
-
const response = await fetchImpl(src, { mode: 'cors' });
|
|
50
|
-
if (!response.ok) {
|
|
51
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
52
|
-
}
|
|
53
|
-
const blob = await response.blob();
|
|
54
|
-
const objectUrl = URL.createObjectURL(blob);
|
|
55
|
-
try {
|
|
56
|
-
return await loadImageElement(objectUrl);
|
|
57
|
-
}
|
|
58
|
-
finally {
|
|
59
|
-
URL.revokeObjectURL(objectUrl);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
export function createBrowserAdapter() {
|
|
63
|
-
return {
|
|
64
|
-
async fetch(input, init) {
|
|
65
|
-
const fetchImpl = globalThis.fetch?.bind(globalThis);
|
|
66
|
-
if (!fetchImpl) {
|
|
67
|
-
throw new Error('Global fetch is not available in this environment.');
|
|
68
|
-
}
|
|
69
|
-
return (await fetchImpl(input, init));
|
|
70
|
-
},
|
|
71
|
-
async loadImage(src) {
|
|
72
|
-
return loadImageFromSource(src);
|
|
73
|
-
},
|
|
74
|
-
createCanvas(width, height) {
|
|
75
|
-
const canvas = document.createElement('canvas');
|
|
76
|
-
canvas.width = width;
|
|
77
|
-
canvas.height = height;
|
|
78
|
-
return canvas;
|
|
79
|
-
},
|
|
80
|
-
createPDFContext(options) {
|
|
81
|
-
const doc = new PDFDocument(options);
|
|
82
|
-
attachAddSVG(doc);
|
|
83
|
-
const blobParts = [];
|
|
84
|
-
const appendTypedArray = (view) => {
|
|
85
|
-
blobParts.push(view.slice());
|
|
86
|
-
};
|
|
87
|
-
doc.on('data', (chunk) => {
|
|
88
|
-
if (chunk instanceof Uint8Array) {
|
|
89
|
-
appendTypedArray(chunk);
|
|
90
|
-
}
|
|
91
|
-
else if (Array.isArray(chunk)) {
|
|
92
|
-
appendTypedArray(new Uint8Array(chunk));
|
|
93
|
-
}
|
|
94
|
-
else if (typeof chunk === 'string') {
|
|
95
|
-
const encoder = new TextEncoder();
|
|
96
|
-
appendTypedArray(encoder.encode(chunk));
|
|
97
|
-
}
|
|
98
|
-
else if (chunk?.buffer instanceof ArrayBuffer) {
|
|
99
|
-
appendTypedArray(new Uint8Array(chunk.buffer));
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
const resultPromise = new Promise((resolve, reject) => {
|
|
103
|
-
doc.on('end', () => {
|
|
104
|
-
try {
|
|
105
|
-
resolve(new Blob(blobParts, { type: 'application/pdf' }));
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
reject(error);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
doc.on('error', reject);
|
|
112
|
-
});
|
|
113
|
-
const finalize = async (options) => {
|
|
114
|
-
if (options?.attrs?.pdfx1a) {
|
|
115
|
-
console.warn('PDF/X-1a conversion is not supported in the browser runtime.');
|
|
116
|
-
}
|
|
117
|
-
return resultPromise;
|
|
118
|
-
};
|
|
119
|
-
return {
|
|
120
|
-
doc,
|
|
121
|
-
finalize,
|
|
122
|
-
};
|
|
123
|
-
},
|
|
124
|
-
registerFont(doc, name, data) {
|
|
125
|
-
const buffer = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
126
|
-
doc.registerFont(name, buffer);
|
|
127
|
-
},
|
|
128
|
-
encodeBase64(data) {
|
|
129
|
-
return arrayBufferToBase64(data);
|
|
130
|
-
},
|
|
131
|
-
decodeBase64(value) {
|
|
132
|
-
return base64ToUint8Array(value);
|
|
133
|
-
},
|
|
134
|
-
embedImage(doc, data, x, y, options, mimeType) {
|
|
135
|
-
if (typeof data === 'string') {
|
|
136
|
-
doc.image(data, x, y, options);
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
const mime = mimeType || 'image/png';
|
|
140
|
-
const base64 = arrayBufferToBase64(data);
|
|
141
|
-
const dataUrl = `data:${mime};base64,${base64}`;
|
|
142
|
-
doc.image(dataUrl, x, y, options);
|
|
143
|
-
},
|
|
144
|
-
};
|
|
145
|
-
}
|