@polotno/pdf-import 0.0.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.
@@ -0,0 +1,181 @@
1
+ // Encode pdfjs-dist image data to base64 data URIs
2
+ // pdfjs gives us raw pixel data (Uint8ClampedArray) with width/height/kind
3
+ function uint8ArrayToBase64(bytes) {
4
+ if (typeof Buffer !== 'undefined') {
5
+ return Buffer.from(bytes).toString('base64');
6
+ }
7
+ // Browser fallback
8
+ let binary = '';
9
+ for (let i = 0; i < bytes.length; i++) {
10
+ binary += String.fromCharCode(bytes[i]);
11
+ }
12
+ return btoa(binary);
13
+ }
14
+ export function imageDataToDataUri(data, width, height, kind) {
15
+ // kind: 1 = grayscale, 2 = RGB, 3 = RGBA
16
+ // Convert to RGBA if needed, then encode as PNG
17
+ let rgba;
18
+ if (kind === 3) {
19
+ // Already RGBA
20
+ rgba = new Uint8Array(data);
21
+ }
22
+ else if (kind === 2) {
23
+ // RGB -> RGBA
24
+ rgba = new Uint8Array(width * height * 4);
25
+ for (let i = 0; i < width * height; i++) {
26
+ rgba[i * 4] = data[i * 3];
27
+ rgba[i * 4 + 1] = data[i * 3 + 1];
28
+ rgba[i * 4 + 2] = data[i * 3 + 2];
29
+ rgba[i * 4 + 3] = 255;
30
+ }
31
+ }
32
+ else if (kind === 1) {
33
+ // Grayscale -> RGBA
34
+ rgba = new Uint8Array(width * height * 4);
35
+ for (let i = 0; i < width * height; i++) {
36
+ rgba[i * 4] = data[i];
37
+ rgba[i * 4 + 1] = data[i];
38
+ rgba[i * 4 + 2] = data[i];
39
+ rgba[i * 4 + 3] = 255;
40
+ }
41
+ }
42
+ else {
43
+ // Unknown kind, try as RGBA
44
+ rgba = new Uint8Array(data);
45
+ }
46
+ const png = encodePng(rgba, width, height);
47
+ const b64 = uint8ArrayToBase64(png);
48
+ return `data:image/png;base64,${b64}`;
49
+ }
50
+ // Minimal PNG encoder (uncompressed)
51
+ function encodePng(rgba, width, height) {
52
+ // PNG signature
53
+ const signature = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
54
+ // IHDR chunk
55
+ const ihdr = new Uint8Array(13);
56
+ const ihdrView = new DataView(ihdr.buffer);
57
+ ihdrView.setUint32(0, width);
58
+ ihdrView.setUint32(4, height);
59
+ ihdr[8] = 8; // bit depth
60
+ ihdr[9] = 6; // color type: RGBA
61
+ ihdr[10] = 0; // compression
62
+ ihdr[11] = 0; // filter
63
+ ihdr[12] = 0; // interlace
64
+ const ihdrChunk = makeChunk('IHDR', ihdr);
65
+ // IDAT: we use zlib stored blocks (no compression)
66
+ // Each row: filter byte (0) + RGBA pixels
67
+ const rowSize = 1 + width * 4;
68
+ const rawData = new Uint8Array(height * rowSize);
69
+ for (let y = 0; y < height; y++) {
70
+ rawData[y * rowSize] = 0; // filter: none
71
+ rawData.set(rgba.subarray(y * width * 4, (y + 1) * width * 4), y * rowSize + 1);
72
+ }
73
+ // Wrap in zlib format (stored blocks)
74
+ const zlibData = deflateStored(rawData);
75
+ const idatChunk = makeChunk('IDAT', zlibData);
76
+ // IEND
77
+ const iendChunk = makeChunk('IEND', new Uint8Array(0));
78
+ // Concatenate
79
+ const total = signature.length +
80
+ ihdrChunk.length +
81
+ idatChunk.length +
82
+ iendChunk.length;
83
+ const result = new Uint8Array(total);
84
+ let offset = 0;
85
+ result.set(signature, offset);
86
+ offset += signature.length;
87
+ result.set(ihdrChunk, offset);
88
+ offset += ihdrChunk.length;
89
+ result.set(idatChunk, offset);
90
+ offset += idatChunk.length;
91
+ result.set(iendChunk, offset);
92
+ return result;
93
+ }
94
+ function makeChunk(type, data) {
95
+ const chunk = new Uint8Array(4 + 4 + data.length + 4);
96
+ const view = new DataView(chunk.buffer);
97
+ // Length
98
+ view.setUint32(0, data.length);
99
+ // Type
100
+ for (let i = 0; i < 4; i++) {
101
+ chunk[4 + i] = type.charCodeAt(i);
102
+ }
103
+ // Data
104
+ chunk.set(data, 8);
105
+ // CRC
106
+ const crcData = chunk.subarray(4, 8 + data.length);
107
+ view.setUint32(8 + data.length, crc32(crcData));
108
+ return chunk;
109
+ }
110
+ function deflateStored(data) {
111
+ // zlib header (no compression) + stored deflate blocks + adler32
112
+ const maxBlockSize = 65535;
113
+ const numBlocks = Math.ceil(data.length / maxBlockSize) || 1;
114
+ // Calculate total size: zlib header (2) + blocks + adler32 (4)
115
+ let totalSize = 2 + 4; // header + adler32
116
+ for (let i = 0; i < numBlocks; i++) {
117
+ const blockStart = i * maxBlockSize;
118
+ const blockLen = Math.min(maxBlockSize, data.length - blockStart);
119
+ totalSize += 5 + blockLen; // 5 byte block header + data
120
+ }
121
+ const result = new Uint8Array(totalSize);
122
+ let offset = 0;
123
+ // zlib header: CMF=0x78 (deflate, window=32768), FLG=0x01 (no dict, check)
124
+ result[offset++] = 0x78;
125
+ result[offset++] = 0x01;
126
+ // Stored blocks
127
+ for (let i = 0; i < numBlocks; i++) {
128
+ const blockStart = i * maxBlockSize;
129
+ const blockLen = Math.min(maxBlockSize, data.length - blockStart);
130
+ const isLast = i === numBlocks - 1;
131
+ result[offset++] = isLast ? 1 : 0; // BFINAL
132
+ result[offset++] = blockLen & 0xff;
133
+ result[offset++] = (blockLen >> 8) & 0xff;
134
+ result[offset++] = ~blockLen & 0xff;
135
+ result[offset++] = (~blockLen >> 8) & 0xff;
136
+ result.set(data.subarray(blockStart, blockStart + blockLen), offset);
137
+ offset += blockLen;
138
+ }
139
+ // Adler32
140
+ const adler = adler32(data);
141
+ result[offset++] = (adler >> 24) & 0xff;
142
+ result[offset++] = (adler >> 16) & 0xff;
143
+ result[offset++] = (adler >> 8) & 0xff;
144
+ result[offset++] = adler & 0xff;
145
+ return result;
146
+ }
147
+ function adler32(data) {
148
+ let a = 1, b = 0;
149
+ for (let i = 0; i < data.length; i++) {
150
+ a = (a + data[i]) % 65521;
151
+ b = (b + a) % 65521;
152
+ }
153
+ return ((b << 16) | a) >>> 0;
154
+ }
155
+ // CRC32 lookup table
156
+ const crcTable = [];
157
+ for (let n = 0; n < 256; n++) {
158
+ let c = n;
159
+ for (let k = 0; k < 8; k++) {
160
+ if (c & 1) {
161
+ c = 0xedb88320 ^ (c >>> 1);
162
+ }
163
+ else {
164
+ c = c >>> 1;
165
+ }
166
+ }
167
+ crcTable[n] = c;
168
+ }
169
+ function crc32(data) {
170
+ let crc = 0xffffffff;
171
+ for (let i = 0; i < data.length; i++) {
172
+ crc = crcTable[(crc ^ data[i]) & 0xff] ^ (crc >>> 8);
173
+ }
174
+ return (crc ^ 0xffffffff) >>> 0;
175
+ }
176
+ // For already-encoded images from pdfjs (JPEG etc)
177
+ export function imageBytesToDataUri(data, mimeType) {
178
+ const b64 = uint8ArrayToBase64(data);
179
+ return `data:${mimeType};base64,${b64}`;
180
+ }
181
+ //# sourceMappingURL=image-encoder.js.map
package/lib/index.d.ts ADDED
@@ -0,0 +1,97 @@
1
+ export interface PolotnoTextElement {
2
+ type: 'text';
3
+ id: string;
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ rotation: number;
9
+ opacity: number;
10
+ visible: boolean;
11
+ selectable: boolean;
12
+ removable: boolean;
13
+ text: string;
14
+ fontSize: number;
15
+ fontFamily: string;
16
+ fontWeight: string;
17
+ fontStyle: string;
18
+ fill: string;
19
+ align: string;
20
+ lineHeight: number;
21
+ letterSpacing: number;
22
+ name: string;
23
+ placeholder: string;
24
+ }
25
+ export interface PolotnoImageElement {
26
+ type: 'image';
27
+ id: string;
28
+ x: number;
29
+ y: number;
30
+ width: number;
31
+ height: number;
32
+ rotation: number;
33
+ opacity: number;
34
+ visible: boolean;
35
+ selectable: boolean;
36
+ removable: boolean;
37
+ src: string;
38
+ cropX: number;
39
+ cropY: number;
40
+ cropWidth: number;
41
+ cropHeight: number;
42
+ clipSrc: string;
43
+ name: string;
44
+ }
45
+ export interface PolotnoSvgElement {
46
+ type: 'svg';
47
+ id: string;
48
+ x: number;
49
+ y: number;
50
+ width: number;
51
+ height: number;
52
+ rotation: number;
53
+ opacity: number;
54
+ src: string;
55
+ clipSrc?: string;
56
+ name: string;
57
+ }
58
+ export interface PolotnoLineElement {
59
+ type: 'line';
60
+ id: string;
61
+ x: number;
62
+ y: number;
63
+ width: number;
64
+ height: number;
65
+ color: string;
66
+ rotation: number;
67
+ dash?: number[];
68
+ opacity?: number;
69
+ name?: string;
70
+ }
71
+ export type PolotnoElement = PolotnoTextElement | PolotnoImageElement | PolotnoSvgElement | PolotnoLineElement;
72
+ export interface PolotnoFont {
73
+ fontFamily: string;
74
+ url?: string | null;
75
+ styles?: Record<string, unknown>[] | null;
76
+ }
77
+ export interface PolotnoPage {
78
+ id: string;
79
+ children: PolotnoElement[];
80
+ width?: number | null;
81
+ height?: number | null;
82
+ background: string;
83
+ }
84
+ export interface PolotnoJson {
85
+ width: number;
86
+ height: number;
87
+ fonts: PolotnoFont[];
88
+ pages: PolotnoPage[];
89
+ unit: string;
90
+ dpi: number;
91
+ }
92
+ export type FontStrategy = 'embed' | 'googleFontsMatch';
93
+ export declare function pdfToJson({ pdf, fontStrategy, }: {
94
+ pdf: ArrayBuffer | Uint8Array;
95
+ fontStrategy?: FontStrategy;
96
+ }): Promise<PolotnoJson>;
97
+ //# sourceMappingURL=index.d.ts.map
package/lib/index.js ADDED
@@ -0,0 +1 @@
1
+ import{getDocument as x,GlobalWorkerOptions as m}from"pdfjs-dist/legacy/build/pdf.mjs";import{parsePage as I}from"./page-parser.js";import{FontRegistry as R}from"./font-registry.js";import{buildJpegIndex as S}from"./pdf-image-extractor.js";let k=0;function A(){return`el_${Date.now()}_${++k}`}async function j({pdf:e,fontStrategy:w="embed"}){typeof window<"u"&&!m.workerSrc&&(m.workerSrc=new URL("pdfjs-dist/legacy/build/pdf.worker.mjs",import.meta.url).toString());const s=new Uint8Array(e instanceof ArrayBuffer?e:e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength)),l=S(s),r=await x({data:s,useSystemFonts:!0,disableFontFace:!0,fontExtraProperties:!0}).promise,i=new R;let c=612,g=792;const f=await r.getPage(1),u=f.getViewport({scale:1});c=u.width,g=u.height;const d=3,n=new Array(r.numPages);for(let o=0;o<r.numPages;o+=d){const y=Math.min(o+d,r.numPages),p=[];for(let t=o;t<y;t++)p.push((async()=>{const a=t===0?f:await r.getPage(t+1),{parsedPage:P}=await I({page:a,pageIdx:t,fontRegistry:i,generateId:A,jpegIndex:l});return{parsedPage:P,pageIdx:t}})());const b=await Promise.all(p);for(const{parsedPage:t,pageIdx:a}of b)n[a]=t}await r.destroy();const h=i.finalize(w,n);return{width:c,height:g,fonts:h,pages:n,unit:"px",dpi:72}}export{j as pdfToJson};
@@ -0,0 +1,6 @@
1
+ export type Matrix = [number, number, number, number, number, number];
2
+ export declare function argsToMatrix(args: any): Matrix;
3
+ export declare function multiplyMatrices(m1: Matrix, m2: Matrix): Matrix;
4
+ export declare function isTextShowOp(fn: number): boolean;
5
+ export declare function isVisualOrderOp(fn: number): boolean;
6
+ //# sourceMappingURL=operator-list-helpers.d.ts.map
@@ -0,0 +1,26 @@
1
+ import { OPS } from 'pdfjs-dist/legacy/build/pdf.mjs';
2
+ export function argsToMatrix(args) {
3
+ return [args[0], args[1], args[2], args[3], args[4], args[5]];
4
+ }
5
+ export function multiplyMatrices(m1, m2) {
6
+ return [
7
+ m1[0] * m2[0] + m1[1] * m2[2],
8
+ m1[0] * m2[1] + m1[1] * m2[3],
9
+ m1[2] * m2[0] + m1[3] * m2[2],
10
+ m1[2] * m2[1] + m1[3] * m2[3],
11
+ m1[4] * m2[0] + m1[5] * m2[2] + m2[4],
12
+ m1[4] * m2[1] + m1[5] * m2[3] + m2[5],
13
+ ];
14
+ }
15
+ export function isTextShowOp(fn) {
16
+ return fn === OPS.showText || fn === OPS.showSpacedText;
17
+ }
18
+ export function isVisualOrderOp(fn) {
19
+ return (fn === OPS.fill ||
20
+ fn === OPS.stroke ||
21
+ fn === OPS.fillStroke ||
22
+ fn === OPS.eoFill ||
23
+ fn === OPS.eoFillStroke ||
24
+ fn === OPS.paintImageXObject);
25
+ }
26
+ //# sourceMappingURL=operator-list-helpers.js.map
@@ -0,0 +1,99 @@
1
+ export interface TextColorEntry {
2
+ fontName: string;
3
+ fillColor: string;
4
+ }
5
+ export declare function extractTextColors(ops: {
6
+ fnArray: number[];
7
+ argsArray: any[];
8
+ }): Map<string, string>;
9
+ export interface PositionColor {
10
+ x: number;
11
+ y: number;
12
+ color: string;
13
+ fontName: string;
14
+ fontSize: number;
15
+ orderIndex: number;
16
+ }
17
+ export declare function extractTextPositionColors(ops: {
18
+ fnArray: number[];
19
+ argsArray: any[];
20
+ }, pageHeight: number): PositionColor[] & {
21
+ fontRefs?: Set<string>;
22
+ };
23
+ export interface GradientInfo {
24
+ type: 'linear' | 'radial';
25
+ stops: {
26
+ offset: number;
27
+ color: string;
28
+ }[];
29
+ x1?: number;
30
+ y1?: number;
31
+ x2?: number;
32
+ y2?: number;
33
+ cx?: number;
34
+ cy?: number;
35
+ r0?: number;
36
+ r1?: number;
37
+ }
38
+ export interface ExtractedDrawing {
39
+ rect: [number, number, number, number];
40
+ fill: [number, number, number] | null;
41
+ stroke: [number, number, number] | null;
42
+ strokeWidth: number;
43
+ items: DrawingItem[];
44
+ opacity: number;
45
+ evenOdd: boolean;
46
+ closePath: boolean;
47
+ orderIndex: number;
48
+ gradient: GradientInfo | null;
49
+ clipPath: DrawingItem[] | null;
50
+ clipRect: [number, number, number, number] | null;
51
+ _shadingNames?: string[] | null;
52
+ _shadingMatrix?: [number, number, number, number, number, number] | null;
53
+ }
54
+ export type DrawingItem = {
55
+ kind: 're';
56
+ x: number;
57
+ y: number;
58
+ w: number;
59
+ h: number;
60
+ } | {
61
+ kind: 'm';
62
+ x: number;
63
+ y: number;
64
+ } | {
65
+ kind: 'l';
66
+ x1: number;
67
+ y1: number;
68
+ x2: number;
69
+ y2: number;
70
+ } | {
71
+ kind: 'c';
72
+ x1: number;
73
+ y1: number;
74
+ cpx1: number;
75
+ cpy1: number;
76
+ cpx2: number;
77
+ cpy2: number;
78
+ x2: number;
79
+ y2: number;
80
+ };
81
+ export interface ExtractedImageRef {
82
+ name: string;
83
+ x: number;
84
+ y: number;
85
+ width: number;
86
+ height: number;
87
+ rotation: number;
88
+ clipPath: DrawingItem[] | null;
89
+ clipRect: [number, number, number, number] | null;
90
+ orderIndex: number;
91
+ }
92
+ export declare function extractDrawingsAndImages(ops: {
93
+ fnArray: number[];
94
+ argsArray: any[];
95
+ }, pageHeight: number): {
96
+ drawings: ExtractedDrawing[];
97
+ imageRefs: ExtractedImageRef[];
98
+ };
99
+ //# sourceMappingURL=operator-list.d.ts.map