pdf-visual-compare 3.4.0 → 3.5.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 +9 -9
- package/out/comparePdf.js +35 -27
- package/out/types/ExcludedPageArea.d.ts +12 -6
- package/package.json +14 -13
package/README.md
CHANGED
|
@@ -76,8 +76,8 @@ const isEqual = await comparePdf('./actual.pdf', './expected.pdf', {
|
|
|
76
76
|
// 0 = pixel-perfect match required (default). Must be >= 0.
|
|
77
77
|
compareThreshold: 200,
|
|
78
78
|
|
|
79
|
-
// Per-page exclusion zones, matched by
|
|
80
|
-
//
|
|
79
|
+
// Per-page exclusion zones, matched by the `pageNumber` field (1-based).
|
|
80
|
+
// `pageNumber: 1` → first page, `pageNumber: 2` → second page, etc.
|
|
81
81
|
// Pixel coordinates are relative to the rendered PNG at the configured viewportScale.
|
|
82
82
|
excludedAreas: [
|
|
83
83
|
{
|
|
@@ -128,11 +128,11 @@ const isEqual = await comparePdf(actual, expected);
|
|
|
128
128
|
|
|
129
129
|
### `comparePdf(actualPdf, expectedPdf, options?)`
|
|
130
130
|
|
|
131
|
-
| Parameter | Type
|
|
132
|
-
| ------------- |
|
|
133
|
-
| `actualPdf` | `string \| Buffer`
|
|
134
|
-
| `expectedPdf` | `string \| Buffer`
|
|
135
|
-
| `options` | `ComparePdfOptions` _(optional)_
|
|
131
|
+
| Parameter | Type | Description |
|
|
132
|
+
| ------------- | ------------------------------------------- | ----------------------------------------- |
|
|
133
|
+
| `actualPdf` | `string \| Buffer \| ArrayBufferLike` | File path or buffer of the PDF under test |
|
|
134
|
+
| `expectedPdf` | `string \| Buffer \| ArrayBufferLike` | File path or buffer of the reference PDF |
|
|
135
|
+
| `options` | `ComparePdfOptions` _(optional)_ | Comparison configuration |
|
|
136
136
|
|
|
137
137
|
Returns `Promise<boolean>` — `true` if the PDFs are visually equivalent within the configured threshold, `false` otherwise.
|
|
138
138
|
|
|
@@ -147,14 +147,14 @@ Returns `Promise<boolean>` — `true` if the PDFs are visually equivalent within
|
|
|
147
147
|
| ------------------------ | --------------------- | -------------------- | --------------------------------------------------------------------------- |
|
|
148
148
|
| `diffsOutputFolder` | `string` | `./comparePdfOutput` | Folder where diff PNG images are written |
|
|
149
149
|
| `compareThreshold` | `number` | `0` | Maximum number of differing pixels allowed before comparison fails |
|
|
150
|
-
| `excludedAreas` | `ExcludedPageArea[]` | `[]` | Per-page exclusion zones
|
|
150
|
+
| `excludedAreas` | `ExcludedPageArea[]` | `[]` | Per-page exclusion zones matched by the `pageNumber` field of each entry (1-based) |
|
|
151
151
|
| `pdfToPngConvertOptions` | `PdfToPngOptions` | see below | Options forwarded to [`pdf-to-png-converter`](https://github.com/dichovsky/pdf-to-png-converter) |
|
|
152
152
|
|
|
153
153
|
### `ExcludedPageArea`
|
|
154
154
|
|
|
155
155
|
| Property | Type | Description |
|
|
156
156
|
| ------------------- | -------- | --------------------------------------------------------------------------------------------------- |
|
|
157
|
-
| `pageNumber` | `number` |
|
|
157
|
+
| `pageNumber` | `number` | 1-based page number this exclusion applies to (`1` = first page, `2` = second page, etc.) |
|
|
158
158
|
| `excludedAreas` | `Area[]` | Rectangles to exclude. `Area` = `{ x1, y1, x2, y2 }` in pixels at the configured `viewportScale` |
|
|
159
159
|
| `excludedAreaColor` | `Color` | Fill color for excluded regions in diff images. `Color` = `{ r, g, b }` with values 0–255 |
|
|
160
160
|
| `diffFilePath` | `string` | Override the diff image output path for this page |
|
package/out/comparePdf.js
CHANGED
|
@@ -27,50 +27,58 @@ async function comparePdf(actualPdf, expectedPdf, opts = {}) {
|
|
|
27
27
|
if (!pdfToPngConvertOpts.outputFileMaskFunc) {
|
|
28
28
|
pdfToPngConvertOpts.outputFileMaskFunc = (pageNumber) => `comparePdf_${pageNumber}.png`;
|
|
29
29
|
}
|
|
30
|
-
const diffsOutputFolder = opts
|
|
31
|
-
const compareThreshold = opts
|
|
32
|
-
const excludedAreas = opts
|
|
30
|
+
const diffsOutputFolder = opts.diffsOutputFolder ?? const_js_1.DEFAULT_DIFFS_FOLDER;
|
|
31
|
+
const compareThreshold = opts.compareThreshold ?? 0;
|
|
32
|
+
const excludedAreas = opts.excludedAreas ?? [];
|
|
33
33
|
if (compareThreshold < 0) {
|
|
34
34
|
throw Error('Compare Threshold cannot be less than 0.');
|
|
35
35
|
}
|
|
36
|
-
// Convert PDFs to PNGs
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (actualPdfPngPages.length < expectedPdfPngPages.length) {
|
|
43
|
-
[actualPdfPngPages, expectedPdfPngPages] = [expectedPdfPngPages, actualPdfPngPages];
|
|
44
|
-
}
|
|
36
|
+
// Convert PDFs to PNGs sequentially to avoid PDF.js worker state corruption.
|
|
37
|
+
// When two pdfToPng calls run concurrently via Promise.all, the pdfDocument.cleanup()
|
|
38
|
+
// in one call can corrupt the shared PDF.js worker state for the other call, causing
|
|
39
|
+
// "Invalid page request" errors — particularly when the two PDFs have different page counts.
|
|
40
|
+
const actualPdfPngPages = await (0, pdf_to_png_converter_1.pdfToPng)(actualPdf, { ...pdfToPngConvertOpts });
|
|
41
|
+
const expectedPdfPngPages = await (0, pdf_to_png_converter_1.pdfToPng)(expectedPdf, { ...pdfToPngConvertOpts });
|
|
45
42
|
let documentCompareResult = true;
|
|
46
|
-
actualPdfPngPages.
|
|
43
|
+
for (const [index, pngPage] of actualPdfPngPages.entries()) {
|
|
44
|
+
// Look up the exclusion zone for this page by 1-based page number.
|
|
45
|
+
const pageExcludedArea = excludedAreas.find((area) => area.pageNumber === index + 1);
|
|
46
|
+
if (!pngPage.content) {
|
|
47
|
+
throw new Error(`Page content is undefined for page: ${pngPage.name}`);
|
|
48
|
+
}
|
|
49
|
+
// Only forward the fields that ComparePngOptions actually recognises.
|
|
50
|
+
// The per-page diffFilePath override takes precedence over the auto-generated path.
|
|
47
51
|
const comparePngOpts = {
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
excludedAreas: pageExcludedArea?.excludedAreas,
|
|
53
|
+
diffFilePath: pageExcludedArea?.diffFilePath ?? (0, node_path_1.resolve)(diffsOutputFolder, `diff_${pngPage.name}`),
|
|
50
54
|
throwErrorOnInvalidInputData: false,
|
|
51
55
|
};
|
|
52
|
-
comparePngOpts.diffFilePath = (0, node_path_1.resolve)(diffsOutputFolder, `diff_${pngPage.name}`);
|
|
53
56
|
const pngPageOutputToCompareWith = expectedPdfPngPages.find((p) => p.name === pngPage.name);
|
|
54
57
|
const pageCompareResult = (0, png_visual_compare_1.comparePng)(pngPage.content, pngPageOutputToCompareWith?.content ?? '', comparePngOpts);
|
|
55
|
-
|
|
58
|
+
// Per-page matchingThreshold overrides the document-level compareThreshold when set.
|
|
59
|
+
const pageThreshold = pageExcludedArea?.matchingThreshold ?? compareThreshold;
|
|
60
|
+
if (pageCompareResult > pageThreshold) {
|
|
56
61
|
documentCompareResult = false;
|
|
57
62
|
}
|
|
58
|
-
}
|
|
63
|
+
}
|
|
64
|
+
// Extra pages present in expected but absent from actual are always a mismatch.
|
|
65
|
+
if (expectedPdfPngPages.length > actualPdfPngPages.length) {
|
|
66
|
+
documentCompareResult = false;
|
|
67
|
+
}
|
|
59
68
|
return documentCompareResult;
|
|
60
69
|
}
|
|
61
70
|
/**
|
|
62
|
-
* Validates the type of the input file.
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* If the input file is neither a Buffer nor a string, an error is thrown.
|
|
71
|
+
* Validates the type of the input file.
|
|
72
|
+
*
|
|
73
|
+
* Accepts a `Buffer`, any `ArrayBufferLike` (`ArrayBuffer` / `SharedArrayBuffer`), or a
|
|
74
|
+
* string path that points to an existing file. Throws for any other input.
|
|
67
75
|
*
|
|
68
|
-
* @param inputFile - The input
|
|
69
|
-
* @throws {Error} If the input
|
|
70
|
-
* @throws {Error} If the input
|
|
76
|
+
* @param inputFile - The input to validate.
|
|
77
|
+
* @throws {Error} If the input is a string path that does not exist.
|
|
78
|
+
* @throws {Error} If the input is neither a recognised buffer type nor a string.
|
|
71
79
|
*/
|
|
72
80
|
function validateInputFileType(inputFile) {
|
|
73
|
-
if (Buffer.isBuffer(inputFile)) {
|
|
81
|
+
if (Buffer.isBuffer(inputFile) || inputFile instanceof ArrayBuffer || inputFile instanceof SharedArrayBuffer) {
|
|
74
82
|
return;
|
|
75
83
|
}
|
|
76
84
|
if (typeof inputFile === 'string') {
|
|
@@ -2,13 +2,14 @@ import type { Area, Color } from 'png-visual-compare';
|
|
|
2
2
|
/**
|
|
3
3
|
* Defines a page-specific exclusion zone for PDF comparison.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Each entry is matched to a PDF page by its `pageNumber` field (1-based):
|
|
6
|
+
* `pageNumber: 1` targets the first page, `pageNumber: 2` the second, and so on.
|
|
7
|
+
* Entries whose `pageNumber` does not correspond to any page are silently ignored.
|
|
7
8
|
*/
|
|
8
9
|
export type ExcludedPageArea = {
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* 1-based page number this exclusion applies to.
|
|
12
|
+
* Must match the page's position in the PDF (`1` = first page, `2` = second page, etc.).
|
|
12
13
|
*/
|
|
13
14
|
pageNumber: number;
|
|
14
15
|
/**
|
|
@@ -21,16 +22,21 @@ export type ExcludedPageArea = {
|
|
|
21
22
|
/**
|
|
22
23
|
* Fill color used to paint excluded regions in the diff output image.
|
|
23
24
|
* Expressed as `{ r, g, b }` with channel values in the range 0–255.
|
|
25
|
+
*
|
|
26
|
+
* @remarks Currently reserved for future use. The underlying `png-visual-compare`
|
|
27
|
+
* library always paints excluded areas blue; this field has no effect at runtime.
|
|
24
28
|
*/
|
|
25
29
|
excludedAreaColor?: Color;
|
|
26
30
|
/**
|
|
27
31
|
* Override the diff image output file path for this specific page.
|
|
28
|
-
* When
|
|
32
|
+
* When set, takes precedence over the path derived from `diffsOutputFolder`.
|
|
33
|
+
* When omitted, the path is auto-generated as `<diffsOutputFolder>/diff_<pageName>`.
|
|
29
34
|
*/
|
|
30
35
|
diffFilePath?: string;
|
|
31
36
|
/**
|
|
32
37
|
* Per-page pixel difference threshold that overrides the document-level
|
|
33
|
-
* `compareThreshold` for this page only.
|
|
38
|
+
* `compareThreshold` for this page only. Must be >= 0.
|
|
39
|
+
* When omitted, the document-level `compareThreshold` applies.
|
|
34
40
|
*/
|
|
35
41
|
matchingThreshold?: number;
|
|
36
42
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdf-visual-compare",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Visual regression testing library for PDFs in Js/Ts without binary and OS dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pdf",
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"pdf compare",
|
|
10
10
|
"compare pdf",
|
|
11
11
|
"pdf diff",
|
|
12
|
-
"visual regression"
|
|
12
|
+
"visual regression",
|
|
13
|
+
"pdf testing",
|
|
14
|
+
"visual-diff"
|
|
13
15
|
],
|
|
14
16
|
"homepage": "https://github.com/dichovsky/pdf-visual-compare#readme",
|
|
15
17
|
"bugs": {
|
|
@@ -38,6 +40,7 @@
|
|
|
38
40
|
"./out"
|
|
39
41
|
],
|
|
40
42
|
"scripts": {
|
|
43
|
+
"prepublishOnly": "npm test && npm run build",
|
|
41
44
|
"prebuild": "npm run clean",
|
|
42
45
|
"build": "tsc --pretty",
|
|
43
46
|
"clean": "rimraf ./out ./coverage ./test-results ./comparePdfOutput",
|
|
@@ -53,23 +56,21 @@
|
|
|
53
56
|
"test:license": "npx --yes license-checker --production --onlyAllow \"ISC; MIT; MIT OR X11; BSD; Apache-2.0; Unlicense\""
|
|
54
57
|
},
|
|
55
58
|
"dependencies": {
|
|
56
|
-
"pdf-to-png-converter": "~3.
|
|
57
|
-
"png-visual-compare": "~
|
|
59
|
+
"pdf-to-png-converter": "~3.18.0",
|
|
60
|
+
"png-visual-compare": "~5.1.0"
|
|
58
61
|
},
|
|
59
62
|
"devDependencies": {
|
|
60
|
-
"@types/node": "^25.
|
|
63
|
+
"@types/node": "^25.6.0",
|
|
61
64
|
"@types/pngjs": "^6.0.5",
|
|
62
|
-
"@vitest/coverage-v8": "^4.
|
|
63
|
-
"eslint": "^10.0
|
|
65
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
66
|
+
"eslint": "^10.2.0",
|
|
64
67
|
"jiti": "^2.6.1",
|
|
65
68
|
"rimraf": "^6.1.3",
|
|
66
|
-
"
|
|
67
|
-
"typescript": "^
|
|
68
|
-
"
|
|
69
|
-
"vitest": "^4.0.18"
|
|
69
|
+
"typescript": "^6.0.2",
|
|
70
|
+
"typescript-eslint": "^8.58.1",
|
|
71
|
+
"vitest": "^4.1.4"
|
|
70
72
|
},
|
|
71
73
|
"engines": {
|
|
72
|
-
"node": ">=20"
|
|
73
|
-
"yarn": "please-use-npm"
|
|
74
|
+
"node": ">=20"
|
|
74
75
|
}
|
|
75
76
|
}
|