pdf-diff-viewer 1.2.0 → 1.3.2

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/README.md CHANGED
@@ -136,15 +136,17 @@ new PDFDiffViewer(container, options)
136
136
  - `colorTolerance` (number) - Color difference threshold, default: 120
137
137
  - `minHighlightArea` (number) - Min area to highlight in pixels, default: 60
138
138
  - `minWordSize` (number) - Min word box size in pixels, default: 8
139
- - `highlightAlpha` (number) - Highlight transparency, default: 0.32
139
+ - `highlightAlpha` (number) - Highlight transparency (0-1), default: 0.32
140
+ - **`highlightColorA` (string)** - Hex color for Document A highlights, default: '#FF1744' (red)
141
+ - **`highlightColorB` (string)** - Hex color for Document B highlights, default: '#2196F3' (blue)
142
+ - **`backgroundFillColor` (string)** - Canvas background fill color, default: 'white'
140
143
  - `labelA` (string) - Label for first document, default: 'Document A'
141
144
  - `labelB` (string) - Label for second document, default: 'Document B'
142
145
  - `showPageNumbers` (boolean) - Show page numbers, default: true
143
146
  - `cropRegions` (Array) - Regions to crop: `[{ page: 1, x, y, width, height }]`
144
147
  - `maskRegions` (Array) - Regions to mask/ignore: `[{ page: 1, x, y, width, height }]`
145
- - **`smartAlignment` (boolean)** - Enable text-based page alignment for content reflow, default: true
146
- - **`alignmentTolerance` (number)** - Search range for matching pages (+/- pages), default: 2
147
- - **`similarityThreshold` (number)** - Minimum text similarity (0-1) for page matching, default: 0.3
148
+ - `alignmentTolerance` (number) - Search range for matching pages (+/- pages), default: 2
149
+ - `similarityThreshold` (number) - Minimum text similarity (0-1) for page matching, default: 0.3
148
150
 
149
151
  ### Methods
150
152
 
@@ -238,6 +240,41 @@ const viewer = new PDFDiffViewer('#container', {
238
240
  });
239
241
  ```
240
242
 
243
+ ### Custom Highlight Colors
244
+
245
+ ```javascript
246
+ // Perfect for gray backgrounds (#D9D9D9)
247
+ const viewer = new PDFDiffViewer('#container', {
248
+ highlightColorA: '#FF1744', // Vibrant red for Doc A changes
249
+ highlightColorB: '#2196F3', // Bright blue for Doc B changes
250
+ backgroundFillColor: '#D9D9D9', // Match your PDF background
251
+ highlightAlpha: 0.4 // Adjust transparency
252
+ });
253
+ ```
254
+
255
+ **Recommended color combinations:**
256
+
257
+ For light backgrounds:
258
+ ```javascript
259
+ // Red vs Blue (high contrast)
260
+ { highlightColorA: '#FF1744', highlightColorB: '#2196F3' }
261
+
262
+ // Purple vs Orange
263
+ { highlightColorA: '#9C27B0', highlightColorB: '#FF6F00' }
264
+
265
+ // Pink vs Teal
266
+ { highlightColorA: '#E91E63', highlightColorB: '#00BCD4' }
267
+ ```
268
+
269
+ For dark backgrounds:
270
+ ```javascript
271
+ // Bright yellow vs Cyan
272
+ { highlightColorA: '#FFEB3B', highlightColorB: '#00E5FF' }
273
+
274
+ // Lime vs Magenta
275
+ { highlightColorA: '#CDDC39', highlightColorB: '#E040FB' }
276
+ ```
277
+
241
278
  ### With Crop Regions (Compare Specific Areas)
242
279
 
243
280
  ```javascript
@@ -251,12 +288,12 @@ const viewer = new PDFDiffViewer('#container', {
251
288
  ### With Smart Alignment (Content Reflow Handling)
252
289
 
253
290
  ```javascript
291
+ // Smart alignment is now automatic! No configuration needed
254
292
  // Handles cases where content shifts across pages
255
293
  // (e.g., adding text pushes content to next page)
256
294
  const viewer = new PDFDiffViewer('#container', {
257
- smartAlignment: true, // Enable intelligent page matching
258
- alignmentTolerance: 2, // Search +/- 2 pages for matches
259
- similarityThreshold: 0.3 // Require 30% content similarity
295
+ alignmentTolerance: 2, // Search +/- 2 pages for matches (default)
296
+ similarityThreshold: 0.3 // Require 30% content similarity (default)
260
297
  });
261
298
 
262
299
  const results = await viewer.compare(pdfA, pdfB);
@@ -266,10 +303,10 @@ console.log('Page mappings:', results.pageMapping);
266
303
  ```
267
304
 
268
305
  **How it works:**
306
+ - Automatically detects and handles different page counts
269
307
  - Extracts text from all pages in both documents
270
308
  - Uses Jaccard similarity to find best-matching pages
271
- - Handles different page counts gracefully
272
- - Shows similarity scores in the UI
309
+ - Shows similarity scores in the results
273
310
 
274
311
  ### With Mask Regions (Ignore Dynamic Content)
275
312
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdf-diff-viewer",
3
- "version": "1.2.0",
3
+ "version": "1.3.2",
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;
@@ -358,7 +352,7 @@ class PDFDiffViewer {
358
352
  padded.height = targetHeight;
359
353
 
360
354
  const ctx = padded.getContext('2d');
361
- ctx.fillStyle = 'white';
355
+ ctx.fillStyle = this.options.backgroundFillColor;
362
356
  ctx.fillRect(0, 0, targetWidth, targetHeight);
363
357
  ctx.drawImage(srcCanvas, 0, 0);
364
358
  return padded;
@@ -369,7 +363,7 @@ class PDFDiffViewer {
369
363
  temp.width = width;
370
364
  temp.height = height;
371
365
  const ctx = temp.getContext('2d');
372
- ctx.fillStyle = 'white';
366
+ ctx.fillStyle = this.options.backgroundFillColor;
373
367
  ctx.fillRect(0, 0, width, height);
374
368
  ctx.drawImage(srcCanvas, dx, dy);
375
369
  return ctx.getImageData(0, 0, width, height);
@@ -529,11 +523,14 @@ class PDFDiffViewer {
529
523
  _drawHighlightBoxes(ctx, boxes, color = 'red') {
530
524
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
531
525
  const alpha = this.options.highlightAlpha;
526
+
527
+ // Use configured colors or fallback to color parameter
532
528
  if (color === 'red') {
533
- ctx.fillStyle = `rgba(255, 0, 0, ${alpha})`;
529
+ ctx.fillStyle = this._hexToRgba(this.options.highlightColorA, alpha);
534
530
  } else if (color === 'green') {
535
- ctx.fillStyle = `rgba(0, 200, 0, ${alpha})`;
531
+ ctx.fillStyle = this._hexToRgba(this.options.highlightColorB, alpha);
536
532
  }
533
+
537
534
  boxes.forEach(({ x, y, width, height }) => {
538
535
  ctx.fillRect(x, y, width, height);
539
536
  });
@@ -784,6 +781,26 @@ class PDFDiffViewer {
784
781
  .split(/\s+/)
785
782
  .filter(word => word.length > 2 && !stopwords.has(word));
786
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
+ }
787
804
  }
788
805
 
789
806
  // Export for different module systems