pdf-visual-compare 3.5.0 → 4.0.0
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 +326 -72
- package/out/comparePdf.d.ts +48 -6
- package/out/comparePdf.js +44 -86
- package/out/const.d.ts +2 -2
- package/out/const.js +5 -3
- package/out/errors/ComparePdfComparisonError.d.ts +6 -0
- package/out/errors/ComparePdfComparisonError.js +10 -0
- package/out/errors/ComparePdfConfigurationError.d.ts +6 -0
- package/out/errors/ComparePdfConfigurationError.js +10 -0
- package/out/errors/ComparePdfError.d.ts +6 -0
- package/out/errors/ComparePdfError.js +13 -0
- package/out/errors/ComparePdfInputError.d.ts +6 -0
- package/out/errors/ComparePdfInputError.js +10 -0
- package/out/errors/ComparePdfRenderingError.d.ts +6 -0
- package/out/errors/ComparePdfRenderingError.js +10 -0
- package/out/index.d.ts +16 -2
- package/out/index.js +14 -2
- package/out/internal/adapters/comparePngOptions.d.ts +3 -0
- package/out/internal/adapters/comparePngOptions.js +41 -0
- package/out/internal/adapters/pdfRenderOptions.d.ts +3 -0
- package/out/internal/adapters/pdfRenderOptions.js +19 -0
- package/out/internal/comparePlannedPage.d.ts +3 -0
- package/out/internal/comparePlannedPage.js +72 -0
- package/out/internal/diffOutputGuards.d.ts +56 -0
- package/out/internal/diffOutputGuards.js +176 -0
- package/out/internal/normalizeComparisonOptions.d.ts +2 -0
- package/out/internal/normalizeComparisonOptions.js +104 -0
- package/out/internal/normalizePdfInput.d.ts +13 -0
- package/out/internal/normalizePdfInput.js +110 -0
- package/out/internal/pageComparisonPlan.d.ts +4 -0
- package/out/internal/pageComparisonPlan.js +35 -0
- package/out/internal/renderOutputFolderGuards.d.ts +22 -0
- package/out/internal/renderOutputFolderGuards.js +70 -0
- package/out/internal/renderPdfPages.d.ts +10 -0
- package/out/internal/renderPdfPages.js +105 -0
- package/out/internal/securePath.d.ts +34 -0
- package/out/internal/securePath.js +75 -0
- package/out/internal/types.d.ts +26 -0
- package/out/internal/types.js +2 -0
- package/out/types/ComparePdfDetailedResult.d.ts +35 -0
- package/out/types/ComparePdfDetailedResult.js +2 -0
- package/out/types/ComparePdfOptions.d.ts +33 -11
- package/out/types/ComparePdfPageResult.d.ts +42 -0
- package/out/types/ComparePdfPageResult.js +2 -0
- package/out/types/ComparePdfPageStatus.d.ts +4 -0
- package/out/types/ComparePdfPageStatus.js +2 -0
- package/out/types/ExcludedPageArea.d.ts +3 -39
- package/out/types/PageArea.d.ts +13 -0
- package/out/types/PageArea.js +2 -0
- package/out/types/PageExclusion.d.ts +46 -0
- package/out/types/PageExclusion.js +2 -0
- package/out/types/PdfInput.d.ts +11 -0
- package/out/types/PdfInput.js +2 -0
- package/out/types/PdfRenderOptions.d.ts +51 -0
- package/out/types/PdfRenderOptions.js +2 -0
- package/out/types/RgbColor.d.ts +11 -0
- package/out/types/RgbColor.js +2 -0
- package/package.json +86 -75
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# pdf-visual-compare
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<strong>Visual regression testing library for PDFs in JavaScript/TypeScript without
|
|
4
|
+
<strong>Visual regression testing library for PDFs in JavaScript/TypeScript without external system package dependencies</strong>
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
@@ -26,20 +26,35 @@
|
|
|
26
26
|
- [Requirements](#requirements)
|
|
27
27
|
- [Installation](#installation)
|
|
28
28
|
- [Usage](#usage)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
- [Basic comparison](#basic-comparison)
|
|
30
|
+
- [Detailed comparison results](#detailed-comparison-results)
|
|
31
|
+
- [Comparison with options](#comparison-with-options)
|
|
32
|
+
- [Comparing PDF buffers](#comparing-pdf-buffers)
|
|
32
33
|
- [API](#api)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
- [comparePdf](#comparepdfactualpdf-expectedpdf-options)
|
|
35
|
+
- [comparePdfDetailed](#comparepdfdetailedactualpdf-expectedpdf-options)
|
|
36
|
+
- [ComparePdfOptions](#comparepdfoptions)
|
|
37
|
+
- [ComparePdfDetailedResult](#comparepdfdetailedresult)
|
|
38
|
+
- [ComparePdfPageResult](#comparepdfpageresult)
|
|
39
|
+
- [ComparePdfPageStatus](#comparepdfpagestatus)
|
|
40
|
+
- [PdfRenderOptions](#pdfrenderoptions)
|
|
41
|
+
- [PageExclusion](#pageexclusion)
|
|
42
|
+
- [PageArea](#pagearea)
|
|
43
|
+
- [RgbColor](#rgbcolor)
|
|
36
44
|
- [Support](#support)
|
|
37
45
|
|
|
38
46
|
---
|
|
39
47
|
|
|
40
48
|
## Requirements
|
|
41
49
|
|
|
42
|
-
- Node.js >=
|
|
50
|
+
- Node.js >= 24
|
|
51
|
+
- Supported and CI-validated runtimes: Linux and macOS
|
|
52
|
+
- Windows is not supported
|
|
53
|
+
|
|
54
|
+
## Docker
|
|
55
|
+
|
|
56
|
+
The Docker image is for local/containerized test execution only. It builds a slim test runner that
|
|
57
|
+
installs just the dependencies needed to execute Vitest against the checked-in source and fixtures.
|
|
43
58
|
|
|
44
59
|
---
|
|
45
60
|
|
|
@@ -49,6 +64,31 @@
|
|
|
49
64
|
npm install -D pdf-visual-compare
|
|
50
65
|
```
|
|
51
66
|
|
|
67
|
+
`pdf-visual-compare` now depends on `pdf-to-png-converter` 4.x, which ships prebuilt native canvas
|
|
68
|
+
bindings through `@napi-rs/canvas`. No external system packages are required, but consumers must use
|
|
69
|
+
Node 24 or newer.
|
|
70
|
+
|
|
71
|
+
## Validation
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
npm run test:types # Type-check all repository TypeScript, including the published-surface fixture
|
|
75
|
+
npm run test:artifacts # Verify the built package entry points and npm pack contents
|
|
76
|
+
npm test # Full pipeline: clean → lint → license check → build → type/artifact checks → vitest --coverage
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`npm run test:types` includes the published-surface fixture under `__tests__/published-artifacts/`,
|
|
80
|
+
so it still expects `./out/` to be up to date. `npm test` handles that automatically.
|
|
81
|
+
|
|
82
|
+
## Repository merge policy
|
|
83
|
+
|
|
84
|
+
For pull requests targeting `main`, the required GitHub status checks are:
|
|
85
|
+
|
|
86
|
+
- `test (ubuntu-24.04)`
|
|
87
|
+
- `test (macos-15)`
|
|
88
|
+
|
|
89
|
+
Those check names come from the matrix jobs in the `CI` workflow (`.github/workflows/test.yml`).
|
|
90
|
+
See [`CONTRIBUTING.md`](./CONTRIBUTING.md) for the contributor workflow and merge expectations.
|
|
91
|
+
|
|
52
92
|
---
|
|
53
93
|
|
|
54
94
|
## Usage
|
|
@@ -63,54 +103,87 @@ const isEqual = await comparePdf('./actual.pdf', './expected.pdf');
|
|
|
63
103
|
// false → PDFs differ
|
|
64
104
|
```
|
|
65
105
|
|
|
106
|
+
### Detailed comparison results
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { comparePdfDetailed } from 'pdf-visual-compare';
|
|
110
|
+
|
|
111
|
+
const result = await comparePdfDetailed('./actual.pdf', './expected.pdf', {
|
|
112
|
+
compareThreshold: 25,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
console.log(result.isEqual);
|
|
116
|
+
console.log(result.actualPageCount, result.expectedPageCount);
|
|
117
|
+
console.log(result.pages[0]);
|
|
118
|
+
```
|
|
119
|
+
|
|
66
120
|
### Comparison with options
|
|
67
121
|
|
|
68
122
|
```typescript
|
|
123
|
+
import type { ComparePdfOptions } from 'pdf-visual-compare';
|
|
69
124
|
import { comparePdf } from 'pdf-visual-compare';
|
|
70
125
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
126
|
+
const options: ComparePdfOptions = {
|
|
127
|
+
// Enable diff PNG output explicitly. Default: false
|
|
128
|
+
writeDiffs: true,
|
|
129
|
+
|
|
130
|
+
// Trusted root folder for diff PNG images written when differences are found.
|
|
131
|
+
// Generated diff paths and per-page diffFilePath overrides must stay inside this folder.
|
|
132
|
+
// If the path already exists, it must be a directory.
|
|
133
|
+
// Default: ./comparePdfOutput
|
|
134
|
+
diffsOutputFolder: 'test-results/diffs',
|
|
135
|
+
|
|
136
|
+
// Optional trust boundary for string PDF paths. When set, both actualPdf and expectedPdf
|
|
137
|
+
// must resolve inside this directory or comparePdf throws ComparePdfConfigurationError.
|
|
138
|
+
allowedInputRoot: '.',
|
|
139
|
+
|
|
140
|
+
// Maximum number of differing pixels allowed before the comparison fails.
|
|
141
|
+
// 0 = pixel-perfect match required (default). Must be a finite non-negative integer.
|
|
142
|
+
compareThreshold: 200,
|
|
143
|
+
|
|
144
|
+
// Per-page exclusion zones, matched by the `pageNumber` field (1-based).
|
|
145
|
+
// `pageNumber: 1` → first page, `pageNumber: 2` → second page, etc.
|
|
146
|
+
// Pixel coordinates are relative to the rendered PNG at the configured viewportScale.
|
|
147
|
+
excludedAreas: [
|
|
148
|
+
{
|
|
149
|
+
pageNumber: 1,
|
|
150
|
+
excludedAreas: [{ x1: 700, y1: 375, x2: 790, y2: 400 }],
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
pageNumber: 2,
|
|
154
|
+
excludedAreas: [{ x1: 680, y1: 240, x2: 955, y2: 465 }],
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
|
|
158
|
+
pdfToPngConvertOptions: {
|
|
159
|
+
viewportScale: 2.0,
|
|
160
|
+
disableFontFace: true,
|
|
161
|
+
useSystemFonts: false,
|
|
162
|
+
enableXfa: false,
|
|
163
|
+
pdfFilePassword: 'pa$$word',
|
|
164
|
+
// Renderer intermediate files are written under output/pngs/actual and output/pngs/expected.
|
|
165
|
+
outputFolder: 'output/pngs',
|
|
166
|
+
outputFileMaskFunc: (pageNumber) => `page_${pageNumber}.png`,
|
|
167
|
+
pagesToProcess: [1, 3],
|
|
168
|
+
verbosityLevel: 0,
|
|
94
169
|
},
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
viewportScale: 2.0, // Rendering scale — higher means more detail. Default: 2.0.
|
|
99
|
-
disableFontFace: true, // Use built-in font renderer. Default: true.
|
|
100
|
-
useSystemFonts: false, // Fall back to system fonts for non-embedded fonts. Default: false.
|
|
101
|
-
enableXfa: false, // Enable XFA form rendering. Default: false.
|
|
102
|
-
pdfFilePassword: 'pa$$word', // Password for encrypted PDFs.
|
|
103
|
-
outputFolder: 'output/pngs', // Save intermediate PNG files here. Omit to keep in memory only.
|
|
104
|
-
outputFileMaskFunc: (pageNumber) => `page_${pageNumber}.png`, // Custom PNG filename.
|
|
105
|
-
pagesToProcess: [1, 3], // Limit comparison to specific pages (1-based). Default: all pages.
|
|
106
|
-
verbosityLevel: 0, // 0 = errors only, 1 = warnings, 5 = info. Default: 0.
|
|
107
|
-
},
|
|
108
|
-
});
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const isEqual = await comparePdf('./actual.pdf', './expected.pdf', options);
|
|
109
173
|
```
|
|
110
174
|
|
|
111
|
-
### Comparing PDF
|
|
175
|
+
### Comparing PDF binary inputs
|
|
176
|
+
|
|
177
|
+
`comparePdf` accepts file paths plus these binary inputs: `Buffer`, `ArrayBuffer`, and
|
|
178
|
+
`SharedArrayBuffer`. `SharedArrayBuffer` inputs are normalized internally before rendering.
|
|
179
|
+
|
|
180
|
+
String paths are intended for trusted local usage by default. If you need to accept caller-provided
|
|
181
|
+
path strings, set `allowedInputRoot` to constrain them to a specific workspace, or prefer binary
|
|
182
|
+
inputs instead.
|
|
112
183
|
|
|
113
|
-
|
|
184
|
+
Diff PNGs are written only when `writeDiffs: true`. When enabled, treat `diffsOutputFolder` as a
|
|
185
|
+
trusted write root and keep any `diffFilePath` overrides inside that directory. This boundary
|
|
186
|
+
assumes a trusted local filesystem while the comparison is running.
|
|
114
187
|
|
|
115
188
|
```typescript
|
|
116
189
|
import { readFileSync } from 'node:fs';
|
|
@@ -122,43 +195,224 @@ const expected = readFileSync('./expected.pdf');
|
|
|
122
195
|
const isEqual = await comparePdf(actual, expected);
|
|
123
196
|
```
|
|
124
197
|
|
|
198
|
+
```typescript
|
|
199
|
+
import { readFileSync } from 'node:fs';
|
|
200
|
+
import { comparePdf, type PdfInput } from 'pdf-visual-compare';
|
|
201
|
+
|
|
202
|
+
const actualBuffer = readFileSync('./actual.pdf');
|
|
203
|
+
const expectedBuffer = readFileSync('./expected.pdf');
|
|
204
|
+
|
|
205
|
+
const actual: PdfInput = actualBuffer.buffer.slice(
|
|
206
|
+
actualBuffer.byteOffset,
|
|
207
|
+
actualBuffer.byteOffset + actualBuffer.byteLength,
|
|
208
|
+
);
|
|
209
|
+
const expected: PdfInput = expectedBuffer.buffer.slice(
|
|
210
|
+
expectedBuffer.byteOffset,
|
|
211
|
+
expectedBuffer.byteOffset + expectedBuffer.byteLength,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const isEqual = await comparePdf(actual, expected);
|
|
215
|
+
```
|
|
216
|
+
|
|
125
217
|
---
|
|
126
218
|
|
|
127
219
|
## API
|
|
128
220
|
|
|
129
221
|
### `comparePdf(actualPdf, expectedPdf, options?)`
|
|
130
222
|
|
|
131
|
-
| Parameter | Type
|
|
132
|
-
| ------------- |
|
|
133
|
-
| `actualPdf` | `
|
|
134
|
-
| `expectedPdf` | `
|
|
135
|
-
| `options` | `ComparePdfOptions` _(optional)_
|
|
223
|
+
| Parameter | Type | Description |
|
|
224
|
+
| ------------- | -------------------------------- | -------------------------------------------------- |
|
|
225
|
+
| `actualPdf` | `PdfInput` | File path or supported binary PDF input under test |
|
|
226
|
+
| `expectedPdf` | `PdfInput` | File path or supported binary reference PDF |
|
|
227
|
+
| `options` | `ComparePdfOptions` _(optional)_ | Comparison configuration |
|
|
228
|
+
|
|
229
|
+
Returns `Promise<boolean>` — a backwards-compatible convenience wrapper over
|
|
230
|
+
`comparePdfDetailed()` that resolves to `result.isEqual`.
|
|
231
|
+
|
|
232
|
+
Rendered pages are paired by the renderer-reported `pageNumber` (1-based), not by generated PNG
|
|
233
|
+
file names or by the position of entries inside `excludedAreas`. If one side is missing a rendered
|
|
234
|
+
counterpart for a page number in the comparison plan, the comparison returns `false`.
|
|
136
235
|
|
|
137
|
-
|
|
236
|
+
The library discovers the page plan first, then renders and compares one page at a time to keep
|
|
237
|
+
memory bounded on multi-page PDFs. `png-visual-compare` 6.x now exposes additional async/ported
|
|
238
|
+
comparison entry points, but this library still uses sequential `comparePng()` calls until a
|
|
239
|
+
benchmark and dependency-level safety review justify parallel execution.
|
|
240
|
+
|
|
241
|
+
String inputs are trusted caller-controlled file paths unless `options.allowedInputRoot` is set. If
|
|
242
|
+
`allowedInputRoot` is configured, both string inputs must resolve within that directory. After
|
|
243
|
+
boundary validation, string inputs are opened and read immediately so rendering operates on bytes
|
|
244
|
+
instead of reopening caller paths later.
|
|
245
|
+
|
|
246
|
+
When `options.writeDiffs` is `true`, diff outputs are written under `options.diffsOutputFolder`,
|
|
247
|
+
which acts as a trusted write root. Auto-generated diff paths and any
|
|
248
|
+
`excludedAreas[].diffFilePath` overrides must resolve within that directory. This boundary assumes a
|
|
249
|
+
trusted local filesystem while the comparison is running. If `diffsOutputFolder` is provided, it is
|
|
250
|
+
still validated even when `writeDiffs` is `false`.
|
|
138
251
|
|
|
139
252
|
**Throws:**
|
|
140
|
-
|
|
141
|
-
- `
|
|
142
|
-
- `
|
|
253
|
+
|
|
254
|
+
- `ComparePdfInputError: PDF file not found: <path>` — when a string argument points to a non-existent file.
|
|
255
|
+
- `ComparePdfInputError: PDF path is not a file: <path>` — when a string argument points to an existing directory or other non-file path.
|
|
256
|
+
- `ComparePdfInputError: Failed to read PDF file: <path>` — when a string argument exists but the library cannot open or read it. The original filesystem exception is attached as `cause`.
|
|
257
|
+
- `ComparePdfInputError: Unknown input file type.` — when an argument is not a `string`, `Buffer`, `ArrayBuffer`, or `SharedArrayBuffer`.
|
|
258
|
+
- `ComparePdfConfigurationError: Options must be an object.` — when an untyped caller passes a non-object third argument.
|
|
259
|
+
- `ComparePdfConfigurationError: diffsOutputFolder must be a non-empty string.` — when an untyped caller passes a non-string or blank diff root.
|
|
260
|
+
- `ComparePdfConfigurationError: diffsOutputFolder must point to a directory when it already exists: <path>` — when the configured diff root already exists as a file.
|
|
261
|
+
- `ComparePdfConfigurationError: allowedInputRoot must be a non-empty string.` — when an untyped caller passes a non-string or blank path boundary.
|
|
262
|
+
- `ComparePdfConfigurationError: allowedInputRoot does not exist: <path>` — when `allowedInputRoot` points to a missing path.
|
|
263
|
+
- `ComparePdfConfigurationError: allowedInputRoot must point to an existing directory: <path>` — when `allowedInputRoot` points to a file instead of a directory.
|
|
264
|
+
- `ComparePdfConfigurationError: actualPdf must resolve within allowedInputRoot: <path>` / `expectedPdf must resolve within allowedInputRoot: <path>` — when a string PDF input escapes the configured root.
|
|
265
|
+
- `ComparePdfConfigurationError: excludedAreas must be an array.` — when an untyped caller passes a non-array `excludedAreas` value.
|
|
266
|
+
- `ComparePdfConfigurationError: Each excludedAreas entry must be an object.` — when an untyped caller passes a non-object item inside `excludedAreas`.
|
|
267
|
+
- `ComparePdfConfigurationError: Diff output path must stay within diffsOutputFolder: <path>` — when an auto-generated diff path or `excludedAreas[].diffFilePath` override escapes the configured diff root.
|
|
268
|
+
- `ComparePdfConfigurationError: Compare Threshold must be a finite non-negative integer.` — when `options.compareThreshold` is negative, fractional, `NaN`, or infinite.
|
|
269
|
+
- `ComparePdfConfigurationError: Matching Threshold must be a finite non-negative integer.` — when `excludedAreas[].matchingThreshold` is negative, fractional, `NaN`, or infinite.
|
|
270
|
+
- `ComparePdfConfigurationError: Page Number must be a finite positive integer.` — when `excludedAreas[].pageNumber` is `<= 0`, fractional, `NaN`, or infinite.
|
|
271
|
+
- `ComparePdfConfigurationError: pdfToPngConvertOptions must be an object.` — when an untyped caller passes a non-object render configuration.
|
|
272
|
+
- `ComparePdfConfigurationError: Unsupported pdfToPngConvertOptions properties: ...` — when an untyped caller passes render settings that would skip page content or enable parallel rendering.
|
|
273
|
+
- `ComparePdfConfigurationError: pdfToPngConvertOptions.outputFolder must be a non-empty string.` — when an untyped caller passes a non-string or blank render output path.
|
|
274
|
+
- `ComparePdfConfigurationError: pdfToPngConvertOptions.outputFolder must point to a directory when it already exists: <path>` — when the configured render output path already exists as a file.
|
|
275
|
+
- `ComparePdfConfigurationError: pdfToPngConvertOptions.outputFolder must not traverse a symbolic link: <path>` — when the render output path itself or an existing ancestor is a symbolic link, or when the post-mkdir leaf check finds a symbolic link at the resolved folder or at the pre-created `actual/` / `expected/` namespace subdirectory (sandbox parity with `diffsOutputFolder`).
|
|
276
|
+
- `ComparePdfConfigurationError: pdfToPngConvertOptions.outputFolder must point to a writable directory: <path>` — when the library cannot create the resolved render output folder or its `actual/` / `expected/` namespace subdirectories (for example, a regular file blocks the leaf path).
|
|
277
|
+
- `ComparePdfRenderingError: Failed to render actual PDF pages.` / `Failed to render expected PDF pages.` — when the PDF renderer dependency fails. The original dependency exception is attached as `cause`.
|
|
278
|
+
- `ComparePdfRenderingError: Rendered page content is missing for page: <page-name>.` — when the renderer returns a page without binary PNG content.
|
|
279
|
+
- `ComparePdfComparisonError: Failed to compare rendered PDF page <page-number>.` — when the PNG comparator dependency fails. The original dependency exception is attached as `cause`.
|
|
280
|
+
|
|
281
|
+
### `comparePdfDetailed(actualPdf, expectedPdf, options?)`
|
|
282
|
+
|
|
283
|
+
Accepts the same parameters as `comparePdf()` and returns
|
|
284
|
+
`Promise<ComparePdfDetailedResult>`.
|
|
285
|
+
|
|
286
|
+
Use this API when you need page-level mismatch counts, applied thresholds, diff output paths, or
|
|
287
|
+
deterministic missing-page information without inspecting the filesystem.
|
|
288
|
+
|
|
289
|
+
### `PdfInput`
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
type PdfInput = string | Buffer | ArrayBufferLike;
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
`string` values are trusted local file paths by default. They are opened and read before rendering.
|
|
296
|
+
For untrusted environments, prefer binary inputs or set `ComparePdfOptions.allowedInputRoot`.
|
|
143
297
|
|
|
144
298
|
### `ComparePdfOptions`
|
|
145
299
|
|
|
146
|
-
| Property | Type
|
|
147
|
-
| ------------------------ |
|
|
148
|
-
| `
|
|
149
|
-
| `
|
|
150
|
-
| `
|
|
151
|
-
| `
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
|
158
|
-
|
|
|
159
|
-
| `
|
|
160
|
-
| `
|
|
161
|
-
| `
|
|
300
|
+
| Property | Type | Default | Description |
|
|
301
|
+
| ------------------------ | ------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
302
|
+
| `writeDiffs` | `boolean` | `false` | Enables diff PNG output on disk |
|
|
303
|
+
| `diffsOutputFolder` | `string` | `./comparePdfOutput` | Trusted diff-output root; validated when provided, and used for written diff PNGs only when `writeDiffs` is `true` |
|
|
304
|
+
| `allowedInputRoot` | `string` | `undefined` | Optional root directory that constrains string PDF inputs; when omitted, string paths are trusted caller-controlled inputs |
|
|
305
|
+
| `compareThreshold` | `number` | `0` | Maximum number of differing pixels allowed before comparison fails; must be a finite non-negative integer |
|
|
306
|
+
| `excludedAreas` | `PageExclusion[]` | `[]` | Per-page exclusion zones matched by rendered `pageNumber` (1-based); non-rendered pages are ignored and the first duplicate entry for a page wins |
|
|
307
|
+
| `pdfToPngConvertOptions` | `PdfRenderOptions` | see below | Options for rendering PDF pages before comparison |
|
|
308
|
+
|
|
309
|
+
### `ComparePdfDetailedResult`
|
|
310
|
+
|
|
311
|
+
| Property | Type | Description |
|
|
312
|
+
| ------------------- | ------------------------ | ------------------------------------------------------------- |
|
|
313
|
+
| `isEqual` | `boolean` | `true` when every planned page comparison is within threshold |
|
|
314
|
+
| `actualPageCount` | `number` | Number of rendered pages produced from the actual PDF |
|
|
315
|
+
| `expectedPageCount` | `number` | Number of rendered pages produced from the expected PDF |
|
|
316
|
+
| `compareThreshold` | `number` | Document-level threshold supplied to the comparison |
|
|
317
|
+
| `writeDiffs` | `boolean` | `true` when diff PNG writing was enabled for the comparison |
|
|
318
|
+
| `diffsOutputFolder` | `string \| null` | Resolved base diff output folder, or `null` when disabled |
|
|
319
|
+
| `pages` | `ComparePdfPageResult[]` | Page-level outcomes sorted by `pageNumber` |
|
|
320
|
+
|
|
321
|
+
### `ComparePdfPageResult`
|
|
322
|
+
|
|
323
|
+
| Property | Type | Description |
|
|
324
|
+
| ------------------ | ---------------------- | ------------------------------------------------------------------- |
|
|
325
|
+
| `pageNumber` | `number` | 1-based rendered page number |
|
|
326
|
+
| `status` | `ComparePdfPageStatus` | `matched`, `mismatched`, `missing-actual`, or `missing-expected` |
|
|
327
|
+
| `isEqual` | `boolean` | `true` when this page is within its applicable threshold |
|
|
328
|
+
| `threshold` | `number` | Threshold actually applied to this page |
|
|
329
|
+
| `mismatchCount` | `number \| null` | Comparator mismatch count, or `null` when the page was not compared |
|
|
330
|
+
| `diffFilePath` | `string \| null` | Diff PNG output path, or `null` when the page was not compared |
|
|
331
|
+
| `actualPageName` | `string \| null` | Renderer-reported actual page image name |
|
|
332
|
+
| `expectedPageName` | `string \| null` | Renderer-reported expected page image name |
|
|
333
|
+
|
|
334
|
+
### `ComparePdfPageStatus`
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
type ComparePdfPageStatus = 'matched' | 'mismatched' | 'missing-actual' | 'missing-expected';
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### `PdfRenderOptions`
|
|
341
|
+
|
|
342
|
+
`PdfRenderOptions` is this library's stable rendering contract. It is adapted internally to the
|
|
343
|
+
current PDF renderer and is not a direct re-export of an upstream dependency type.
|
|
344
|
+
|
|
345
|
+
| Property | Type | Description |
|
|
346
|
+
| -------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
347
|
+
| `viewportScale` | `number` | Scale factor applied before rendering |
|
|
348
|
+
| `disableFontFace` | `boolean` | Use built-in fonts instead of embedded fonts |
|
|
349
|
+
| `useSystemFonts` | `boolean` | Allow system font fallbacks |
|
|
350
|
+
| `enableXfa` | `boolean` | Render XFA form data |
|
|
351
|
+
| `pdfFilePassword` | `string` | Password for encrypted PDFs |
|
|
352
|
+
| `outputFolder` | `string` | Folder for intermediate PNG files; validated as a non-empty path that does not traverse a symbolic link. This library writes under `actual/` and `expected/` subfolders to avoid collisions |
|
|
353
|
+
| `outputFileMaskFunc` | `(pageNumber: number) => string` | Custom PNG filename generator |
|
|
354
|
+
| `pagesToProcess` | `number[]` | 1-based pages to render |
|
|
355
|
+
| `verbosityLevel` | `number` | Renderer verbosity level |
|
|
356
|
+
|
|
357
|
+
`comparePdf()` always renders page content and always calls the renderer sequentially. The following
|
|
358
|
+
upstream renderer flags are intentionally excluded from `PdfRenderOptions` and are rejected at runtime
|
|
359
|
+
for untyped JavaScript callers: `returnPageContent`, `returnMetadataOnly`, `processPagesInParallel`,
|
|
360
|
+
and `concurrencyLimit`.
|
|
361
|
+
|
|
362
|
+
`pdfToPngConvertOptions.outputFolder` controls renderer intermediate files independently from
|
|
363
|
+
`diffsOutputFolder`, which only constrains diff PNG output paths. When `outputFolder` is set, this
|
|
364
|
+
library writes the two compared PDFs into `actual/` and `expected/` subfolders beneath that root so
|
|
365
|
+
custom filename masks do not collide.
|
|
366
|
+
|
|
367
|
+
The render output path is validated with the same sandbox-parity contract applied to
|
|
368
|
+
`diffsOutputFolder`: it must be a non-empty string, must resolve to a directory when the path
|
|
369
|
+
already exists, and may not traverse a symbolic link at the leaf or at any existing ancestor.
|
|
370
|
+
On top of the path-walker check, this library takes ownership of leaf creation: the resolved
|
|
371
|
+
path and both the `actual/` and `expected/` namespace subdirectories are pre-created and then
|
|
372
|
+
re-asserted to be real (non-symlink) directories before any render call runs. That closes the
|
|
373
|
+
residual validate→render TOCTOU window the path walker cannot cover on its own, so an attacker
|
|
374
|
+
cannot replace a future write destination with a symbolic link between validation and the
|
|
375
|
+
renderer's first write (CWE-59 / CWE-61 / CWE-367).
|
|
376
|
+
|
|
377
|
+
### `PageExclusion`
|
|
378
|
+
|
|
379
|
+
| Property | Type | Description |
|
|
380
|
+
| ------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
381
|
+
| `pageNumber` | `number` | 1-based page number this exclusion applies to (`1` = first page, `2` = second page, etc.); must be a finite positive integer |
|
|
382
|
+
| `excludedAreas` | `PageArea[]` | Rectangles to exclude from comparison |
|
|
383
|
+
| `excludedAreaColor` | `RgbColor` | Fill colour applied to `excludedAreas` before comparison. When omitted, `png-visual-compare` uses its default blue `{ r: 0, g: 0, b: 255 }` |
|
|
384
|
+
| `diffFilePath` | `string` | Override the diff image output path for this page; the resolved path must stay inside `diffsOutputFolder` |
|
|
385
|
+
| `matchingThreshold` | `number` | Per-page pixel threshold, overrides the document-level `compareThreshold` for this page; must be a finite non-negative integer |
|
|
386
|
+
|
|
387
|
+
`ExcludedPageArea` remains exported as a backwards-compatible alias of `PageExclusion`.
|
|
388
|
+
|
|
389
|
+
Entries whose `pageNumber` does not correspond to a rendered page are ignored. If multiple entries
|
|
390
|
+
target the same `pageNumber`, only the first matching entry is used.
|
|
391
|
+
|
|
392
|
+
### `PageArea`
|
|
393
|
+
|
|
394
|
+
`PageArea` is a rectangle on a rendered PDF page:
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
{
|
|
398
|
+
x1: number;
|
|
399
|
+
y1: number;
|
|
400
|
+
x2: number;
|
|
401
|
+
y2: number;
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### `RgbColor`
|
|
406
|
+
|
|
407
|
+
`RgbColor` is an RGB colour object used in diff-related configuration:
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
{
|
|
411
|
+
r: number;
|
|
412
|
+
g: number;
|
|
413
|
+
b: number;
|
|
414
|
+
}
|
|
415
|
+
```
|
|
162
416
|
|
|
163
417
|
---
|
|
164
418
|
|
package/out/comparePdf.d.ts
CHANGED
|
@@ -1,11 +1,53 @@
|
|
|
1
1
|
import type { ComparePdfOptions } from './types/ComparePdfOptions.js';
|
|
2
|
+
import type { ComparePdfDetailedResult } from './types/ComparePdfDetailedResult.js';
|
|
3
|
+
import type { PdfInput } from './types/PdfInput.js';
|
|
2
4
|
/**
|
|
3
|
-
* Compares two PDF
|
|
5
|
+
* Compares two PDF inputs and returns a boolean indicating whether they are visually equivalent.
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
+
* Supported PDF inputs are file paths, `Buffer`, `ArrayBuffer`, and `SharedArrayBuffer`. String
|
|
8
|
+
* paths are trusted caller-controlled inputs by default. To constrain them to a specific
|
|
9
|
+
* workspace, set `options.allowedInputRoot`. `SharedArrayBuffer` inputs are normalized to
|
|
10
|
+
* `ArrayBuffer` before rendering.
|
|
11
|
+
*
|
|
12
|
+
* Diff PNGs are not written unless `options.writeDiffs` is explicitly set to `true`. When enabled,
|
|
13
|
+
* `options.diffsOutputFolder` acts as a trusted write root on a trusted local filesystem.
|
|
14
|
+
*
|
|
15
|
+
* Rendered pages are paired by renderer-reported `pageNumber`, not by generated PNG filename
|
|
16
|
+
* or `excludedAreas` array position. If either PDF is missing a rendered counterpart for a
|
|
17
|
+
* page number present in the comparison plan, the overall comparison returns `false`.
|
|
18
|
+
*
|
|
19
|
+
* @param actualPdf - The file path or binary content of the actual PDF to compare.
|
|
20
|
+
* @param expectedPdf - The file path or binary content of the expected PDF to compare against.
|
|
21
|
+
* @param opts - Optional comparison options.
|
|
22
|
+
* @returns A promise that resolves to `true` when every compared page stays within its
|
|
23
|
+
* applicable threshold, otherwise `false`.
|
|
24
|
+
* @throws {ComparePdfInputError} When a PDF input has an unsupported type or points to a missing file.
|
|
25
|
+
* @throws {ComparePdfConfigurationError} When runtime comparison configuration is invalid.
|
|
26
|
+
* @throws {ComparePdfRenderingError} When PDF rendering fails.
|
|
27
|
+
* @throws {ComparePdfComparisonError} When PNG comparison fails.
|
|
28
|
+
*/
|
|
29
|
+
export declare function comparePdf(actualPdf: PdfInput, expectedPdf: PdfInput, opts?: ComparePdfOptions): Promise<boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* Compares two PDF inputs and returns structured page-level comparison details.
|
|
32
|
+
*
|
|
33
|
+
* Supported PDF inputs are file paths, `Buffer`, `ArrayBuffer`, and `SharedArrayBuffer`. String
|
|
34
|
+
* paths are trusted caller-controlled inputs by default. To constrain them to a specific
|
|
35
|
+
* workspace, set `options.allowedInputRoot`. `SharedArrayBuffer` inputs are normalized to
|
|
36
|
+
* `ArrayBuffer` before rendering.
|
|
37
|
+
*
|
|
38
|
+
* Diff PNGs are not written unless `options.writeDiffs` is explicitly set to `true`.
|
|
39
|
+
*
|
|
40
|
+
* Rendered pages are paired by renderer-reported `pageNumber`, not by generated PNG filename
|
|
41
|
+
* or `excludedAreas` array position. Missing rendered counterpart pages are surfaced in the
|
|
42
|
+
* returned page results without requiring callers to inspect diff files on disk.
|
|
43
|
+
*
|
|
44
|
+
* @param actualPdf - The file path or binary content of the actual PDF to compare.
|
|
45
|
+
* @param expectedPdf - The file path or binary content of the expected PDF to compare against.
|
|
7
46
|
* @param opts - Optional comparison options.
|
|
8
|
-
* @returns A promise that resolves to a
|
|
9
|
-
* @throws
|
|
47
|
+
* @returns A promise that resolves to a structured comparison result.
|
|
48
|
+
* @throws {ComparePdfInputError} When a PDF input has an unsupported type or points to a missing file.
|
|
49
|
+
* @throws {ComparePdfConfigurationError} When runtime comparison configuration is invalid.
|
|
50
|
+
* @throws {ComparePdfRenderingError} When PDF rendering fails.
|
|
51
|
+
* @throws {ComparePdfComparisonError} When PNG comparison fails.
|
|
10
52
|
*/
|
|
11
|
-
export declare function
|
|
53
|
+
export declare function comparePdfDetailed(actualPdf: PdfInput, expectedPdf: PdfInput, opts?: ComparePdfOptions): Promise<ComparePdfDetailedResult>;
|