pdfdancer-client-typescript 1.0.9 → 1.0.10

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 (41) hide show
  1. package/README.md +189 -27
  2. package/dist/__tests__/e2e/pdf-assertions.d.ts +20 -3
  3. package/dist/__tests__/e2e/pdf-assertions.d.ts.map +1 -1
  4. package/dist/__tests__/e2e/pdf-assertions.js +151 -86
  5. package/dist/__tests__/e2e/pdf-assertions.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +7 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/models.d.ts +106 -1
  11. package/dist/models.d.ts.map +1 -1
  12. package/dist/models.js +195 -2
  13. package/dist/models.js.map +1 -1
  14. package/dist/paragraph-builder.d.ts +2 -2
  15. package/dist/paragraph-builder.d.ts.map +1 -1
  16. package/dist/paragraph-builder.js +10 -2
  17. package/dist/paragraph-builder.js.map +1 -1
  18. package/dist/pdfdancer_v1.d.ts +41 -4
  19. package/dist/pdfdancer_v1.d.ts.map +1 -1
  20. package/dist/pdfdancer_v1.js +198 -20
  21. package/dist/pdfdancer_v1.js.map +1 -1
  22. package/dist/types.d.ts +15 -3
  23. package/dist/types.d.ts.map +1 -1
  24. package/dist/types.js +41 -1
  25. package/dist/types.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/__tests__/e2e/acroform.test.ts +13 -0
  28. package/src/__tests__/e2e/create-new.test.ts +133 -0
  29. package/src/__tests__/e2e/form_x_object.test.ts +8 -0
  30. package/src/__tests__/e2e/image.test.ts +30 -18
  31. package/src/__tests__/e2e/line.test.ts +33 -6
  32. package/src/__tests__/e2e/page.test.ts +4 -0
  33. package/src/__tests__/e2e/paragraph.test.ts +29 -2
  34. package/src/__tests__/e2e/path.test.ts +8 -0
  35. package/src/__tests__/e2e/pdf-assertions.ts +164 -73
  36. package/src/__tests__/url-builder.test.ts +44 -0
  37. package/src/index.ts +8 -1
  38. package/src/models.ts +255 -1
  39. package/src/paragraph-builder.ts +13 -5
  40. package/src/pdfdancer_v1.ts +277 -28
  41. package/src/types.ts +55 -3
package/README.md CHANGED
@@ -1,16 +1,28 @@
1
1
  # PDFDancer TypeScript Client
2
2
 
3
- A TypeScript client library for the PDFDancer PDF manipulation API.
3
+ **Getting Started with PDFDancer**
4
4
 
5
- ## Features
5
+ PDFDancer gives you pixel-perfect programmatic control over any PDF document from TypeScript. Locate existing elements by coordinates or text, adjust them precisely, add brand-new content, and ship the modified PDF in memory or on disk. The same API is also available for Python and Java, so teams can orchestrate identical PDF workflows across stacks.
6
6
 
7
- - Session-based PDF manipulation with automatic session creation
8
- - Type-safe models and fluent builders for paragraphs and images
9
- - Page-scoped selectors for paragraphs, text lines, images, forms, and paths
10
- - Form filling helpers with field-name lookup
11
- - Custom font registration with on-the-fly TTF uploads
12
- - Detailed exceptions for validation, HTTP, and session errors
13
- - Works in both Node.js and browser environments
7
+ > Need the raw API schema? The latest OpenAPI description is published at https://bucket.pdfdancer.com/api-doc/development-0.0.yml.
8
+
9
+ ## Highlights
10
+
11
+ - Locate paragraphs, text lines, images, vector paths, form fields, and pages by index, coordinates, or text prefixes.
12
+ - Edit existing content in place with fluent editors that apply changes safely.
13
+ - Programmatically control third-party PDFs—modify invoices, contracts, and reports you did not author.
14
+ - Add content with precise XY positioning using paragraph and image builders, custom fonts, and color helpers.
15
+ - Export results as bytes for downstream processing or save directly to disk with one call.
16
+ - Works in both Node.js and browser environments.
17
+
18
+ ## What Makes PDFDancer Different
19
+
20
+ - **Edit any PDF**: Work with documents from customers, governments, or vendors—not just ones you generated.
21
+ - **Pixel-perfect positioning**: Move or add elements at exact coordinates and keep the original layout intact.
22
+ - **Surgical text replacement**: Swap or rewrite paragraphs without reflowing the rest of the page.
23
+ - **Form manipulation**: Inspect, fill, and update AcroForm fields programmatically.
24
+ - **Coordinate-based selection**: Select objects by position, bounding box, or text patterns.
25
+ - **Real PDF editing**: Modify the underlying PDF structure instead of merely stamping overlays.
14
26
 
