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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdf-diff-viewer",
3
- "version": "1.1.0",
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",
@@ -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 || 200,
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
- smartAlignment: options.smartAlignment !== false, // Enable text-based alignment
34
- alignmentTolerance: options.alignmentTolerance || 2, // Search +/- 2 pages for matches
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
- // Use smart alignment if enabled and page counts differ
76
- if (this.options.smartAlignment && docA.numPages !== docB.numPages) {
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
- if (docA.numPages === docB.numPages) {
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 = 'white';
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 = 'white';
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 = `rgba(255, 0, 0, ${alpha})`;
529
+ ctx.fillStyle = this._hexToRgba(this.options.highlightColorA, alpha);
537
530
  } else if (color === 'green') {
538
- ctx.fillStyle = `rgba(0, 200, 0, ${alpha})`;
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