refactor: remove HTML report export and implement PDF export options in PDF comparison tool

- Deleted the exportCompareHtmlReport function and its related imports.
- Introduced a dropdown menu for exporting comparison results as PDFs with multiple modes (split, alternating, left, right).
- Updated the comparison logic to utilize caching for page models and comparison results.
- Refactored rendering functions to improve code organization and maintainability.
- Enhanced UI elements for better user experience during PDF export.
This commit is contained in:
alam00000
2026-03-09 23:26:52 +05:30
parent 0fe94795cc
commit d2a1450bc0
15 changed files with 953 additions and 526 deletions

View File

@@ -8,6 +8,8 @@ import type {
CompareTextItem,
CompareWordToken,
} from '../types.ts';
import { calculateBoundingRect } from './text-normalization.ts';
import { COMPARE_GEOMETRY } from '../config.ts';
interface WordToken {
word: string;
@@ -86,7 +88,11 @@ function groupAdjacentRects(rects: CompareRectangle[]): CompareRectangle[] {
const lastRect = prev[prev.length - 1];
const curr = sorted[i];
const sameLine =
Math.abs(curr.y - lastRect.y) < Math.max(lastRect.height * 0.6, 4);
Math.abs(curr.y - lastRect.y) <
Math.max(
lastRect.height * COMPARE_GEOMETRY.LINE_TOLERANCE_FACTOR,
COMPARE_GEOMETRY.MIN_LINE_TOLERANCE
);
const close = curr.x <= lastRect.x + lastRect.width + lastRect.height * 2;
if (sameLine && close) {
@@ -96,13 +102,7 @@ function groupAdjacentRects(rects: CompareRectangle[]): CompareRectangle[] {
}
}
return groups.map((group) => {
const minX = Math.min(...group.map((r) => r.x));
const minY = Math.min(...group.map((r) => r.y));
const maxX = Math.max(...group.map((r) => r.x + r.width));
const maxY = Math.max(...group.map((r) => r.y + r.height));
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
});
return groups.map((group) => calculateBoundingRect(group));
}
function collapseWords(words: WordToken[]) {

View File

@@ -33,8 +33,10 @@ const textMeasurementCache: Map<string, number> | null = measurementContext
: null;
let lastMeasurementFont = '';
const DEFAULT_CHAR_WIDTH = 1;
const DEFAULT_SPACE_WIDTH = 0.33;
import { COMPARE_TEXT, COMPARE_GEOMETRY } from '../config.ts';
const DEFAULT_CHAR_WIDTH = COMPARE_TEXT.DEFAULT_CHAR_WIDTH;
const DEFAULT_SPACE_WIDTH = COMPARE_TEXT.DEFAULT_SPACE_WIDTH;
function shouldJoinTokenWithPrevious(previous: string, current: string) {
if (!previous) return false;
@@ -261,8 +263,9 @@ function toRect(
export function sortCompareTextItems(items: CompareTextItem[]) {
return [...items].sort((left, right) => {
const lineTolerance = Math.max(
Math.min(left.rect.height, right.rect.height) * 0.6,
4
Math.min(left.rect.height, right.rect.height) *
COMPARE_GEOMETRY.LINE_TOLERANCE_FACTOR,
COMPARE_GEOMETRY.MIN_LINE_TOLERANCE
);
const topDiff = left.rect.y - right.rect.y;
@@ -450,8 +453,9 @@ export function mergeIntoLines(
const anchor = currentLine[0];
const curr = sortedItems[i];
const lineTolerance = Math.max(
Math.min(anchor.rect.height, curr.rect.height) * 0.6,
4
Math.min(anchor.rect.height, curr.rect.height) *
COMPARE_GEOMETRY.LINE_TOLERANCE_FACTOR,
COMPARE_GEOMETRY.MIN_LINE_TOLERANCE
);
if (Math.abs(curr.rect.y - anchor.rect.y) <= lineTolerance) {

View File

@@ -1,8 +1,5 @@
import type { ComparePagePair, ComparePageSignature } from '../types.ts';
function tokenize(text: string) {
return new Set(text.split(/\s+/).filter(Boolean));
}
import { tokenizeTextAsSet } from './text-normalization.ts';
function similarityScore(
left: ComparePageSignature,
@@ -16,8 +13,8 @@ function similarityScore(
return 0.08;
}
const leftTokens = tokenize(left.plainText);
const rightTokens = tokenize(right.plainText);
const leftTokens = tokenizeTextAsSet(left.plainText);
const rightTokens = tokenizeTextAsSet(right.plainText);
const union = new Set([...leftTokens, ...rightTokens]);
let intersectionCount = 0;

View File

@@ -1,4 +1,4 @@
import type { CompareTextItem } from '../types.ts';
import type { CompareRectangle, CompareTextItem } from '../types.ts';
export function normalizeCompareText(text: string) {
return text
@@ -62,3 +62,22 @@ export function isLowQualityExtractedText(text: string) {
return false;
}
export function tokenizeText(text: string): string[] {
return text.split(/\s+/).filter(Boolean);
}
export function tokenizeTextAsSet(text: string): Set<string> {
return new Set(tokenizeText(text));
}
export function calculateBoundingRect(
rects: CompareRectangle[]
): CompareRectangle {
if (rects.length === 0) return { x: 0, y: 0, width: 0, height: 0 };
const minX = Math.min(...rects.map((r) => r.x));
const minY = Math.min(...rects.map((r) => r.y));
const maxX = Math.max(...rects.map((r) => r.x + r.width));
const maxY = Math.max(...rects.map((r) => r.y + r.height));
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
}

View File

@@ -1,6 +1,7 @@
import pixelmatch from 'pixelmatch';
import type { CompareVisualDiff } from '../types.ts';
import { VISUAL_DIFF as VISUAL_DIFF_CONFIG } from '../config.ts';
type FocusRegion = {
x: number;
@@ -69,12 +70,16 @@ export function renderVisualDiff(
width,
height,
{
threshold: 0.12,
threshold: VISUAL_DIFF_CONFIG.PIXELMATCH_THRESHOLD,
includeAA: false,
alpha: 0.2,
alpha: VISUAL_DIFF_CONFIG.ALPHA,
diffMask: false,
diffColor: [239, 68, 68],
diffColorAlt: [34, 197, 94],
diffColor: [...VISUAL_DIFF_CONFIG.DIFF_COLOR] as [number, number, number],
diffColorAlt: [...VISUAL_DIFF_CONFIG.DIFF_COLOR_ALT] as [
number,
number,
number,
],
}
);