pdfdancer-client-typescript 1.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.
Files changed (52) hide show
  1. package/.eslintrc.js +19 -0
  2. package/README.md +267 -0
  3. package/dist/__tests__/e2e/test-helpers.d.ts +32 -0
  4. package/dist/__tests__/e2e/test-helpers.d.ts.map +1 -0
  5. package/dist/__tests__/e2e/test-helpers.js +157 -0
  6. package/dist/__tests__/e2e/test-helpers.js.map +1 -0
  7. package/dist/client-v1.d.ts +169 -0
  8. package/dist/client-v1.d.ts.map +1 -0
  9. package/dist/client-v1.js +558 -0
  10. package/dist/client-v1.js.map +1 -0
  11. package/dist/exceptions.d.ts +43 -0
  12. package/dist/exceptions.d.ts.map +1 -0
  13. package/dist/exceptions.js +66 -0
  14. package/dist/exceptions.js.map +1 -0
  15. package/dist/index.d.ts +13 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +33 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/models.d.ts +216 -0
  20. package/dist/models.d.ts.map +1 -0
  21. package/dist/models.js +427 -0
  22. package/dist/models.js.map +1 -0
  23. package/dist/paragraph-builder.d.ts +67 -0
  24. package/dist/paragraph-builder.d.ts.map +1 -0
  25. package/dist/paragraph-builder.js +160 -0
  26. package/dist/paragraph-builder.js.map +1 -0
  27. package/example.ts +103 -0
  28. package/fixtures/DancingScript-Regular.ttf +0 -0
  29. package/fixtures/JetBrainsMono-Regular.ttf +0 -0
  30. package/fixtures/ObviouslyAwesome.pdf +0 -0
  31. package/fixtures/README.md +25 -0
  32. package/fixtures/basic-paths.pdf +0 -0
  33. package/fixtures/logo-80.png +0 -0
  34. package/fixtures/mixed-form-types.pdf +0 -0
  35. package/jest.config.js +26 -0
  36. package/package.json +38 -0
  37. package/scripts/release.js +91 -0
  38. package/scripts/test-release.js +59 -0
  39. package/src/__tests__/client-v1.test.ts +111 -0
  40. package/src/__tests__/e2e/form.test.ts +51 -0
  41. package/src/__tests__/e2e/image.test.ts +108 -0
  42. package/src/__tests__/e2e/line.test.ts +134 -0
  43. package/src/__tests__/e2e/page.test.ts +45 -0
  44. package/src/__tests__/e2e/paragraph.test.ts +210 -0
  45. package/src/__tests__/e2e/path.test.ts +72 -0
  46. package/src/__tests__/e2e/test-helpers.ts +132 -0
  47. package/src/client-v1.ts +673 -0
  48. package/src/exceptions.ts +67 -0
  49. package/src/index.ts +34 -0
  50. package/src/models.ts +476 -0
  51. package/src/paragraph-builder.ts +184 -0
  52. package/tsconfig.json +20 -0
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Exception classes for the PDFDancer TypeScript client.
3
+ * Mirrors the Python client exception hierarchy.
4
+ */
5
+
6
+ /**
7
+ * Base exception for all PDFDancer client errors.
8
+ * Equivalent to PdfDancerException in the Python client.
9
+ */
10
+ export class PdfDancerException extends Error {
11
+ public readonly cause?: Error;
12
+
13
+ constructor(message: string, cause?: Error) {
14
+ super(message);
15
+ this.name = 'PdfDancerException';
16
+ this.cause = cause;
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Exception raised when a required font is not found or available.
22
+ * Equivalent to FontNotFoundException in the Python client.
23
+ */
24
+ export class FontNotFoundException extends PdfDancerException {
25
+ constructor(message: string) {
26
+ super(`Font not found: ${message}`);
27
+ this.name = 'FontNotFoundException';
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Exception raised for HTTP client errors during API communication.
33
+ * Wraps fetch exceptions and HTTP errors from the API.
34
+ */
35
+ export class HttpClientException extends PdfDancerException {
36
+ public readonly response?: Response;
37
+ public readonly statusCode?: number;
38
+
39
+ constructor(message: string, response?: Response, cause?: Error) {
40
+ super(message, cause);
41
+ this.name = 'HttpClientException';
42
+ this.response = response;
43
+ this.statusCode = response?.status;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Exception raised for session-related errors.
49
+ * Occurs when session creation fails or session is invalid.
50
+ */
51
+ export class SessionException extends PdfDancerException {
52
+ constructor(message: string, cause?: Error) {
53
+ super(message, cause);
54
+ this.name = 'SessionException';
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Exception raised for input validation errors.
60
+ * Equivalent to ValidationException in the Python client.
61
+ */
62
+ export class ValidationException extends PdfDancerException {
63
+ constructor(message: string) {
64
+ super(message);
65
+ this.name = 'ValidationException';
66
+ }
67
+ }
package/src/index.ts ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * PDFDancer TypeScript Client
3
+ *
4
+ * A TypeScript client library for the PDFDancer PDF manipulation API.
5
+ * Provides a clean, TypeScript interface for PDF operations that closely
6
+ * mirrors the Python client structure and functionality.
7
+ */
8
+
9
+ export { ClientV1 } from './client-v1';
10
+ export { ParagraphBuilder } from './paragraph-builder';
11
+
12
+ export {
13
+ PdfDancerException,
14
+ FontNotFoundException,
15
+ ValidationException,
16
+ HttpClientException,
17
+ SessionException
18
+ } from './exceptions';
19
+
20
+ export {
21
+ ObjectRef,
22
+ Position,
23
+ ObjectType,
24
+ Font,
25
+ Color,
26
+ Image,
27
+ BoundingRect,
28
+ Paragraph,
29
+ PositionMode,
30
+ ShapeType,
31
+ Point
32
+ } from './models';
33
+
34
+ export const VERSION = "1.0.0";
package/src/models.ts ADDED
@@ -0,0 +1,476 @@
1
+ /**
2
+ * Model classes for the PDFDancer TypeScript client.
3
+ * Closely mirrors the Python model classes with TypeScript conventions.
4
+ */
5
+
6
+ /**
7
+ * Object type enumeration matching the Python ObjectType.
8
+ */
9
+ export enum ObjectType {
10
+ IMAGE = "IMAGE",
11
+ FORM = "FORM",
12
+ PATH = "PATH",
13
+ PARAGRAPH = "PARAGRAPH",
14
+ TEXT_LINE = "TEXT_LINE",
15
+ PAGE = "PAGE"
16
+ }
17
+
18
+ /**
19
+ * Defines how position matching should be performed when searching for objects.
20
+ */
21
+ export enum PositionMode {
22
+ INTERSECT = "INTERSECT", // Objects that intersect with the specified position area
23
+ CONTAINS = "CONTAINS" // Objects completely contained within the specified position area
24
+ }
25
+
26
+ /**
27
+ * Defines the geometric shape type used for position specification.
28
+ */
29
+ export enum ShapeType {
30
+ POINT = "POINT", // Single point coordinate
31
+ LINE = "LINE", // Linear shape between two points
32
+ CIRCLE = "CIRCLE", // Circular area with radius
33
+ RECT = "RECT" // Rectangular area with width and height
34
+ }
35
+
36
+ /**
37
+ * Represents a 2D point with x and y coordinates.
38
+ */
39
+ export interface Point {
40
+ x: number;
41
+ y: number;
42
+ }
43
+
44
+ /**
45
+ * Represents a bounding rectangle with position and dimensions.
46
+ * Matches the Python BoundingRect class.
47
+ */
48
+ export class BoundingRect {
49
+ constructor(
50
+ public x: number,
51
+ public y: number,
52
+ public width: number,
53
+ public height: number
54
+ ) {}
55
+
56
+ getX(): number {
57
+ return this.x;
58
+ }
59
+
60
+ getY(): number {
61
+ return this.y;
62
+ }
63
+
64
+ getWidth(): number {
65
+ return this.width;
66
+ }
67
+
68
+ getHeight(): number {
69
+ return this.height;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Represents spatial positioning and location information for PDF objects.
75
+ * Closely mirrors the Python Position class with TypeScript conventions.
76
+ */
77
+ export class Position {
78
+ constructor(
79
+ public pageIndex?: number,
80
+ public shape?: ShapeType,
81
+ public mode?: PositionMode,
82
+ public boundingRect?: BoundingRect,
83
+ public textStartsWith?: string
84
+ ) {}
85
+
86
+ /**
87
+ * Creates a position specification for an entire page.
88
+ * Equivalent to Position.fromPageIndex() in Python.
89
+ */
90
+ static fromPageIndex(pageIndex: number): Position {
91
+ return new Position(pageIndex, undefined, PositionMode.CONTAINS);
92
+ }
93
+
94
+ /**
95
+ * Creates a position specification for specific coordinates on a page.
96
+ * Equivalent to Position.onPageCoordinates() in Python.
97
+ */
98
+ static onPageCoordinates(pageIndex: number, x: number, y: number): Position {
99
+ const position = Position.fromPageIndex(pageIndex);
100
+ position.setPoint({ x, y });
101
+ return position;
102
+ }
103
+
104
+ /**
105
+ * Sets the position to a specific point location.
106
+ * Equivalent to Position.set() in Python.
107
+ */
108
+ setPoint(point: Point): void {
109
+ this.mode = PositionMode.CONTAINS;
110
+ this.shape = ShapeType.POINT;
111
+ this.boundingRect = new BoundingRect(point.x, point.y, 0, 0);
112
+ }
113
+
114
+ /**
115
+ * Move the position horizontally by the specified offset.
116
+ */
117
+ moveX(xOffset: number): Position {
118
+ if (this.boundingRect) {
119
+ this.setPoint({ x: this.getX()! + xOffset, y: this.getY()! });
120
+ }
121
+ return this;
122
+ }
123
+
124
+ /**
125
+ * Move the position vertically by the specified offset.
126
+ */
127
+ moveY(yOffset: number): Position {
128
+ if (this.boundingRect) {
129
+ this.setPoint({ x: this.getX()!, y: this.getY()! + yOffset });
130
+ }
131
+ return this;
132
+ }
133
+
134
+ /**
135
+ * Returns the X coordinate of this position.
136
+ */
137
+ getX(): number | undefined {
138
+ return this.boundingRect?.getX();
139
+ }
140
+
141
+ /**
142
+ * Returns the Y coordinate of this position.
143
+ */
144
+ getY(): number | undefined {
145
+ return this.boundingRect?.getY();
146
+ }
147
+
148
+ /**
149
+ * Creates a copy of this position.
150
+ */
151
+ copy(): Position {
152
+ let boundingRectCopy: BoundingRect | undefined;
153
+ if (this.boundingRect) {
154
+ boundingRectCopy = new BoundingRect(
155
+ this.boundingRect.x,
156
+ this.boundingRect.y,
157
+ this.boundingRect.width,
158
+ this.boundingRect.height
159
+ );
160
+ }
161
+
162
+ return new Position(
163
+ this.pageIndex,
164
+ this.shape,
165
+ this.mode,
166
+ boundingRectCopy,
167
+ this.textStartsWith
168
+ );
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Lightweight reference to a PDF object providing identity and type information.
174
+ * Mirrors the Python ObjectRef class exactly.
175
+ */
176
+ export class ObjectRef {
177
+ constructor(
178
+ public internalId: string,
179
+ public position: Position,
180
+ public type: ObjectType
181
+ ) {}
182
+
183
+ getInternalId(): string {
184
+ return this.internalId;
185
+ }
186
+
187
+ getPosition(): Position {
188
+ return this.position;
189
+ }
190
+
191
+ setPosition(position: Position): void {
192
+ this.position = position;
193
+ }
194
+
195
+ getType(): ObjectType {
196
+ return this.type;
197
+ }
198
+
199
+ toDict(): Record<string, any> {
200
+ return {
201
+ internalId: this.internalId,
202
+ position: positionToDict(this.position),
203
+ type: this.type
204
+ };
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Represents an RGB color with optional alpha channel, values from 0-255.
210
+ */
211
+ export class Color {
212
+ constructor(
213
+ public r: number,
214
+ public g: number,
215
+ public b: number,
216
+ public a: number = 255 // Alpha channel, default fully opaque
217
+ ) {
218
+ // Validation similar to Python client
219
+ for (const component of [this.r, this.g, this.b, this.a]) {
220
+ if (component < 0 || component > 255) {
221
+ throw new Error(`Color component must be between 0 and 255, got ${component}`);
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Represents a font with name and size.
229
+ */
230
+ export class Font {
231
+ constructor(
232
+ public name: string,
233
+ public size: number
234
+ ) {
235
+ if (this.size <= 0) {
236
+ throw new Error(`Font size must be positive, got ${this.size}`);
237
+ }
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Represents an image object in a PDF document.
243
+ * Matches the Python Image class structure.
244
+ */
245
+ export class Image {
246
+ constructor(
247
+ public position?: Position,
248
+ public format?: string,
249
+ public width?: number,
250
+ public height?: number,
251
+ public data?: Uint8Array
252
+ ) {}
253
+
254
+ getPosition(): Position | undefined {
255
+ return this.position;
256
+ }
257
+
258
+ setPosition(position: Position): void {
259
+ this.position = position;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Represents a paragraph of text in a PDF document.
265
+ * Structure mirrors the Python Paragraph class.
266
+ */
267
+ export class Paragraph {
268
+ constructor(
269
+ public position?: Position,
270
+ public textLines?: string[],
271
+ public font?: Font,
272
+ public color?: Color,
273
+ public lineSpacing: number = 1.2
274
+ ) {}
275
+
276
+ getPosition(): Position | undefined {
277
+ return this.position;
278
+ }
279
+
280
+ setPosition(position: Position): void {
281
+ this.position = position;
282
+ }
283
+ }
284
+
285
+ // Request classes for API communication
286
+
287
+ /**
288
+ * Request object for find operations.
289
+ */
290
+ export class FindRequest {
291
+ constructor(
292
+ public objectType?: ObjectType,
293
+ public position?: Position,
294
+ public hint?: string
295
+ ) {}
296
+
297
+ toDict(): Record<string, any> {
298
+ return {
299
+ objectType: this.objectType || null,
300
+ position: this.position ? positionToDict(this.position) : null,
301
+ hint: this.hint || null
302
+ };
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Request object for delete operations.
308
+ */
309
+ export class DeleteRequest {
310
+ constructor(public objectRef: ObjectRef) {}
311
+
312
+ toDict(): Record<string, any> {
313
+ return {
314
+ objectRef: {
315
+ internalId: this.objectRef.internalId,
316
+ position: positionToDict(this.objectRef.position),
317
+ type: this.objectRef.type
318
+ }
319
+ };
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Request object for move operations.
325
+ */
326
+ export class MoveRequest {
327
+ constructor(
328
+ public objectRef: ObjectRef,
329
+ public position: Position
330
+ ) {}
331
+
332
+ toDict(): Record<string, any> {
333
+ return {
334
+ objectRef: {
335
+ internalId: this.objectRef.internalId,
336
+ position: positionToDict(this.objectRef.position),
337
+ type: this.objectRef.type
338
+ },
339
+ newPosition: positionToDict(this.position)
340
+ };
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Request object for add operations.
346
+ */
347
+ export class AddRequest {
348
+ constructor(public pdfObject: Image | Paragraph) {}
349
+
350
+ toDict(): Record<string, any> {
351
+ return {
352
+ object: this.objectToDict(this.pdfObject)
353
+ };
354
+ }
355
+
356
+ private objectToDict(obj: Image | Paragraph): Record<string, any> {
357
+ if (obj instanceof Image) {
358
+ const size = obj.width !== undefined && obj.height !== undefined
359
+ ? { width: obj.width, height: obj.height }
360
+ : null;
361
+
362
+ const dataB64 = obj.data ? btoa(String.fromCharCode(...obj.data)) : null;
363
+
364
+ return {
365
+ type: "IMAGE",
366
+ position: obj.position ? positionToDict(obj.position) : null,
367
+ format: obj.format || null,
368
+ size,
369
+ data: dataB64
370
+ };
371
+ } else if (obj instanceof Paragraph) {
372
+ const lines: any[] = [];
373
+ if (obj.textLines) {
374
+ for (const line of obj.textLines) {
375
+ const textElement = {
376
+ text: line,
377
+ font: obj.font ? { name: obj.font.name, size: obj.font.size } : null,
378
+ color: obj.color ? { r: obj.color.r, g: obj.color.g, b: obj.color.b } : null,
379
+ position: obj.position ? positionToDict(obj.position) : null
380
+ };
381
+
382
+ const textLine: any = {
383
+ textElements: [textElement]
384
+ };
385
+
386
+ if (obj.color) {
387
+ textLine.color = { r: obj.color.r, g: obj.color.g, b: obj.color.b };
388
+ }
389
+ if (obj.position) {
390
+ textLine.position = positionToDict(obj.position);
391
+ }
392
+ lines.push(textLine);
393
+ }
394
+ }
395
+
396
+ const lineSpacings = obj.lineSpacing !== undefined ? [obj.lineSpacing] : null;
397
+
398
+ return {
399
+ type: "PARAGRAPH",
400
+ position: obj.position ? positionToDict(obj.position) : null,
401
+ lines,
402
+ lineSpacings,
403
+ font: obj.font ? { name: obj.font.name, size: obj.font.size } : null
404
+ };
405
+ } else {
406
+ throw new Error(`Unsupported object type: ${typeof obj}`);
407
+ }
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Request object for modify operations.
413
+ */
414
+ export class ModifyRequest {
415
+ constructor(
416
+ public objectRef: ObjectRef,
417
+ public newObject: Image | Paragraph
418
+ ) {}
419
+
420
+ toDict(): Record<string, any> {
421
+ return {
422
+ ref: {
423
+ internalId: this.objectRef.internalId,
424
+ position: positionToDict(this.objectRef.position),
425
+ type: this.objectRef.type
426
+ },
427
+ newObject: new AddRequest(this.newObject).toDict().object
428
+ };
429
+ }
430
+ }
431
+
432
+ /**
433
+ * Request object for text modification operations.
434
+ */
435
+ export class ModifyTextRequest {
436
+ constructor(
437
+ public objectRef: ObjectRef,
438
+ public newText: string
439
+ ) {}
440
+
441
+ toDict(): Record<string, any> {
442
+ return {
443
+ ref: {
444
+ internalId: this.objectRef.internalId,
445
+ position: positionToDict(this.objectRef.position),
446
+ type: this.objectRef.type
447
+ },
448
+ newTextLine: this.newText
449
+ };
450
+ }
451
+ }
452
+
453
+ // Helper function to convert Position to dictionary for JSON serialization
454
+ function positionToDict(position: Position): Record<string, any> {
455
+ const result: Record<string, any> = {
456
+ pageIndex: position.pageIndex,
457
+ textStartsWith: position.textStartsWith
458
+ };
459
+
460
+ if (position.shape) {
461
+ result.shape = position.shape;
462
+ }
463
+ if (position.mode) {
464
+ result.mode = position.mode;
465
+ }
466
+ if (position.boundingRect) {
467
+ result.boundingRect = {
468
+ x: position.boundingRect.x,
469
+ y: position.boundingRect.y,
470
+ width: position.boundingRect.width,
471
+ height: position.boundingRect.height
472
+ };
473
+ }
474
+
475
+ return result;
476
+ }