15
27
  ## Installation
16
28
 
@@ -18,36 +30,109 @@ A TypeScript client library for the PDFDancer PDF manipulation API.
18
30
  npm install pdfdancer-client-typescript
19
31
  ```
20
32
 
21
- ## Quick Start
33
+ Requires Node.js 18+ (or a modern browser) and a PDFDancer API token.
34
+
35
+ ## Quick Start — Edit an Existing PDF
22
36
 
23
37
  ```typescript
24
- import { PDFDancer, Color } from 'pdfdancer-client-typescript';
38
+ import { PDFDancer, Color, StandardFonts } from 'pdfdancer-client-typescript';
25
39
  import { promises as fs } from 'node:fs';
26
40
 
27
41
  async function run() {
28
42
  const pdfBytes = await fs.readFile('input.pdf');
29
43
 
30
- // Token defaults to PDFDANCER_TOKEN when omitted.
31
- const pdf = await PDFDancer.open(pdfBytes, 'your-auth-token');
44
+ // Token defaults to PDFDANCER_TOKEN environment variable when omitted
45
+ const pdf = await PDFDancer.open(
46
+ pdfBytes,
47
+ 'your-api-token', // optional when PDFDANCER_TOKEN is set
48
+ 'https://api.pdfdancer.com' // optional base URL
49
+ );
50
+
51
+ // Locate and update an existing paragraph
52
+ const heading = (await pdf.page(0).selectParagraphsStartingWith('Executive Summary'))[0];
53
+ await heading.moveTo(72, 680);
32
54
 
33
- const page0 = pdf.page(0); // Page indexes are zero-based
55
+ const result = await heading.edit()
56
+ .replace('Overview')
57
+ .apply();
34
58
 
35
- await page0.newParagraph()
36
- .text('Hello, PDFDancer!')
37
- .font('Roboto-Regular', 14)
38
- .color(new Color(255, 64, 64))
39
- .lineSpacing(1.1)
40
- .at(100, 200)
59
+ // Add a new paragraph with precise placement
60
+ await pdf.page(0).newParagraph()
61
+ .text('Generated with PDFDancer')
62
+ .font(StandardFonts.HELVETICA, 12)
63
+ .color(new Color(70, 70, 70))
64
+ .lineSpacing(1.4)
65
+ .at(72, 520)
41
66
  .apply();
42
67
 
43
- const updated = await pdf.getBytes();
44
- await fs.writeFile('output.pdf', updated);
68
+ // Persist the modified document
69
+ await pdf.save('output.pdf');
70
+ // or keep it in memory
71
+ const updatedBytes = await pdf.getBytes();
45
72
  }
46
73
 
47
74
  run().catch(console.error);
