pdf-diff-viewer 1.1.0 → 1.3.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/package.json +1 -1
- package/src/PDFDiffViewer.js +36 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdf-diff-viewer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Browser-based PDF comparison tool with visual diff highlighting. Zero system dependencies, pure JavaScript, client-side processing.",
|
|
5
5
|
"main": "src/PDFDiffViewer.js",
|
|
6
6
|
"type": "module",
|
package/src/PDFDiffViewer.js
CHANGED
|
@@ -20,19 +20,21 @@ class PDFDiffViewer {
|
|
|
20
20
|
scale: options.scale || 3.0,
|
|
21
21
|
maxShift: options.maxShift || 3,
|
|
22
22
|
dilationRadius: options.dilationRadius || 0,
|
|
23
|
-
colorTolerance: options.colorTolerance ||
|
|
23
|
+
colorTolerance: options.colorTolerance || 120,
|
|
24
24
|
minHighlightArea: options.minHighlightArea || 60,
|
|
25
25
|
minWordSize: options.minWordSize || 8,
|
|
26
26
|
highlightAlpha: options.highlightAlpha || 0.32,
|
|
27
|
+
highlightColorA: options.highlightColorA || '#FF1744', // Red for Doc A changes
|
|
28
|
+
highlightColorB: options.highlightColorB || '#2196F3', // Blue for Doc B changes
|
|
29
|
+
backgroundFillColor: options.backgroundFillColor || 'white', // Canvas background
|
|
27
30
|
labelA: options.labelA || 'Document A',
|
|
28
31
|
labelB: options.labelB || 'Document B',
|
|
29
32
|
workerSrc: options.workerSrc || 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js',
|
|
30
33
|
showPageNumbers: options.showPageNumbers !== false,
|
|
31
34
|
cropRegions: options.cropRegions || [],
|
|
32
35
|
maskRegions: options.maskRegions || [],
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
similarityThreshold: options.similarityThreshold || 0.3 // Minimum similarity score (0-1)
|
|
36
|
+
alignmentTolerance: options.alignmentTolerance || 2,
|
|
37
|
+
similarityThreshold: options.similarityThreshold || 0.3
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
// Check if PDF.js is loaded
|
|
@@ -72,13 +74,9 @@ class PDFDiffViewer {
|
|
|
72
74
|
summaryDiv.className = 'pdf-diff-summary';
|
|
73
75
|
this.container.appendChild(summaryDiv);
|
|
74
76
|
|
|
75
|
-
//
|
|
76
|
-
if (
|
|
77
|
-
summaryDiv.innerHTML = '<p>Analyzing document structure for smart alignment...</p>';
|
|
77
|
+
// Automatically handle different page counts with smart alignment
|
|
78
|
+
if (docA.numPages !== docB.numPages) {
|
|
78
79
|
pageMapping = await this._findPageMappings(docA, docB);
|
|
79
|
-
summaryDiv.innerHTML = `<h3>Smart Alignment Active: Comparing ${pageMapping.length} matched page(s)</h3>`;
|
|
80
|
-
} else if (docA.numPages !== docB.numPages) {
|
|
81
|
-
throw new Error(`Page count mismatch: ${docA.numPages} vs ${docB.numPages}. Enable 'smartAlignment' option to handle different page counts.`);
|
|
82
80
|
} else {
|
|
83
81
|
// Direct 1-to-1 mapping
|
|
84
82
|
for (let i = 1; i <= docA.numPages; i++) {
|
|
@@ -105,11 +103,7 @@ class PDFDiffViewer {
|
|
|
105
103
|
|
|
106
104
|
// Update summary
|
|
107
105
|
if (this.options.showPageNumbers) {
|
|
108
|
-
|
|
109
|
-
summaryDiv.innerHTML = `<h3>Comparison Results: ${docA.numPages} page(s)</h3>`;
|
|
110
|
-
} else {
|
|
111
|
-
summaryDiv.innerHTML += `<p>Doc A: ${docA.numPages} pages | Doc B: ${docB.numPages} pages</p>`;
|
|
112
|
-
}
|
|
106
|
+
summaryDiv.innerHTML = `<h3>Comparison Results: ${pageMapping.length} page(s)</h3>`;
|
|
113
107
|
}
|
|
114
108
|
|
|
115
109
|
return this.results;
|
|
@@ -226,9 +220,6 @@ class PDFDiffViewer {
|
|
|
226
220
|
alignment: { dx: best.dx, dy: best.dy }
|
|
227
221
|
};
|
|
228
222
|
}
|
|
229
|
-
alignment: { dx: best.dx, dy: best.dy }
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
223
|
|
|
233
224
|
_renderPageComparison(pageResult, pageNumA, pageNumB = null) {
|
|
234
225
|
const pageDiv = document.createElement('div');
|
|
@@ -361,7 +352,7 @@ class PDFDiffViewer {
|
|
|
361
352
|
padded.height = targetHeight;
|
|
362
353
|
|
|
363
354
|
const ctx = padded.getContext('2d');
|
|
364
|
-
ctx.fillStyle =
|
|
355
|
+
ctx.fillStyle = this.options.backgroundFillColor;
|
|
365
356
|
ctx.fillRect(0, 0, targetWidth, targetHeight);
|
|
366
357
|
ctx.drawImage(srcCanvas, 0, 0);
|
|
367
358
|
return padded;
|
|
@@ -372,7 +363,7 @@ class PDFDiffViewer {
|
|
|
372
363
|
temp.width = width;
|
|
373
364
|
temp.height = height;
|
|
374
365
|
const ctx = temp.getContext('2d');
|
|
375
|
-
ctx.fillStyle =
|
|
366
|
+
ctx.fillStyle = this.options.backgroundFillColor;
|
|
376
367
|
ctx.fillRect(0, 0, width, height);
|
|
377
368
|
ctx.drawImage(srcCanvas, dx, dy);
|
|
378
369
|
return ctx.getImageData(0, 0, width, height);
|
|
@@ -532,11 +523,14 @@ class PDFDiffViewer {
|
|
|
532
523
|
_drawHighlightBoxes(ctx, boxes, color = 'red') {
|
|
533
524
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
|
534
525
|
const alpha = this.options.highlightAlpha;
|
|
526
|
+
|
|
527
|
+
// Use configured colors or fallback to color parameter
|
|
535
528
|
if (color === 'red') {
|
|
536
|
-
ctx.fillStyle =
|
|
529
|
+
ctx.fillStyle = this._hexToRgba(this.options.highlightColorA, alpha);
|
|
537
530
|
} else if (color === 'green') {
|
|
538
|
-
ctx.fillStyle =
|
|
531
|
+
ctx.fillStyle = this._hexToRgba(this.options.highlightColorB, alpha);
|
|
539
532
|
}
|
|
533
|
+
|
|
540
534
|
boxes.forEach(({ x, y, width, height }) => {
|
|
541
535
|
ctx.fillRect(x, y, width, height);
|
|
542
536
|
});
|
|
@@ -787,6 +781,26 @@ class PDFDiffViewer {
|
|
|
787
781
|
.split(/\s+/)
|
|
788
782
|
.filter(word => word.length > 2 && !stopwords.has(word));
|
|
789
783
|
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Convert hex color to rgba with alpha
|
|
787
|
+
*/
|
|
788
|
+
_hexToRgba(hex, alpha) {
|
|
789
|
+
// Handle both #RGB and #RRGGBB formats
|
|
790
|
+
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
|
791
|
+
hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);
|
|
792
|
+
|
|
793
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
794
|
+
if (result) {
|
|
795
|
+
const r = parseInt(result[1], 16);
|
|
796
|
+
const g = parseInt(result[2], 16);
|
|
797
|
+
const b = parseInt(result[3], 16);
|
|
798
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Fallback to red if invalid hex
|
|
802
|
+
return `rgba(255, 0, 0, ${alpha})`;
|
|
803
|
+
}
|
|
790
804
|
}
|
|
791
805
|
|
|
792
806
|
// Export for different module systems
|