UNPKG

pdf-visual-diff

Version:

Visual Regression Testing for PDFs in JavaScript

166 lines 7.09 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.snapshotsDirName = exports.SNAPSHOTS_DIR_NAME = void 0; exports.comparePdfToSnapshot = comparePdfToSnapshot; const path = __importStar(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 { return handleMissingSnapshot(pdf, snapshotContext, mergedOptions); } } function mergeOptionsWithDefaults(options) { return { maskRegions: options?.maskRegions ?? (() => []), pdf2PngOptions: options?.pdf2PngOptions ?? { dpi: types_1.Dpi.High }, failOnMissingSnapshot: options?.failOnMissingSnapshot ?? false, tolerance: options?.tolerance ?? 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 { 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 { // File doesn't exist, no need to remove } } //# sourceMappingURL=compare-pdf-to-snapshot.js.map