48
75
  ```
49
76
 
50
- ## Authentication & Configuration
77
+ ## Create a Blank PDF
78
+
79
+ ```typescript
80
+ import { PDFDancer, Color, StandardFonts } from 'pdfdancer-client-typescript';
81
+
82
+ async function createNew() {
83
+ const pdf = await PDFDancer.new('your-api-token');
84
+
85
+ await pdf.page(0).newParagraph()
86
+ .text('Quarterly Summary')
87
+ .font(StandardFonts.TIMES_BOLD, 18)
88
+ .color(new Color(10, 10, 80))
89
+ .lineSpacing(1.2)
90
+ .at(72, 730)
91
+ .apply();
92
+
93
+ await pdf.newImage()
94
+ .fromFile('logo.png')
95
+ .at(0, 420, 710)
96
+ .add();
97
+
98
+ await pdf.save('summary.pdf');
99
+ }
100
+
101
+ createNew().catch(console.error);
102
+ ```
103
+
104
+ ## Work with Forms and Layout
105
+
106
+ ```typescript
107
+ import { PDFDancer } from 'pdfdancer-client-typescript';
108
+
109
+ async function workWithForms() {
110
+ const pdf = await PDFDancer.open('contract.pdf');
111
+
112
+ // Inspect global document structure
113
+ const pages = await pdf.pages();
114
+ console.log('Total pages:', pages.length);
115
+
116
+ // Update form fields
117
+ const signature = (await pdf.selectFieldsByName('signature'))[0];
118
+ await signature.fill('Signed by Jane Doe');
119
+
120
+ // Trim or move content at specific coordinates
121
+ const images = await pdf.page(1).selectImages();
122
+ for (const image of images) {
123
+ const x = image.position.boundingRect?.x;
124
+ if (x !== undefined && x < 100) {
125
+ await image.delete();
126
+ }
127
+ }
128
+
129
+ await pdf.save('contract-updated.pdf');
130
+ }
131
+ ```
132
+
133
+ Selectors return typed objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, `PageRef`, …) with helpers such as `delete()`, `moveTo(x, y)`, or `edit()` depending on the object type.
134
+
135
+ ## Configuration
51
136
 
52
137
  ```typescript
