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.
- package/README.md +189 -27
- package/dist/__tests__/e2e/pdf-assertions.d.ts +20 -3
- package/dist/__tests__/e2e/pdf-assertions.d.ts.map +1 -1
- package/dist/__tests__/e2e/pdf-assertions.js +151 -86
- package/dist/__tests__/e2e/pdf-assertions.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +106 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +195 -2
- package/dist/models.js.map +1 -1
- package/dist/paragraph-builder.d.ts +2 -2
- package/dist/paragraph-builder.d.ts.map +1 -1
- package/dist/paragraph-builder.js +10 -2
- package/dist/paragraph-builder.js.map +1 -1
- package/dist/pdfdancer_v1.d.ts +41 -4
- package/dist/pdfdancer_v1.d.ts.map +1 -1
- package/dist/pdfdancer_v1.js +198 -20
- package/dist/pdfdancer_v1.js.map +1 -1
- package/dist/types.d.ts +15 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +41 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/e2e/acroform.test.ts +13 -0
- package/src/__tests__/e2e/create-new.test.ts +133 -0
- package/src/__tests__/e2e/form_x_object.test.ts +8 -0
- package/src/__tests__/e2e/image.test.ts +30 -18
- package/src/__tests__/e2e/line.test.ts +33 -6
- package/src/__tests__/e2e/page.test.ts +4 -0
- package/src/__tests__/e2e/paragraph.test.ts +29 -2
- package/src/__tests__/e2e/path.test.ts +8 -0
- package/src/__tests__/e2e/pdf-assertions.ts +164 -73
- package/src/__tests__/url-builder.test.ts +44 -0
- package/src/index.ts +8 -1
- package/src/models.ts +255 -1
- package/src/paragraph-builder.ts +13 -5
- package/src/pdfdancer_v1.ts +277 -28
- package/src/types.ts +55 -3
package/README.md
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
# PDFDancer TypeScript Client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Getting Started with PDFDancer**
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
55
|
+
const result = await heading.edit()
|
|
56
|
+
.replace('Overview')
|
|
57
|
+
.apply();
|
|
34
58
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
.
|
|
38
|
-
.
|
|
39
|
-
.
|
|
40
|
-
.
|
|
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
|
-
|
|
44
|
-
await
|
|
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
|
-
##
|
|
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`
|
|
62
|
-
- Override the API
|
|
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
|
-
|
|
10
|
-
|
|
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;
|
|
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
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
41
|
-
const
|
|
42
|
-
const
|
|
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 =
|
|
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 =
|
|
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
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
75
|
-
await this.
|
|
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).
|
|
78
|
-
const reference = paragraphs[0].
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
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).
|
|
86
|
-
const reference = paragraphs[0].
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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].
|
|
109
|
-
|
|
110
|
-
expect(
|
|
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].
|
|
117
|
+
const reference = lines[0].objectRef();
|
|
120
118
|
expect(reference.fontName).toBe(fontName);
|
|
121
|
-
|
|
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].
|
|
128
|
-
expect(reference.fontName).
|
|
129
|
-
|
|
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 =
|
|
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].
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
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
|
}
|