UNPKG

pdf-visual-diff

Version:

Visual Regression Testing for PDFs in JavaScript

134 lines 6.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.snapshotsDirName = exports.SNAPSHOTS_DIR_NAME = void 0; exports.comparePdfToSnapshot = comparePdfToSnapshot; const path = require("node:path"); const promises_1 = require("node:fs/promises"); const pdf2png_1 = require("./pdf2png/pdf2png"); const compare_images_1 = require("./compare-images"); const jimp_1 = require("jimp"); const types_1 = require("./types"); const imageUtils_1 = require("./imageUtils"); const colorToNum = { Red: 0xff0000ff, Green: 0x00ff00ff, Blue: 0x0000ffff, White: 0x00000000, Cyan: 0x00ffffff, Magenta: 0xff00ffff, Yellow: 0xffff00ff, Black: 0x000000ff, Gray: 0xbfbfbfff, }; const maskImgWithRegions = (maskRegions) => (images) => { images.forEach((img, idx) => { ; (maskRegions(idx + 1) || []).forEach(({ type, x, y, width, height, color }) => { if (type === 'rectangle-mask') { img.composite(new jimp_1.Jimp({ width, height, color: colorToNum[color] }), x, y); } }); }); return images; }; /** * Compares a PDF to a persisted snapshot, with behavior for handling missing snapshots * controlled by the `failOnMissingSnapshot` option. * * @remarks * The function has the following **side effects**: * - If no snapshot exists: * - If `failOnMissingSnapshot` is `false` (default), the PDF is converted to an image, * saved as a new snapshot, and the function returns `true`. * - If `failOnMissingSnapshot` is `true`, the function returns `false` without creating a new snapshot. * - If a snapshot exists, the PDF is converted to an image and compared to the snapshot: * - If they differ, the function returns `false` and creates two additional images * next to the snapshot: one with the suffix `new` (the current view of the PDF as an image) * and one with the suffix `diff` (showing the difference between the snapshot and the `new` image). * - If they are equal, the function returns `true`. If `new` and `diff` versions are present, they are deleted. * * @param pdf - Path to the PDF file or a Buffer containing the PDF. * @param snapshotDir - Path to the directory where the `__snapshots__` folder will be created. * @param snapshotName - Unique name for the snapshot within the specified path. * @param options - Options for comparison, including tolerance, mask regions, and behavior * regarding missing snapshots. See {@link CompareOptions} for more details. * * @returns * A promise that resolves to `true` if the PDF matches the snapshot or if the behavior * allows for missing snapshots. Resolves to `false` if the PDF differs from the snapshot * or if `failOnMissingSnapshot` is `true` and no snapshot exists. */ async function comparePdfToSnapshot(pdf, snapshotDir, snapshotName, options) { const mergedOptions = mergeOptionsWithDefaults(options); const snapshotContext = await createSnapshotContext(snapshotDir, snapshotName); try { // Check if snapshot exits and handle accordingly await (0, promises_1.access)(snapshotContext.path); return compareWithSnapshot(pdf, snapshotContext, mergedOptions); } catch (_a) { return handleMissingSnapshot(pdf, snapshotContext, mergedOptions); } } function mergeOptionsWithDefaults(options) { var _a, _b, _c, _d; return { maskRegions: (_a = options === null || options === void 0 ? void 0 : options.maskRegions) !== null && _a !== void 0 ? _a : (() => []), pdf2PngOptions: (_b = options === null || options === void 0 ? void 0 : options.pdf2PngOptions) !== null && _b !== void 0 ? _b : { dpi: types_1.Dpi.High }, failOnMissingSnapshot: (_c = options === null || options === void 0 ? void 0 : options.failOnMissingSnapshot) !== null && _c !== void 0 ? _c : false, tolerance: (_d = options === null || options === void 0 ? void 0 : options.tolerance) !== null && _d !== void 0 ? _d : 0, }; } exports.SNAPSHOTS_DIR_NAME = '__snapshots__'; /** * @deprecated Use SNAPSHOTS_DIR_NAME instead. */ exports.snapshotsDirName = exports.SNAPSHOTS_DIR_NAME; /** Generates the snapshot context and creates the folder if it doesn’t already exist. */ async function createSnapshotContext(snapshotDir, snapshotName) { const dirPath = path.join(snapshotDir, exports.SNAPSHOTS_DIR_NAME); try { await (0, promises_1.access)(dirPath); } catch (_a) { await (0, promises_1.mkdir)(dirPath, { recursive: true }); } const basePath = path.join(dirPath, snapshotName); return { name: snapshotName, dirPath, path: `${basePath}.png`, diffPath: `${basePath}.diff.png`, newPath: `${basePath}.new.png`, }; } async function handleMissingSnapshot(pdf, snapshotContext, { failOnMissingSnapshot, maskRegions, pdf2PngOptions }) { if (failOnMissingSnapshot) { return false; } // Generate snapshot if missing const images = await (0, pdf2png_1.pdf2png)(pdf, pdf2PngOptions).then(maskImgWithRegions(maskRegions)); await (0, imageUtils_1.writeImages)(snapshotContext.path)(images); return true; } async function compareWithSnapshot(pdf, snapshotContext, { maskRegions, pdf2PngOptions, tolerance }) { const images = await (0, pdf2png_1.pdf2png)(pdf, pdf2PngOptions).then(maskImgWithRegions(maskRegions)); const result = await (0, compare_images_1.compareImages)(snapshotContext.path, images, { tolerance }); if (result.equal) { await removeIfExists(snapshotContext.diffPath); await removeIfExists(snapshotContext.newPath); return true; } await (0, imageUtils_1.writeImages)(snapshotContext.newPath)(images); await (0, imageUtils_1.writeImages)(snapshotContext.diffPath)(result.diffs.map((x) => x.diff)); return false; } async function removeIfExists(filePath) { try { await (0, promises_1.unlink)(filePath); } catch (_a) { // File doesn't exist, no need to remove } } //# sourceMappingURL=compare-pdf-to-snapshot.js.map