53
138
  const pdf = await PDFDancer.open(
@@ -58,8 +143,9 @@ const pdf = await PDFDancer.open(
58
143
  );
59
144
  ```
60
145
 
61
- - Set `PDFDANCER_TOKEN` to avoid passing the token explicitly.
62
- - Override the API endpoint with `PDFDANCER_BASE_URL`.
146
+ - Set `PDFDANCER_TOKEN` for authentication (preferred for local development and CI).
147
+ - Override the API host with `PDFDANCER_BASE_URL` (e.g., sandbox environments).
148
+ - Tune HTTP read timeouts via the `timeout` argument on `PDFDancer.open()` and `PDFDancer.new()`.
63
149
  - Page indexes start at `0` throughout the API.
64
150
 
65
151
  ## Working with Pages
@@ -112,17 +198,46 @@ await pdf.page(0).newParagraph()
112
198
  const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
113
199
 
114
200
  if (para) {
115
- await para.edit()
201
+ const result = await para.edit()
116
202
  .replace('Awesomely\nObvious!')
117
203
  .font('Helvetica', 12)
118
204
  .color(new Color(0, 0, 0))
119
205
  .moveTo(280, 460)
120
206
  .apply();
207
+
208
+ // Check for warnings (e.g., embedded font modifications)
209
+ if (typeof result === 'object' && result.warning) {
210
+ console.warn('Operation warning:', result.warning);
211
+ }
121
212
  }
122
213
  ```
123
214
 
215
+ **Note:** When modifying text with embedded fonts, you may receive warnings. Embedded fonts have limited character sets, and modifying text may result in unrenderable characters. Consider using standard fonts when possible.
216
+
124
217
  `ParagraphBuilder` also supports `.fontFile(ttfBytes, size)` to register a custom font before applying.
125
218
 
219
+ ### Text Object Status
220
+
221
+ Text objects (paragraphs and lines) include status information about their font and modification state:
222
+
223
+ ```typescript
224
+ const lines = await pdf.page(0).selectTextLines();
225
+ const line = lines[0];
226
+
227
+ // Check text object status
228
+ const status = line.objectRef().status;
229
+ if (status) {
230
+ console.log('Font type:', status.getFontType()); // SYSTEM, STANDARD, or EMBEDDED
231
+ console.log('Is modified:', status.isModified()); // true if text was changed
232
+ console.log('Is encodable:', status.isEncodable()); // true if text can be rendered
233
+
234
+ // Get font recommendation if available
235
+ const recommendation = status.getFontRecommendation();
236
+ console.log('Recommended font:', recommendation.getFontName());
237
+ console.log('Similarity score:', recommendation.getSimilarityScore());
238
+ }
239
+ ```
240
+
126
241
  ## Working with Images
127
242
 
128
243
  ```typescript
@@ -164,6 +279,15 @@ await pdf.save('output.pdf'); // Node.js helper that writes the file
164
279
 
165
280
  ## Error Handling
166
281
 
282
+ Operations raise subclasses of `PdfDancerException`:
283
+
284
+ - `ValidationException`: input validation problems (missing token, invalid coordinates, etc.).
285
+ - `FontNotFoundException`: requested font unavailable on the service.
286
+ - `HttpClientException`: transport or server errors with detailed context.
287
+ - `SessionException`: session creation and lifecycle failures.
288
+
289
+ Wrap automated workflows in `try/catch` blocks to surface actionable errors to your users:
290
+
167
291
  ```typescript
168
292
  import {
169
293
  ValidationException,
@@ -221,6 +345,39 @@ try {
221
345
  - `CIRCLE` - Circular area with radius
222
346
  - `RECT` - Rectangular area with width and height
223
347
 
348
+ ### FontType
349
+
350
+ - `SYSTEM` - System fonts available on the local machine
351
+ - `STANDARD` - Standard PDF fonts (14 built-in fonts)
352
+ - `EMBEDDED` - Fonts embedded in the PDF document
353
+
354
+ ### Text Modification Results
355
+
356
+ When modifying text objects (paragraphs or lines), the operation returns a `CommandResult`:
357
+
358
+ ```typescript
359
+ interface CommandResult {
360
+ commandName: string; // Name of the operation
361
+ elementId: string | null; // ID of the affected element
362
+ message: string | null; // Optional status message
363
+ success: boolean; // Operation success status
364
+ warning: string | null; // Warning message (e.g., embedded font issues)
365
+ }
366
+ ```
367
+
368
+ ### Text Status
369
+
370
+ Text objects include status information via `TextStatus`:
371
+
372
+ ```typescript
373
+ interface TextStatus {
374
+ modified: boolean; // Whether text has been modified
375
+ encodable: boolean; // Whether text is encodable with current font
376
+ fontType: FontType; // Type of font being used
377
+ fontRecommendation: FontRecommendation; // Recommended alternative font
378
+ }
379
+ ```
380
+
224
381
  ## Development
225
382
 
226
383
  ```bash
@@ -259,6 +416,11 @@ The project includes comprehensive end-to-end tests. To run them:
259
416
 
260
417
  The e2e suite covers paragraphs, pages, text lines, images, form fields, and path manipulation scenarios.
261
418
 
419
+ ## Related SDKs
420
+
421
+ - Python client: https://github.com/MenschMachine/pdfdancer-client-python
422
+ - Java client: https://github.com/MenschMachine/pdfdancer-client-java
423
+
262
424
  ## License
263
425
 
264
- MIT
426
+ MIT © The Famous Cat Ltd.
@@ -1,18 +1,35 @@
1
1
  /**
2
2
  * PDF Assertions helper for e2e tests
3
3
  */
4
- import { Color, PDFDancer } from '../../index';
4
+ import { Color, Orientation, PDFDancer } from '../../index';
5
5
  export declare class PDFAssertions {
6
6
  private pdf;
7
7
  private constructor();
8
8
  static create(sourcePdf: PDFDancer): Promise<PDFAssertions>;
9
- assertTextHasColor(text: string, color: Color, page?: number): Promise<this>;
10
- assertTextHasFont(text: string, fontName: string, fontSize: number, page?: number): Promise<this>;
9
+ private aggregateCounts;
10
+ assertTotalNumberOfElements(expected: number, pageIndex?: number): Promise<this>;
11
+ assertPageCount(expected: number): Promise<this>;
12
+ assertPageDimension(width: number, height: number, orientation?: Orientation, pageIndex?: number): Promise<this>;
11
13
  assertParagraphIsAt(text: string, x: number, y: number, page?: number, epsilon?: number): Promise<this>;
14
+ assertTextHasFont(text: string, fontName: string, fontSize: number, page?: number): Promise<this>;
12
15
  assertTextHasFontMatching(text: string, fontName: string, fontSize: number, page?: number): Promise<this>;
16
+ assertTextHasColor(text: string, color: Color, page?: number): Promise<this>;
13
17
  assertTextlineHasColor(text: string, color: Color, page?: number): Promise<this>;
14
18
  assertTextlineHasFont(text: string, fontName: string, fontSize: number, page?: number): Promise<this>;
15
19
  assertTextlineHasFontMatching(text: string, fontName: string, fontSize: number, page?: number): Promise<this>;
16
20
  assertTextlineIsAt(text: string, x: number, y: number, page?: number, epsilon?: number): Promise<this>;
21
+ assertPathIsAt(internalId: string, x: number, y: number, page?: number, epsilon?: number): Promise<this>;
22
+ assertNoPathAt(x: number, y: number, page?: number): Promise<this>;
23
+ assertNumberOfPaths(expected: number, page?: number): Promise<this>;
24
+ assertNumberOfImages(expected: number, page?: number): Promise<this>;
25
+ assertImageAt(x: number, y: number, page?: number): Promise<this>;
26
+ assertNoImageAt(x: number, y: number, page?: number): Promise<this>;
27
+ assertImageWithIdAt(internalId: string, x: number, y: number, page?: number): Promise<this>;
28
+ assertNumberOfFormXObjects(expected: number, page?: number): Promise<this>;
29
+ assertNumberOfFormFields(expected: number, page?: number): Promise<this>;
30
+ assertParagraphExists(text: string, page?: number): Promise<this>;
31
+ assertParagraphDoesNotExist(text: string, page?: number): Promise<this>;
32
+ assertTextlineExists(text: string, page?: number): Promise<this>;
33
+ assertTextlineDoesNotExist(text: string, page?: number): Promise<this>;
17
34
  }
18
35
  //# sourceMappingURL=pdf-assertions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pdf-assertions.d.ts","sourceRoot":"","sources":["../../../src/__tests__/e2e/pdf-assertions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAAC,KAAK,EAAE,SAAS,EAAC,MAAM,aAAa,CAAC;AAG7C,qBAAa,aAAa;IACtB,OAAO,CAAC,GAAG,CAAY;IAEvB,OAAO;WAIM,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC;IAiB3D,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB/E,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAapG,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,EAAE,OAAO,GAAE,MAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBlH,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5G,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBnF,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxG,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhH,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,EAAE,OAAO,GAAE,MAAe,GAAG,OAAO,CAAC,IAAI,CAAC;CAiB1H"}
1
+ {"version":3,"file":"pdf-assertions.d.ts","sourceRoot":"","sources":["../../../src/__tests__/e2e/pdf-assertions.ts"],"names":[],"mappings":"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"}
@@ -2,44 +2,14 @@
2
2
  /**
3
3
  * PDF Assertions helper for e2e tests
4
4
  */
5
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- var desc = Object.getOwnPropertyDescriptor(m, k);
8
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
- desc = { enumerable: true, get: function() { return m[k]; } };
10
- }
11
- Object.defineProperty(o, k2, desc);
12
- }) : (function(o, m, k, k2) {
13
- if (k2 === undefined) k2 = k;
14
- o[k2] = m[k];
15
- }));
16
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
- Object.defineProperty(o, "default", { enumerable: true, value: v });
18
- }) : function(o, v) {
19
- o["default"] = v;
20
- });
21
- var __importStar = (this && this.__importStar) || (function () {
22
- var ownKeys = function(o) {
23
- ownKeys = Object.getOwnPropertyNames || function (o) {
24
- var ar = [];
25
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
- return ar;
27
- };
28
- return ownKeys(o);
29
- };
30
- return function (mod) {
31
- if (mod && mod.__esModule) return mod;
32
- var result = {};
33
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
- __setModuleDefault(result, mod);
35
- return result;
36
- };
37
- })();
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
38
8
  Object.defineProperty(exports, "__esModule", { value: true });
39
9
  exports.PDFAssertions = void 0;
40
- const fs = __importStar(require("fs"));
41
- const os = __importStar(require("os"));
42
- const path = __importStar(require("path"));
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const os_1 = __importDefault(require("os"));
12
+ const path_1 = __importDefault(require("path"));
43
13
  const index_1 = require("../../index");
44
14
  const assertions_1 = require("../assertions");
45
15
  class PDFAssertions {
@@ -52,94 +22,189 @@ class PDFAssertions {
52
22
  const token = sourcePdf._token;
53
23
  const baseUrl = sourcePdf._baseUrl;
54
24
  // Create a temporary file
55
- const tempFile = path.join(os.tmpdir(), `test-${Date.now()}.pdf`);
25
+ const tempFile = path_1.default.join(os_1.default.tmpdir(), `test-${Date.now()}.pdf`);
56
26
  await sourcePdf.save(tempFile);
57
27
  // Reopen the PDF with the same token and baseUrl
58
- const pdfData = fs.readFileSync(tempFile);
28
+ const pdfData = fs_1.default.readFileSync(tempFile);
59
29
  const pdf = await index_1.PDFDancer.open(new Uint8Array(pdfData), token, baseUrl);
60
30
  return new PDFAssertions(pdf);
61
31
  }
62
- async assertTextHasColor(text, color, page = 0) {
63
- await this.assertTextlineHasColor(text, color, page);
64
- const paragraphs = await this.pdf.page(page).selectParagraphsMatching(text);
65
- expect(paragraphs).toHaveLength(1);
66
- const reference = paragraphs[0].ref();
67
- expect(reference.text).toContain(text);
68
- // Compare RGB values, alpha channel may differ
69
- expect(reference.color.r).toBe(color.r);
70
- expect(reference.color.g).toBe(color.g);
71
- expect(reference.color.b).toBe(color.b);
32
+ async aggregateCounts(pageIndex) {
33
+ const [paragraphs, textLines, images, paths, forms, fields] = await Promise.all([
34
+ this.pdf.selectParagraphs(),
35
+ this.pdf.selectLines(),
36
+ this.pdf.selectImages(),
37
+ this.pdf.selectPaths(),
38
+ this.pdf.selectForms(),
39
+ this.pdf.selectFormFields()
40
+ ]);
41
+ const filter = (items) => {
42
+ if (pageIndex === undefined) {
43
+ return items;
44
+ }
45
+ return items.filter(item => item.position.pageIndex === pageIndex);
46
+ };
47
+ return {
48
+ paragraphs: filter(paragraphs).length,
49
+ textLines: filter(textLines).length,
50
+ images: filter(images).length,
51
+ paths: filter(paths).length,
52
+ forms: filter(forms).length,
53
+ fields: filter(fields).length
54
+ };
55
+ }
56
+ async assertTotalNumberOfElements(expected, pageIndex) {
57
+ const totals = await this.aggregateCounts(pageIndex);
58
+ const actual = Object.values(totals).reduce((acc, count) => acc + count, 0);
59
+ expect(actual).toBe(expected);
72
60
  return this;
73
61
  }
74
- async assertTextHasFont(text, fontName, fontSize, page = 0) {
75
- await this.assertTextlineHasFont(text, fontName, fontSize, page);
62
+ async assertPageCount(expected) {
63
+ const pages = await this.pdf.pages();
64
+ expect(pages.length).toBe(expected);
65
+ return this;
66
+ }
67
+ async assertPageDimension(width, height, orientation, pageIndex = 0) {
68
+ const pages = await this.pdf.pages();
69
+ expect(pageIndex).toBeLessThan(pages.length);
70
+ const page = pages[pageIndex];
71
+ expect(page.pageSize?.width).toBeCloseTo(width, 6);
72
+ expect(page.pageSize?.height).toBeCloseTo(height, 6);
73
+ if (orientation) {
74
+ expect(page.orientation).toBe(orientation);
75
+ }
76
+ return this;
77
+ }
78
+ async assertParagraphIsAt(text, x, y, page = 0, epsilon = 1e-4) {
76
79
  const paragraphs = await this.pdf.page(page).selectParagraphsMatching(`.*${text}.*`);
77
- expect(paragraphs).toHaveLength(1);
78
- const reference = paragraphs[0].ref();
79
- expect(reference.fontName).toBe(fontName);
80
- expect(reference.fontSize).toBe(fontSize);
80
+ expect(paragraphs.length).toBeGreaterThan(0);
81
+ const reference = paragraphs[0].objectRef();
82
+ (0, assertions_1.expectWithin)(reference.position.getX(), x, epsilon);
83
+ (0, assertions_1.expectWithin)(reference.position.getY(), y, epsilon);
84
+ const byPosition = await this.pdf.page(page).selectParagraphsAt(x, y);
85
+ expect(byPosition.length).toBeGreaterThan(0);
81
86
  return this;
82
87
  }
83
- async assertParagraphIsAt(text, x, y, page = 0, epsilon = 0.0001) {
88
+ async assertTextHasFont(text, fontName, fontSize, page = 0) {
89
+ await this.assertTextlineHasFont(text, fontName, fontSize, page);
84
90
  const paragraphs = await this.pdf.page(page).selectParagraphsMatching(`.*${text}.*`);
85
- expect(paragraphs.length).toBe(1);
86
- const reference = paragraphs[0].ref();
87
- const posX = reference.position.getX();
88
- const posY = reference.position.getY();
89
- (0, assertions_1.expectWithin)(posX, x, epsilon);
90
- (0, assertions_1.expectWithin)(posY, y, epsilon);
91
- const paragraphByPosition = await this.pdf.page(page).selectParagraphsAt(x, y);
92
- expect(paragraphByPosition.length).toBeGreaterThan(0);
93
- expect(paragraphs[0].internalId).toBe(paragraphByPosition[0].internalId);
91
+ expect(paragraphs.length).toBeGreaterThan(0);
92
+ const reference = paragraphs[0].objectRef();
93
+ expect(reference.fontName).toBe(fontName);
94
+ (0, assertions_1.expectWithin)(reference.fontSize, fontSize, 1e-6);
94
95
  return this;
95
96
  }
96
97
  async assertTextHasFontMatching(text, fontName, fontSize, page = 0) {
97
98
  await this.assertTextlineHasFontMatching(text, fontName, fontSize, page);
98
- const paragraphs = await this.pdf.page(page).selectParagraphsMatching(`.*${text}.*`);
99
- expect(paragraphs).toHaveLength(1);
100
- const reference = paragraphs[0].ref();
101
- expect(reference.fontName).toContain(fontName);
102
- expect(reference.fontSize).toBe(fontSize);
99
+ return this;
100
+ }
101
+ async assertTextHasColor(text, color, page = 0) {
102
+ await this.assertTextlineHasColor(text, color, page);
103
103
  return this;
104
104
  }
105
105
  async assertTextlineHasColor(text, color, page = 0) {
106
106
  const lines = await this.pdf.page(page).selectTextLinesMatching(text);
107
107
  expect(lines.length).toBe(1);
108
- const reference = lines[0].ref();
109
- // Compare RGB values, alpha channel may differ
110
- expect(reference.color.r).toBe(color.r);
111
- expect(reference.color.g).toBe(color.g);
112
- expect(reference.color.b).toBe(color.b);
108
+ const reference = lines[0].objectRef();
109
+ const refColor = reference.color;
110
+ expect(refColor).toEqual(color);
113
111
  expect(reference.text).toContain(text);
114
112
  return this;
115
113
  }
116
114
  async assertTextlineHasFont(text, fontName, fontSize, page = 0) {
117
115
  const lines = await this.pdf.page(page).selectTextLinesStartingWith(text);
118
116
  expect(lines.length).toBe(1);
119
- const reference = lines[0].ref();
117
+ const reference = lines[0].objectRef();
120
118
  expect(reference.fontName).toBe(fontName);
121
- expect(reference.fontSize).toBe(fontSize);
119
+ (0, assertions_1.expectWithin)(reference.fontSize, fontSize, 1e-6);
122
120
  return this;
123
121
  }
124
122
  async assertTextlineHasFontMatching(text, fontName, fontSize, page = 0) {
125
123
  const lines = await this.pdf.page(page).selectTextLinesStartingWith(text);
126
124
  expect(lines.length).toBe(1);
127
- const reference = lines[0].ref();
128
- expect(reference.fontName).toContain(fontName);
129
- expect(reference.fontSize).toBe(fontSize);
125
+ const reference = lines[0].objectRef();
126
+ expect((reference.fontName ?? '').includes(fontName)).toBe(true);
127
+ (0, assertions_1.expectWithin)(reference.fontSize, fontSize, 1e-6);
130
128
  return this;
131
129
  }
132
- async assertTextlineIsAt(text, x, y, page = 0, epsilon = 0.0001) {
130
+ async assertTextlineIsAt(text, x, y, page = 0, epsilon = 1e-4) {
133
131
  const lines = await this.pdf.page(page).selectTextLinesStartingWith(text);
134
132
  expect(lines.length).toBe(1);
135
- const reference = lines[0].ref();
136
- const posX = reference.position.getX();
137
- const posY = reference.position.getY();
138
- (0, assertions_1.expectWithin)(posX, x, epsilon);
139
- (0, assertions_1.expectWithin)(posY, y, epsilon);
133
+ const reference = lines[0].objectRef();
134
+ (0, assertions_1.expectWithin)(reference.position.getX(), x, epsilon);
135
+ (0, assertions_1.expectWithin)(reference.position.getY(), y, epsilon);
140
136
  const byPosition = await this.pdf.page(page).selectTextLinesAt(x, y);
141
137
  expect(byPosition.length).toBeGreaterThan(0);
142
- expect(lines[0].internalId).toBe(byPosition[0].internalId);
138
+ return this;
139
+ }
140
+ async assertPathIsAt(internalId, x, y, page = 0, epsilon = 1e-4) {
141
+ const paths = await this.pdf.page(page).selectPathsAt(x, y);
142
+ expect(paths.length).toBe(1);
143
+ const reference = paths[0];
144
+ expect(reference.internalId).toBe(internalId);
145
+ (0, assertions_1.expectWithin)(reference.position.getX(), x, epsilon);
146
+ (0, assertions_1.expectWithin)(reference.position.getY(), y, epsilon);
147
+ return this;
148
+ }
149
+ async assertNoPathAt(x, y, page = 0) {
150
+ const paths = await this.pdf.page(page).selectPathsAt(x, y);
151
+ expect(paths.length).toBe(0);
152
+ return this;
153
+ }
154
+ async assertNumberOfPaths(expected, page) {
155
+ const paths = page === undefined ? await this.pdf.selectPaths() : await this.pdf.page(page).selectPaths();
156
+ expect(paths.length).toBe(expected);
157
+ return this;
158
+ }
159
+ async assertNumberOfImages(expected, page) {
160
+ const images = page === undefined ? await this.pdf.selectImages() : await this.pdf.page(page).selectImages();
161
+ expect(images.length).toBe(expected);
162
+ return this;
163
+ }
164
+ async assertImageAt(x, y, page = 0) {
165
+ const images = await this.pdf.page(page).selectImagesAt(x, y);
166
+ expect(images.length).toBe(1);
167
+ return this;
168
+ }
169
+ async assertNoImageAt(x, y, page = 0) {
170
+ const images = await this.pdf.page(page).selectImagesAt(x, y);
171
+ expect(images.length).toBe(0);
172
+ return this;
173
+ }
174
+ async assertImageWithIdAt(internalId, x, y, page = 0) {
175
+ const images = await this.pdf.page(page).selectImagesAt(x, y);
176
+ expect(images.length).toBe(1);
177
+ expect(images[0].internalId).toBe(internalId);
178
+ return this;
179
+ }
180
+ async assertNumberOfFormXObjects(expected, page) {
181
+ const forms = page === undefined ? await this.pdf.selectForms() : await this.pdf.page(page).selectForms();
182
+ expect(forms.length).toBe(expected);
183
+ return this;
184
+ }
185
+ async assertNumberOfFormFields(expected, page) {
186
+ const fields = page === undefined ? await this.pdf.selectFormFields() : await this.pdf.page(page).selectFormFields();
187
+ expect(fields.length).toBe(expected);
188
+ return this;
189
+ }
190
+ async assertParagraphExists(text, page = 0) {
191
+ const paragraphs = await this.pdf.page(page).selectParagraphsStartingWith(text);
192
+ expect(paragraphs.length).toBeGreaterThan(0);
193
+ return this;
194
+ }
195
+ async assertParagraphDoesNotExist(text, page = 0) {
196
+ const paragraphs = await this.pdf.page(page).selectParagraphsStartingWith(text);
197
+ expect(paragraphs.length).toBe(0);
198
+ return this;
199
+ }
200
+ async assertTextlineExists(text, page = 0) {
201
+ const lines = await this.pdf.page(page).selectTextLinesStartingWith(text);
202
+ expect(lines.length).toBeGreaterThan(0);
203
+ return this;
204
+ }
205
+ async assertTextlineDoesNotExist(text, page = 0) {
206
+ const lines = await this.pdf.page(page).selectTextLinesStartingWith(text);
207
+ expect(lines.length).toBe(0);
143
208
  return this;
144
209
  }
145
210
  }