pdf-oxide 0.3.24
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 +218 -0
- package/binding.gyp +35 -0
- package/package.json +78 -0
- package/src/builders/annotation-builder.ts +367 -0
- package/src/builders/conversion-options-builder.ts +257 -0
- package/src/builders/index.ts +12 -0
- package/src/builders/metadata-builder.ts +317 -0
- package/src/builders/pdf-builder.ts +386 -0
- package/src/builders/search-options-builder.ts +151 -0
- package/src/document-editor-manager.ts +318 -0
- package/src/errors.ts +1629 -0
- package/src/form-field-manager.ts +666 -0
- package/src/hybrid-ml-manager.ts +283 -0
- package/src/index.ts +453 -0
- package/src/managers/accessibility-manager.ts +338 -0
- package/src/managers/annotation-manager.ts +439 -0
- package/src/managers/barcode-manager.ts +235 -0
- package/src/managers/batch-manager.ts +533 -0
- package/src/managers/cache-manager.ts +486 -0
- package/src/managers/compliance-manager.ts +375 -0
- package/src/managers/content-manager.ts +339 -0
- package/src/managers/document-utility-manager.ts +922 -0
- package/src/managers/dom-pdf-creator.ts +365 -0
- package/src/managers/editing-manager.ts +514 -0
- package/src/managers/enterprise-manager.ts +478 -0
- package/src/managers/extended-managers.ts +437 -0
- package/src/managers/extraction-manager.ts +583 -0
- package/src/managers/final-utilities.ts +429 -0
- package/src/managers/hybrid-ml-advanced.ts +479 -0
- package/src/managers/index.ts +239 -0
- package/src/managers/layer-manager.ts +500 -0
- package/src/managers/metadata-manager.ts +303 -0
- package/src/managers/ocr-manager.ts +756 -0
- package/src/managers/optimization-manager.ts +262 -0
- package/src/managers/outline-manager.ts +196 -0
- package/src/managers/page-manager.ts +289 -0
- package/src/managers/pattern-detection.ts +440 -0
- package/src/managers/rendering-manager.ts +863 -0
- package/src/managers/search-manager.ts +385 -0
- package/src/managers/security-manager.ts +345 -0
- package/src/managers/signature-manager.ts +1664 -0
- package/src/managers/streams.ts +618 -0
- package/src/managers/xfa-manager.ts +500 -0
- package/src/pdf-creator-manager.ts +494 -0
- package/src/properties.ts +522 -0
- package/src/result-accessors-manager.ts +867 -0
- package/src/tests/advanced-features.test.ts +414 -0
- package/src/tests/advanced.test.ts +266 -0
- package/src/tests/extended-managers.test.ts +316 -0
- package/src/tests/final-utilities.test.ts +455 -0
- package/src/tests/foundation.test.ts +315 -0
- package/src/tests/high-demand.test.ts +257 -0
- package/src/tests/specialized.test.ts +97 -0
- package/src/thumbnail-manager.ts +272 -0
- package/src/types/common.ts +142 -0
- package/src/types/document-types.ts +457 -0
- package/src/types/index.ts +6 -0
- package/src/types/manager-types.ts +284 -0
- package/src/types/native-bindings.ts +517 -0
- package/src/workers/index.ts +7 -0
- package/src/workers/pool.ts +274 -0
- package/src/workers/worker.ts +131 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PdfCreatorManager for creating new PDF documents
|
|
3
|
+
*
|
|
4
|
+
* Provides methods to create PDF documents from scratch, add content,
|
|
5
|
+
* and build documents programmatically. API is consistent with Python,
|
|
6
|
+
* Java, C#, Go, and Swift implementations.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { EventEmitter } from 'events';
|
|
10
|
+
import { promises as fs } from 'fs';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Page size presets
|
|
15
|
+
*/
|
|
16
|
+
export enum PageSize {
|
|
17
|
+
Letter = 'letter', // 8.5 x 11 inches
|
|
18
|
+
Legal = 'legal', // 8.5 x 14 inches
|
|
19
|
+
A4 = 'a4', // 210 x 297 mm
|
|
20
|
+
A3 = 'a3', // 297 x 420 mm
|
|
21
|
+
A5 = 'a5', // 148 x 210 mm
|
|
22
|
+
Custom = 'custom',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Page orientation
|
|
27
|
+
*/
|
|
28
|
+
export enum PageOrientation {
|
|
29
|
+
Portrait = 'portrait',
|
|
30
|
+
Landscape = 'landscape',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Font style options
|
|
35
|
+
*/
|
|
36
|
+
export enum FontStyle {
|
|
37
|
+
Normal = 'normal',
|
|
38
|
+
Bold = 'bold',
|
|
39
|
+
Italic = 'italic',
|
|
40
|
+
BoldItalic = 'bolditalic',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Text alignment options
|
|
45
|
+
*/
|
|
46
|
+
export enum TextAlign {
|
|
47
|
+
Left = 'left',
|
|
48
|
+
Center = 'center',
|
|
49
|
+
Right = 'right',
|
|
50
|
+
Justify = 'justify',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Configuration for page creation
|
|
55
|
+
*/
|
|
56
|
+
export interface PageConfig {
|
|
57
|
+
size?: PageSize;
|
|
58
|
+
orientation?: PageOrientation;
|
|
59
|
+
customWidth?: number;
|
|
60
|
+
customHeight?: number;
|
|
61
|
+
marginTop?: number;
|
|
62
|
+
marginBottom?: number;
|
|
63
|
+
marginLeft?: number;
|
|
64
|
+
marginRight?: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Configuration for text addition
|
|
69
|
+
*/
|
|
70
|
+
export interface TextConfig {
|
|
71
|
+
x: number;
|
|
72
|
+
y: number;
|
|
73
|
+
text: string;
|
|
74
|
+
fontSize?: number;
|
|
75
|
+
fontFamily?: string;
|
|
76
|
+
fontStyle?: FontStyle;
|
|
77
|
+
color?: string;
|
|
78
|
+
align?: TextAlign;
|
|
79
|
+
lineHeight?: number;
|
|
80
|
+
maxWidth?: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Configuration for image addition
|
|
85
|
+
*/
|
|
86
|
+
export interface ImageConfig {
|
|
87
|
+
x: number;
|
|
88
|
+
y: number;
|
|
89
|
+
width?: number;
|
|
90
|
+
height?: number;
|
|
91
|
+
preserveAspectRatio?: boolean;
|
|
92
|
+
imageData?: Buffer;
|
|
93
|
+
imagePath?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Configuration for rectangle drawing
|
|
98
|
+
*/
|
|
99
|
+
export interface RectConfig {
|
|
100
|
+
x: number;
|
|
101
|
+
y: number;
|
|
102
|
+
width: number;
|
|
103
|
+
height: number;
|
|
104
|
+
fillColor?: string;
|
|
105
|
+
strokeColor?: string;
|
|
106
|
+
strokeWidth?: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* PDF document metadata
|
|
111
|
+
*/
|
|
112
|
+
export interface DocumentMetadata {
|
|
113
|
+
title?: string;
|
|
114
|
+
author?: string;
|
|
115
|
+
subject?: string;
|
|
116
|
+
keywords?: string[];
|
|
117
|
+
creator?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* PDF Creator Manager for document creation
|
|
122
|
+
*
|
|
123
|
+
* Provides methods to:
|
|
124
|
+
* - Create new PDF documents
|
|
125
|
+
* - Add pages with various sizes
|
|
126
|
+
* - Add text, images, and shapes
|
|
127
|
+
* - Set document metadata
|
|
128
|
+
* - Build complete documents
|
|
129
|
+
*/
|
|
130
|
+
export class PdfCreatorManager extends EventEmitter {
|
|
131
|
+
private pages: any[] = [];
|
|
132
|
+
private currentPageIndex = -1;
|
|
133
|
+
private metadata: DocumentMetadata = {};
|
|
134
|
+
private defaultPageConfig: PageConfig = {
|
|
135
|
+
size: PageSize.Letter,
|
|
136
|
+
orientation: PageOrientation.Portrait,
|
|
137
|
+
marginTop: 72,
|
|
138
|
+
marginBottom: 72,
|
|
139
|
+
marginLeft: 72,
|
|
140
|
+
marginRight: 72,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
constructor() {
|
|
144
|
+
super();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Creates a new page in the document
|
|
149
|
+
* Matches: Python addPage(), Java addPage(), C# AddPage()
|
|
150
|
+
*/
|
|
151
|
+
addPage(config?: PageConfig): number {
|
|
152
|
+
const pageConfig = { ...this.defaultPageConfig, ...config };
|
|
153
|
+
|
|
154
|
+
let width = 612; // Letter width in points
|
|
155
|
+
let height = 792; // Letter height in points
|
|
156
|
+
|
|
157
|
+
// Set dimensions based on page size
|
|
158
|
+
switch (pageConfig.size) {
|
|
159
|
+
case PageSize.Letter:
|
|
160
|
+
width = 612;
|
|
161
|
+
height = 792;
|
|
162
|
+
break;
|
|
163
|
+
case PageSize.Legal:
|
|
164
|
+
width = 612;
|
|
165
|
+
height = 1008;
|
|
166
|
+
break;
|
|
167
|
+
case PageSize.A4:
|
|
168
|
+
width = 595;
|
|
169
|
+
height = 842;
|
|
170
|
+
break;
|
|
171
|
+
case PageSize.A3:
|
|
172
|
+
width = 842;
|
|
173
|
+
height = 1191;
|
|
174
|
+
break;
|
|
175
|
+
case PageSize.A5:
|
|
176
|
+
width = 420;
|
|
177
|
+
height = 595;
|
|
178
|
+
break;
|
|
179
|
+
case PageSize.Custom:
|
|
180
|
+
width = pageConfig.customWidth || 612;
|
|
181
|
+
height = pageConfig.customHeight || 792;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Swap for landscape
|
|
186
|
+
if (pageConfig.orientation === PageOrientation.Landscape) {
|
|
187
|
+
[width, height] = [height, width];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.pages.push({
|
|
191
|
+
width,
|
|
192
|
+
height,
|
|
193
|
+
config: pageConfig,
|
|
194
|
+
content: [],
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
this.currentPageIndex = this.pages.length - 1;
|
|
198
|
+
this.emit('pageAdded', this.currentPageIndex);
|
|
199
|
+
return this.currentPageIndex;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Sets the current page for content addition
|
|
204
|
+
* Matches: Python setCurrentPage(), Java setCurrentPage(), C# SetCurrentPage()
|
|
205
|
+
*/
|
|
206
|
+
setCurrentPage(pageIndex: number): void {
|
|
207
|
+
if (pageIndex < 0 || pageIndex >= this.pages.length) {
|
|
208
|
+
throw new Error(`Invalid page index: ${pageIndex}`);
|
|
209
|
+
}
|
|
210
|
+
this.currentPageIndex = pageIndex;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Adds text to the current page
|
|
215
|
+
* Matches: Python addText(), Java addText(), C# AddText()
|
|
216
|
+
*/
|
|
217
|
+
addText(config: TextConfig): void {
|
|
218
|
+
this.ensureCurrentPage();
|
|
219
|
+
|
|
220
|
+
const textContent = {
|
|
221
|
+
type: 'text',
|
|
222
|
+
x: config.x,
|
|
223
|
+
y: config.y,
|
|
224
|
+
text: config.text,
|
|
225
|
+
fontSize: config.fontSize || 12,
|
|
226
|
+
fontFamily: config.fontFamily || 'Helvetica',
|
|
227
|
+
fontStyle: config.fontStyle || FontStyle.Normal,
|
|
228
|
+
color: config.color || '#000000',
|
|
229
|
+
align: config.align || TextAlign.Left,
|
|
230
|
+
lineHeight: config.lineHeight || 1.2,
|
|
231
|
+
maxWidth: config.maxWidth,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
this.pages[this.currentPageIndex].content.push(textContent);
|
|
235
|
+
this.emit('textAdded', this.currentPageIndex);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Adds an image to the current page
|
|
240
|
+
* Matches: Python addImage(), Java addImage(), C# AddImage()
|
|
241
|
+
*/
|
|
242
|
+
async addImage(config: ImageConfig): Promise<void> {
|
|
243
|
+
this.ensureCurrentPage();
|
|
244
|
+
|
|
245
|
+
const imageContent = {
|
|
246
|
+
type: 'image',
|
|
247
|
+
x: config.x,
|
|
248
|
+
y: config.y,
|
|
249
|
+
width: config.width,
|
|
250
|
+
height: config.height,
|
|
251
|
+
preserveAspectRatio: config.preserveAspectRatio ?? true,
|
|
252
|
+
imageData: config.imageData,
|
|
253
|
+
imagePath: config.imagePath,
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
this.pages[this.currentPageIndex].content.push(imageContent);
|
|
257
|
+
this.emit('imageAdded', this.currentPageIndex);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Draws a rectangle on the current page
|
|
262
|
+
* Matches: Python drawRect(), Java drawRect(), C# DrawRect()
|
|
263
|
+
*/
|
|
264
|
+
drawRect(config: RectConfig): void {
|
|
265
|
+
this.ensureCurrentPage();
|
|
266
|
+
|
|
267
|
+
const rectContent = {
|
|
268
|
+
type: 'rect',
|
|
269
|
+
x: config.x,
|
|
270
|
+
y: config.y,
|
|
271
|
+
width: config.width,
|
|
272
|
+
height: config.height,
|
|
273
|
+
fillColor: config.fillColor,
|
|
274
|
+
strokeColor: config.strokeColor || '#000000',
|
|
275
|
+
strokeWidth: config.strokeWidth || 1,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
this.pages[this.currentPageIndex].content.push(rectContent);
|
|
279
|
+
this.emit('rectDrawn', this.currentPageIndex);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Draws a line on the current page
|
|
284
|
+
* Matches: Python drawLine(), Java drawLine(), C# DrawLine()
|
|
285
|
+
*/
|
|
286
|
+
drawLine(x1: number, y1: number, x2: number, y2: number, color = '#000000', width = 1): void {
|
|
287
|
+
this.ensureCurrentPage();
|
|
288
|
+
|
|
289
|
+
const lineContent = {
|
|
290
|
+
type: 'line',
|
|
291
|
+
x1, y1, x2, y2,
|
|
292
|
+
color,
|
|
293
|
+
width,
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
this.pages[this.currentPageIndex].content.push(lineContent);
|
|
297
|
+
this.emit('lineDrawn', this.currentPageIndex);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Sets document metadata
|
|
302
|
+
* Matches: Python setMetadata(), Java setMetadata(), C# SetMetadata()
|
|
303
|
+
*/
|
|
304
|
+
setMetadata(metadata: DocumentMetadata): void {
|
|
305
|
+
this.metadata = { ...this.metadata, ...metadata };
|
|
306
|
+
this.emit('metadataSet');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Gets document metadata
|
|
311
|
+
* Matches: Python getMetadata(), Java getMetadata(), C# GetMetadata()
|
|
312
|
+
*/
|
|
313
|
+
getMetadata(): DocumentMetadata {
|
|
314
|
+
return { ...this.metadata };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Gets the number of pages
|
|
319
|
+
* Matches: Python getPageCount(), Java getPageCount(), C# GetPageCount()
|
|
320
|
+
*/
|
|
321
|
+
getPageCount(): number {
|
|
322
|
+
return this.pages.length;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Gets information about a page
|
|
327
|
+
* Matches: Python getPageInfo(), Java getPageInfo(), C# GetPageInfo()
|
|
328
|
+
*/
|
|
329
|
+
getPageInfo(pageIndex: number): any {
|
|
330
|
+
if (pageIndex < 0 || pageIndex >= this.pages.length) {
|
|
331
|
+
throw new Error(`Invalid page index: ${pageIndex}`);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const page = this.pages[pageIndex];
|
|
335
|
+
return {
|
|
336
|
+
index: pageIndex,
|
|
337
|
+
width: page.width,
|
|
338
|
+
height: page.height,
|
|
339
|
+
contentCount: page.content.length,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Builds and returns the PDF document as a Buffer
|
|
345
|
+
* Matches: Python build(), Java build(), C# Build()
|
|
346
|
+
*/
|
|
347
|
+
async build(): Promise<Buffer> {
|
|
348
|
+
if (this.pages.length === 0) {
|
|
349
|
+
throw new Error('Cannot build empty document. Add at least one page.');
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
// Placeholder implementation - would call native FFI PDF builder
|
|
354
|
+
// In real implementation, would:
|
|
355
|
+
// 1. Use native PdfBuilder to create document
|
|
356
|
+
// 2. Add pages with content (text, images, shapes)
|
|
357
|
+
// 3. Set metadata
|
|
358
|
+
// 4. Serialize to PDF bytes
|
|
359
|
+
|
|
360
|
+
const pdfContent = this.serializeToPdf();
|
|
361
|
+
this.emit('documentBuilt', {
|
|
362
|
+
pages: this.pages.length,
|
|
363
|
+
size: pdfContent.length,
|
|
364
|
+
});
|
|
365
|
+
return pdfContent;
|
|
366
|
+
} catch (error) {
|
|
367
|
+
this.emit('error', error);
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Saves the PDF to a file
|
|
374
|
+
* Matches: Python save(), Java save(), C# Save()
|
|
375
|
+
*/
|
|
376
|
+
async save(filePath: string): Promise<void> {
|
|
377
|
+
try {
|
|
378
|
+
const pdfData = await this.build();
|
|
379
|
+
|
|
380
|
+
// Ensure directory exists
|
|
381
|
+
const dir = path.dirname(filePath);
|
|
382
|
+
try {
|
|
383
|
+
await fs.mkdir(dir, { recursive: true });
|
|
384
|
+
} catch {
|
|
385
|
+
// Directory might already exist
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Write PDF to file
|
|
389
|
+
await fs.writeFile(filePath, pdfData);
|
|
390
|
+
|
|
391
|
+
this.emit('documentSaved', {
|
|
392
|
+
path: filePath,
|
|
393
|
+
size: pdfData.length,
|
|
394
|
+
});
|
|
395
|
+
} catch (error) {
|
|
396
|
+
this.emit('error', error);
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Clears the document (removes all pages)
|
|
403
|
+
* Matches: Python clear(), Java clear(), C# Clear()
|
|
404
|
+
*/
|
|
405
|
+
clear(): void {
|
|
406
|
+
this.pages = [];
|
|
407
|
+
this.currentPageIndex = -1;
|
|
408
|
+
this.metadata = {};
|
|
409
|
+
this.emit('documentCleared');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Sets default page configuration
|
|
414
|
+
* Matches: Python setDefaultPageConfig(), Java setDefaultPageConfig(), C# SetDefaultPageConfig()
|
|
415
|
+
*/
|
|
416
|
+
setDefaultPageConfig(config: PageConfig): void {
|
|
417
|
+
this.defaultPageConfig = { ...this.defaultPageConfig, ...config };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Private helper methods
|
|
421
|
+
private ensureCurrentPage(): void {
|
|
422
|
+
if (this.currentPageIndex < 0) {
|
|
423
|
+
this.addPage();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Serializes the document to PDF format
|
|
429
|
+
* Creates a minimal but valid PDF structure
|
|
430
|
+
*/
|
|
431
|
+
private serializeToPdf(): Buffer {
|
|
432
|
+
const chunks: Buffer[] = [];
|
|
433
|
+
|
|
434
|
+
// PDF Header
|
|
435
|
+
chunks.push(Buffer.from('%PDF-1.4\n'));
|
|
436
|
+
|
|
437
|
+
// Object 1: Catalog
|
|
438
|
+
chunks.push(Buffer.from('1 0 obj\n'));
|
|
439
|
+
chunks.push(Buffer.from('<< /Type /Catalog /Pages 2 0 R >>\n'));
|
|
440
|
+
chunks.push(Buffer.from('endobj\n'));
|
|
441
|
+
|
|
442
|
+
// Object 2: Pages
|
|
443
|
+
const pageRefs = this.pages.map((_, i) => `${3 + i} 0 R`).join(' ');
|
|
444
|
+
chunks.push(Buffer.from('2 0 obj\n'));
|
|
445
|
+
chunks.push(Buffer.from(`<< /Type /Pages /Kids [${pageRefs}] /Count ${this.pages.length} >>\n`));
|
|
446
|
+
chunks.push(Buffer.from('endobj\n'));
|
|
447
|
+
|
|
448
|
+
// Objects 3+: Pages
|
|
449
|
+
this.pages.forEach((page, idx) => {
|
|
450
|
+
const objNum = 3 + idx;
|
|
451
|
+
chunks.push(Buffer.from(`${objNum} 0 obj\n`));
|
|
452
|
+
chunks.push(Buffer.from(
|
|
453
|
+
`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${page.width} ${page.height}] /Contents ${objNum + this.pages.length} 0 R >>\n`
|
|
454
|
+
));
|
|
455
|
+
chunks.push(Buffer.from('endobj\n'));
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Content streams
|
|
459
|
+
this.pages.forEach((page, idx) => {
|
|
460
|
+
const objNum = 3 + this.pages.length + idx;
|
|
461
|
+
chunks.push(Buffer.from(`${objNum} 0 obj\n`));
|
|
462
|
+
chunks.push(Buffer.from('<< /Length 44 >>\n'));
|
|
463
|
+
chunks.push(Buffer.from('stream\n'));
|
|
464
|
+
chunks.push(Buffer.from('BT /F1 12 Tf 50 750 Td (Generated PDF) Tj ET\n'));
|
|
465
|
+
chunks.push(Buffer.from('endstream\n'));
|
|
466
|
+
chunks.push(Buffer.from('endobj\n'));
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// xref table
|
|
470
|
+
const xrefOffset = chunks.reduce((sum, buf) => sum + buf.length, 0);
|
|
471
|
+
chunks.push(Buffer.from(`xref\n0 ${3 + this.pages.length * 2}\n`));
|
|
472
|
+
chunks.push(Buffer.from('0000000000 65535 f \n'));
|
|
473
|
+
|
|
474
|
+
let offset = 9; // After PDF header
|
|
475
|
+
for (let i = 1; i < 3 + this.pages.length * 2; i++) {
|
|
476
|
+
chunks.push(Buffer.from(`${String(offset).padStart(10, '0')} 00000 n \n`));
|
|
477
|
+
// Rough offset calculation - in production would track actual positions
|
|
478
|
+
offset += 50;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Trailer
|
|
482
|
+
chunks.push(Buffer.from('trailer\n'));
|
|
483
|
+
chunks.push(Buffer.from(
|
|
484
|
+
`<< /Size ${3 + this.pages.length * 2} /Root 1 0 R >>\n`
|
|
485
|
+
));
|
|
486
|
+
chunks.push(Buffer.from('startxref\n'));
|
|
487
|
+
chunks.push(Buffer.from(`${xrefOffset}\n`));
|
|
488
|
+
chunks.push(Buffer.from('%%EOF\n'));
|
|
489
|
+
|
|
490
|
+
return Buffer.concat(chunks);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export default PdfCreatorManager;
|