hazo_pdf 1.5.6 → 1.5.8
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 +148 -1
- package/config/hazo_pdf_config.ini +45 -0
- package/dist/chunk-AOSHQP7D.js +12 -0
- package/dist/{chunk-GFRC6LEG.js → chunk-CXHR3TT6.js} +11 -8
- package/dist/{chunk-GFRC6LEG.js.map → chunk-CXHR3TT6.js.map} +1 -1
- package/dist/chunk-FXOJ3DPX.js +71 -0
- package/dist/chunk-FXOJ3DPX.js.map +1 -0
- package/dist/{chunk-HT7OPS2V.js → chunk-ROKBDX4O.js} +163 -6
- package/dist/chunk-ROKBDX4O.js.map +1 -0
- package/dist/{chunk-3LPXEBPN.js → chunk-RQOQLZRS.js} +2 -2
- package/dist/index.d.ts +87 -21
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/{pdf_saver-IOWST6TO.js → pdf_saver-JXAIRQVL.js} +4 -3
- package/dist/pdf_viewer-ZTWYOEX3.js +12 -0
- package/dist/pdf_viewer-ZTWYOEX3.js.map +1 -0
- package/dist/text_search-GW2VYMU6.js +9 -0
- package/dist/text_search-GW2VYMU6.js.map +1 -0
- package/package.json +5 -3
- package/dist/chunk-HT7OPS2V.js.map +0 -1
- package/dist/pdf_viewer-ZJG52QDR.js +0 -11
- /package/dist/{pdf_saver-IOWST6TO.js.map → chunk-AOSHQP7D.js.map} +0 -0
- /package/dist/{chunk-3LPXEBPN.js.map → chunk-RQOQLZRS.js.map} +0 -0
- /package/dist/{pdf_viewer-ZJG52QDR.js.map → pdf_saver-JXAIRQVL.js.map} +0 -0
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ A React component library for viewing and annotating PDF documents with support
|
|
|
6
6
|
|
|
7
7
|
- 📄 **PDF Viewing** - Render PDF documents with customizable zoom levels
|
|
8
8
|
- ✏️ **Annotations** - Square and FreeText annotation tools
|
|
9
|
+
- ✨ **Auto-Highlighting** - Automatically highlight extracted field values in PDFs
|
|
9
10
|
- 🔍 **Programmatic Highlights** - Ref-based API for creating and managing highlights programmatically
|
|
10
11
|
- 🎨 **Customizable Styling** - Extensive configuration options via INI file
|
|
11
12
|
- ⏰ **Timestamp Support** - Automatic timestamp appending to annotations
|
|
@@ -516,9 +517,155 @@ See `config/hazo_pdf_config.ini` in the project root for all available configura
|
|
|
516
517
|
|
|
517
518
|
---
|
|
518
519
|
|
|
520
|
+
## Auto-Highlighting
|
|
521
|
+
|
|
522
|
+
The PDF viewer can automatically highlight extracted field values in your PDFs. Simply provide field names and values via the `highlight_fields_info` prop, and the viewer will search for and highlight them automatically.
|
|
523
|
+
|
|
524
|
+
### Basic Usage
|
|
525
|
+
|
|
526
|
+
```tsx
|
|
527
|
+
import { PdfViewer } from 'hazo_pdf';
|
|
528
|
+
import 'hazo_pdf/styles.css';
|
|
529
|
+
|
|
530
|
+
function AutoHighlightExample() {
|
|
531
|
+
const extracted_data = {
|
|
532
|
+
document_date: '30 June 2024',
|
|
533
|
+
total_amount: '$29,696.60',
|
|
534
|
+
vendor_name: 'ACME Corporation',
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
return (
|
|
538
|
+
<div style={{ width: '100%', height: '800px' }}>
|
|
539
|
+
<PdfViewer
|
|
540
|
+
url="/invoice.pdf"
|
|
541
|
+
highlight_fields_info={[
|
|
542
|
+
{ field_name: 'document_date', value: '30 June 2024', page_index: 0 },
|
|
543
|
+
{ field_name: 'total_amount', value: '$29,696.60', page_index: 0 },
|
|
544
|
+
{ field_name: 'vendor_name', value: 'ACME Corporation', page_index: 0 },
|
|
545
|
+
]}
|
|
546
|
+
show_file_info_button={true}
|
|
547
|
+
/>
|
|
548
|
+
</div>
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
**That's it!** The viewer will:
|
|
554
|
+
1. Search for each value in the PDF text layer
|
|
555
|
+
2. Create visual highlight boxes at the found positions
|
|
556
|
+
3. Display field names and values in the File Info sidepanel
|
|
557
|
+
|
|
558
|
+
### How It Works
|
|
559
|
+
|
|
560
|
+
- **Smart Text Search**: Exact match first, then partial match with normalization (removes commas/spaces)
|
|
561
|
+
- **Automatic Cleanup**: Highlights are removed when the PDF changes or component unmounts
|
|
562
|
+
- **Non-Blocking**: Text search runs asynchronously without freezing the UI
|
|
563
|
+
- **Configurable**: Customize colors, opacity, padding, and search behavior
|
|
564
|
+
|
|
565
|
+
### HighlightFieldInfo Type
|
|
566
|
+
|
|
567
|
+
```typescript
|
|
568
|
+
interface HighlightFieldInfo {
|
|
569
|
+
field_name: string; // Field identifier (auto-formatted to Title Case in sidepanel)
|
|
570
|
+
value: string; // The text to search for and highlight
|
|
571
|
+
page_index?: number; // Page to search on (0-based, default: 0)
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Customization Options
|
|
576
|
+
|
|
577
|
+
#### Custom Highlight Colors
|
|
578
|
+
|
|
579
|
+
```tsx
|
|
580
|
+
<PdfViewer
|
|
581
|
+
url="/document.pdf"
|
|
582
|
+
highlight_fields_info={highlight_fields}
|
|
583
|
+
auto_highlight_options={{
|
|
584
|
+
border_color: '#0066CC',
|
|
585
|
+
background_color: '#E6F2FF',
|
|
586
|
+
background_opacity: 0.4,
|
|
587
|
+
border_width: 2,
|
|
588
|
+
}}
|
|
589
|
+
/>
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
#### Custom Search Options
|
|
593
|
+
|
|
594
|
+
```tsx
|
|
595
|
+
<PdfViewer
|
|
596
|
+
url="/document.pdf"
|
|
597
|
+
highlight_fields_info={highlight_fields}
|
|
598
|
+
auto_highlight_search_options={{
|
|
599
|
+
normalize: false, // Disable text normalization (exact match only)
|
|
600
|
+
padding_x: 5, // More horizontal padding
|
|
601
|
+
padding_y: 2, // More vertical padding
|
|
602
|
+
y_offset: -5, // Adjust vertical position
|
|
603
|
+
}}
|
|
604
|
+
/>
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
#### Disable Auto-Highlighting (Sidepanel Only)
|
|
608
|
+
|
|
609
|
+
```tsx
|
|
610
|
+
<PdfViewer
|
|
611
|
+
url="/document.pdf"
|
|
612
|
+
highlight_fields_info={highlight_fields}
|
|
613
|
+
auto_highlight_enabled={false} // Only show in sidepanel, no visual highlights
|
|
614
|
+
/>
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
### Configuration via INI File
|
|
618
|
+
|
|
619
|
+
You can set default auto-highlight styling in your config file:
|
|
620
|
+
|
|
621
|
+
```ini
|
|
622
|
+
[auto_highlight]
|
|
623
|
+
auto_highlight_border_color = #FF6B00
|
|
624
|
+
auto_highlight_background_color = #FFF3E0
|
|
625
|
+
auto_highlight_background_opacity = 0.3
|
|
626
|
+
auto_highlight_border_width = 1
|
|
627
|
+
auto_highlight_normalize_text = true
|
|
628
|
+
auto_highlight_padding_x = 2
|
|
629
|
+
auto_highlight_padding_y = 1
|
|
630
|
+
auto_highlight_y_offset = -3
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### Props
|
|
634
|
+
|
|
635
|
+
| Prop | Type | Default | Description |
|
|
636
|
+
|------|------|---------|-------------|
|
|
637
|
+
| `highlight_fields_info` | `HighlightFieldInfo[]` | `undefined` | Fields to highlight and display in sidepanel |
|
|
638
|
+
| `auto_highlight_enabled` | `boolean` | `true` | Enable/disable auto-highlighting feature |
|
|
639
|
+
| `auto_highlight_options` | `HighlightOptions` | From config | Custom highlight styling |
|
|
640
|
+
| `auto_highlight_search_options` | `Partial<TextSearchOptions>` | From config | Custom search behavior |
|
|
641
|
+
|
|
642
|
+
### Advanced: Custom Text Search
|
|
643
|
+
|
|
644
|
+
For advanced use cases, you can use the text search utility directly:
|
|
645
|
+
|
|
646
|
+
```tsx
|
|
647
|
+
import { find_text_in_pdf, type TextSearchOptions } from 'hazo_pdf';
|
|
648
|
+
|
|
649
|
+
const search_result = await find_text_in_pdf(pdf_document, 'Invoice #12345', {
|
|
650
|
+
page_index: 0,
|
|
651
|
+
normalize: true,
|
|
652
|
+
padding_x: 10,
|
|
653
|
+
padding_y: 5,
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
if (search_result) {
|
|
657
|
+
console.log('Found at:', search_result.x, search_result.y);
|
|
658
|
+
console.log('Match type:', search_result.match_type); // 'exact' | 'partial'
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
519
664
|
## Programmatic Highlight API
|
|
520
665
|
|
|
521
|
-
The PDF viewer exposes a ref-based API for programmatically creating, removing, and managing highlights. This
|
|
666
|
+
The PDF viewer also exposes a ref-based API for programmatically creating, removing, and managing highlights. This is useful when you need manual control over highlights or want to create highlights from coordinates.
|
|
667
|
+
|
|
668
|
+
**Note**: For most use cases, the [Auto-Highlighting](#auto-highlighting) feature is recommended as it's simpler and requires less code.
|
|
522
669
|
|
|
523
670
|
### Basic Usage
|
|
524
671
|
|
|
@@ -615,6 +615,51 @@ toolbar_show_metadata_button = false
|
|
|
615
615
|
# Default: 36 (0.5 inch)
|
|
616
616
|
# margin = 36
|
|
617
617
|
|
|
618
|
+
# =============================================================================
|
|
619
|
+
# [auto_highlight] - Auto-highlighting configuration
|
|
620
|
+
# =============================================================================
|
|
621
|
+
[auto_highlight]
|
|
622
|
+
|
|
623
|
+
# Border color for auto-created highlights (hex format: #RRGGBB)
|
|
624
|
+
# Color of the highlight border when using auto-highlighting feature
|
|
625
|
+
# Default: "#FF6B00" (orange)
|
|
626
|
+
# auto_highlight_border_color = #FF6B00
|
|
627
|
+
|
|
628
|
+
# Background color for auto-created highlights (hex format: #RRGGBB)
|
|
629
|
+
# Fill color inside the highlight box
|
|
630
|
+
# Default: "#FFF3E0" (light orange)
|
|
631
|
+
# auto_highlight_background_color = #FFF3E0
|
|
632
|
+
|
|
633
|
+
# Background opacity for auto-created highlights (0.0 to 1.0)
|
|
634
|
+
# Transparency level of the highlight background
|
|
635
|
+
# Default: 0.3
|
|
636
|
+
# auto_highlight_background_opacity = 0.3
|
|
637
|
+
|
|
638
|
+
# Border width for auto-created highlights in pixels
|
|
639
|
+
# Thickness of the highlight border
|
|
640
|
+
# Default: 1
|
|
641
|
+
# auto_highlight_border_width = 1
|
|
642
|
+
|
|
643
|
+
# Whether to normalize text during search (true/false)
|
|
644
|
+
# When enabled, removes commas and spaces from text before matching
|
|
645
|
+
# Default: true
|
|
646
|
+
# auto_highlight_normalize_text = true
|
|
647
|
+
|
|
648
|
+
# Horizontal padding around highlighted text (in PDF units)
|
|
649
|
+
# Extra space to add on left/right sides of the text box
|
|
650
|
+
# Default: 2
|
|
651
|
+
# auto_highlight_padding_x = 2
|
|
652
|
+
|
|
653
|
+
# Vertical padding around highlighted text (in PDF units)
|
|
654
|
+
# Extra space to add on top/bottom of the text box
|
|
655
|
+
# Default: 1
|
|
656
|
+
# auto_highlight_padding_y = 1
|
|
657
|
+
|
|
658
|
+
# Y-axis offset to adjust highlight position (in PDF units)
|
|
659
|
+
# Negative values move highlight down, positive values move up
|
|
660
|
+
# Default: -3
|
|
661
|
+
# auto_highlight_y_offset = -3
|
|
662
|
+
|
|
618
663
|
# =============================================================================
|
|
619
664
|
# [file_button] - File manager button configuration
|
|
620
665
|
# =============================================================================
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
__require
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=chunk-AOSHQP7D.js.map
|
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
-
}) : x)(function(x) {
|
|
5
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
-
});
|
|
8
2
|
|
|
9
3
|
// src/config/default_config.ts
|
|
10
4
|
var default_config = {
|
|
@@ -137,6 +131,16 @@ var default_config = {
|
|
|
137
131
|
image_fit: "fit",
|
|
138
132
|
margin: 36
|
|
139
133
|
},
|
|
134
|
+
auto_highlight: {
|
|
135
|
+
auto_highlight_border_color: "#FF6B00",
|
|
136
|
+
auto_highlight_background_color: "#FFF3E0",
|
|
137
|
+
auto_highlight_background_opacity: 0.3,
|
|
138
|
+
auto_highlight_border_width: 1,
|
|
139
|
+
auto_highlight_normalize_text: true,
|
|
140
|
+
auto_highlight_padding_x: 2,
|
|
141
|
+
auto_highlight_padding_y: 1,
|
|
142
|
+
auto_highlight_y_offset: -3
|
|
143
|
+
},
|
|
140
144
|
file_button: {
|
|
141
145
|
icon_size: 24,
|
|
142
146
|
icon_color: "#6b7280",
|
|
@@ -148,7 +152,6 @@ var default_config = {
|
|
|
148
152
|
};
|
|
149
153
|
|
|
150
154
|
export {
|
|
151
|
-
__require,
|
|
152
155
|
default_config
|
|
153
156
|
};
|
|
154
|
-
//# sourceMappingURL=chunk-
|
|
157
|
+
//# sourceMappingURL=chunk-CXHR3TT6.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/default_config.ts"],"sourcesContent":["/**\n * Default configuration values for hazo_pdf\n * All styling defaults organized by category\n */\n\nimport type { PdfViewerConfig } from '../types/config';\n\n/**\n * Default PDF Viewer Configuration\n * These values are used when no config file is provided or when values are missing\n */\nexport const default_config: PdfViewerConfig = {\n fonts: {\n freetext_font_family: 'Arial, sans-serif',\n freetext_font_size_min: 12,\n freetext_font_size_max: 24,\n freetext_font_size_default: 14,\n font_foreground_color: '#000000', // Black by default\n },\n\n highlight_annotation: {\n highlight_fill_color: '#FFFF00',\n highlight_fill_opacity: 0.3,\n highlight_border_color: '#FFD700',\n highlight_border_color_hover: '#FFD700',\n highlight_fill_opacity_hover: 0.4,\n },\n\n square_annotation: {\n square_fill_color: '#FF0000',\n square_fill_opacity: 0.2,\n square_border_color: '#FF0000',\n square_border_color_hover: '#CC0000',\n square_fill_opacity_hover: 0.3,\n },\n\n freetext_annotation: {\n freetext_text_color: '#000000',\n freetext_text_color_hover: '#000000',\n freetext_border_color: '#003366',\n freetext_border_width: 1,\n freetext_background_color: '#E6F3FF',\n freetext_background_opacity: 0.1,\n freetext_font_weight: 'normal',\n freetext_font_style: 'normal',\n freetext_text_decoration: 'none',\n freetext_padding_horizontal: 4,\n freetext_padding_vertical: 2,\n },\n\n page_styling: {\n page_border_color: '#999999',\n page_box_shadow: '0 2px 8px rgba(0, 0, 0, 0.3)',\n page_background_color: '#ffffff',\n },\n\n viewer: {\n viewer_background_color: '#2d2d2d',\n append_timestamp_to_text_edits: false,\n annotation_text_suffix_fixed_text: '',\n add_enclosing_brackets_to_suffixes: true,\n suffix_enclosing_brackets: '[]',\n suffix_text_position: 'below_multi_line',\n },\n\n context_menu: {\n context_menu_background_color: '#ffffff',\n context_menu_border_color: '#d1d5db',\n context_menu_item_hover_background: '#f3f4f6',\n context_menu_item_disabled_opacity: 0.5,\n right_click_custom_stamps: '', // Empty string means no custom stamps\n },\n\n dialog: {\n dialog_backdrop_opacity: 0.2,\n dialog_background_color: '#ffffff',\n dialog_border_color: '#d1d5db',\n dialog_button_submit_color: '#16a34a',\n dialog_button_submit_color_hover: '#15803d',\n dialog_button_cancel_color: '#6b7280',\n dialog_button_cancel_color_hover: '#4b5563',\n dialog_button_disabled_opacity: 0.4,\n },\n\n toolbar: {\n toolbar_background_color: '#f9fafb',\n toolbar_border_color: '#e5e7eb',\n toolbar_font_family: 'system-ui, -apple-system, sans-serif',\n toolbar_font_size: 14,\n toolbar_font_color: '#111827',\n toolbar_button_background_color: '#ffffff',\n toolbar_button_background_color_hover: '#f3f4f6',\n toolbar_button_text_color: '#374151',\n toolbar_button_active_background_color: '#3b82f6',\n toolbar_button_active_text_color: '#ffffff',\n toolbar_button_save_background_color: '#10b981',\n toolbar_button_save_background_color_hover: '#059669',\n toolbar_button_save_text_color: '#ffffff',\n toolbar_button_disabled_opacity: 0.5,\n toolbar_show_zoom_controls: true,\n toolbar_show_rotation_controls: true,\n toolbar_show_square_button: true,\n toolbar_show_undo_button: true,\n toolbar_show_redo_button: true,\n toolbar_show_save_button: true,\n toolbar_show_metadata_button: true,\n toolbar_show_annotate_button: true,\n toolbar_show_file_info_button: true,\n toolbar_show_extract_button: true,\n toolbar_zoom_out_label: '−',\n toolbar_zoom_in_label: '+',\n toolbar_zoom_reset_label: 'Reset',\n toolbar_square_label: 'Square',\n toolbar_undo_label: 'Undo',\n toolbar_redo_label: 'Redo',\n toolbar_save_label: 'Save',\n toolbar_saving_label: 'Saving...',\n toolbar_metadata_label: 'Metadata',\n },\n\n file_manager: {\n file_manager_enabled: false,\n show_file_list: true,\n allow_delete: true,\n show_popout_button: true,\n file_list_height: 60,\n selected_color: '#3b82f6',\n file_list_background_color: '#f9fafb',\n file_list_border_color: '#e5e7eb',\n },\n\n file_upload: {\n upload_enabled: true,\n allowed_types: 'application/pdf,image/jpeg,image/png,image/gif,image/webp,text/plain,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel',\n max_file_size: 10485760, // 10MB\n max_files: 10,\n show_add_button: true,\n dropzone_border_color: '#d1d5db',\n dropzone_border_color_active: '#3b82f6',\n dropzone_background_color: '#f9fafb',\n },\n\n pdf_conversion: {\n conversion_enabled: true,\n page_size: 'letter',\n image_quality: 0.85,\n image_fit: 'fit',\n margin: 36,\n },\n\n file_button: {\n icon_size: 24,\n icon_color: '#6b7280',\n icon_color_hover: '#374151',\n icon_color_with_files: '#3b82f6',\n badge_background: '#3b82f6',\n badge_text_color: '#ffffff',\n },\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/config/default_config.ts"],"sourcesContent":["/**\n * Default configuration values for hazo_pdf\n * All styling defaults organized by category\n */\n\nimport type { PdfViewerConfig } from '../types/config';\n\n/**\n * Default PDF Viewer Configuration\n * These values are used when no config file is provided or when values are missing\n */\nexport const default_config: PdfViewerConfig = {\n fonts: {\n freetext_font_family: 'Arial, sans-serif',\n freetext_font_size_min: 12,\n freetext_font_size_max: 24,\n freetext_font_size_default: 14,\n font_foreground_color: '#000000', // Black by default\n },\n\n highlight_annotation: {\n highlight_fill_color: '#FFFF00',\n highlight_fill_opacity: 0.3,\n highlight_border_color: '#FFD700',\n highlight_border_color_hover: '#FFD700',\n highlight_fill_opacity_hover: 0.4,\n },\n\n square_annotation: {\n square_fill_color: '#FF0000',\n square_fill_opacity: 0.2,\n square_border_color: '#FF0000',\n square_border_color_hover: '#CC0000',\n square_fill_opacity_hover: 0.3,\n },\n\n freetext_annotation: {\n freetext_text_color: '#000000',\n freetext_text_color_hover: '#000000',\n freetext_border_color: '#003366',\n freetext_border_width: 1,\n freetext_background_color: '#E6F3FF',\n freetext_background_opacity: 0.1,\n freetext_font_weight: 'normal',\n freetext_font_style: 'normal',\n freetext_text_decoration: 'none',\n freetext_padding_horizontal: 4,\n freetext_padding_vertical: 2,\n },\n\n page_styling: {\n page_border_color: '#999999',\n page_box_shadow: '0 2px 8px rgba(0, 0, 0, 0.3)',\n page_background_color: '#ffffff',\n },\n\n viewer: {\n viewer_background_color: '#2d2d2d',\n append_timestamp_to_text_edits: false,\n annotation_text_suffix_fixed_text: '',\n add_enclosing_brackets_to_suffixes: true,\n suffix_enclosing_brackets: '[]',\n suffix_text_position: 'below_multi_line',\n },\n\n context_menu: {\n context_menu_background_color: '#ffffff',\n context_menu_border_color: '#d1d5db',\n context_menu_item_hover_background: '#f3f4f6',\n context_menu_item_disabled_opacity: 0.5,\n right_click_custom_stamps: '', // Empty string means no custom stamps\n },\n\n dialog: {\n dialog_backdrop_opacity: 0.2,\n dialog_background_color: '#ffffff',\n dialog_border_color: '#d1d5db',\n dialog_button_submit_color: '#16a34a',\n dialog_button_submit_color_hover: '#15803d',\n dialog_button_cancel_color: '#6b7280',\n dialog_button_cancel_color_hover: '#4b5563',\n dialog_button_disabled_opacity: 0.4,\n },\n\n toolbar: {\n toolbar_background_color: '#f9fafb',\n toolbar_border_color: '#e5e7eb',\n toolbar_font_family: 'system-ui, -apple-system, sans-serif',\n toolbar_font_size: 14,\n toolbar_font_color: '#111827',\n toolbar_button_background_color: '#ffffff',\n toolbar_button_background_color_hover: '#f3f4f6',\n toolbar_button_text_color: '#374151',\n toolbar_button_active_background_color: '#3b82f6',\n toolbar_button_active_text_color: '#ffffff',\n toolbar_button_save_background_color: '#10b981',\n toolbar_button_save_background_color_hover: '#059669',\n toolbar_button_save_text_color: '#ffffff',\n toolbar_button_disabled_opacity: 0.5,\n toolbar_show_zoom_controls: true,\n toolbar_show_rotation_controls: true,\n toolbar_show_square_button: true,\n toolbar_show_undo_button: true,\n toolbar_show_redo_button: true,\n toolbar_show_save_button: true,\n toolbar_show_metadata_button: true,\n toolbar_show_annotate_button: true,\n toolbar_show_file_info_button: true,\n toolbar_show_extract_button: true,\n toolbar_zoom_out_label: '−',\n toolbar_zoom_in_label: '+',\n toolbar_zoom_reset_label: 'Reset',\n toolbar_square_label: 'Square',\n toolbar_undo_label: 'Undo',\n toolbar_redo_label: 'Redo',\n toolbar_save_label: 'Save',\n toolbar_saving_label: 'Saving...',\n toolbar_metadata_label: 'Metadata',\n },\n\n file_manager: {\n file_manager_enabled: false,\n show_file_list: true,\n allow_delete: true,\n show_popout_button: true,\n file_list_height: 60,\n selected_color: '#3b82f6',\n file_list_background_color: '#f9fafb',\n file_list_border_color: '#e5e7eb',\n },\n\n file_upload: {\n upload_enabled: true,\n allowed_types: 'application/pdf,image/jpeg,image/png,image/gif,image/webp,text/plain,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel',\n max_file_size: 10485760, // 10MB\n max_files: 10,\n show_add_button: true,\n dropzone_border_color: '#d1d5db',\n dropzone_border_color_active: '#3b82f6',\n dropzone_background_color: '#f9fafb',\n },\n\n pdf_conversion: {\n conversion_enabled: true,\n page_size: 'letter',\n image_quality: 0.85,\n image_fit: 'fit',\n margin: 36,\n },\n\n auto_highlight: {\n auto_highlight_border_color: '#FF6B00',\n auto_highlight_background_color: '#FFF3E0',\n auto_highlight_background_opacity: 0.3,\n auto_highlight_border_width: 1,\n auto_highlight_normalize_text: true,\n auto_highlight_padding_x: 2,\n auto_highlight_padding_y: 1,\n auto_highlight_y_offset: -3,\n },\n\n file_button: {\n icon_size: 24,\n icon_color: '#6b7280',\n icon_color_hover: '#374151',\n icon_color_with_files: '#3b82f6',\n badge_background: '#3b82f6',\n badge_text_color: '#ffffff',\n },\n};\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/utils/text_search.ts
|
|
4
|
+
async function find_text_in_pdf(pdf, search_value, options) {
|
|
5
|
+
try {
|
|
6
|
+
const {
|
|
7
|
+
page_index,
|
|
8
|
+
normalize = true,
|
|
9
|
+
min_partial_match_length = 3,
|
|
10
|
+
padding_x = 2,
|
|
11
|
+
padding_y = 1,
|
|
12
|
+
y_offset = -3
|
|
13
|
+
} = options;
|
|
14
|
+
const page = await pdf.getPage(page_index + 1);
|
|
15
|
+
const text_content = await page.getTextContent();
|
|
16
|
+
const normalized_search = normalize ? search_value.toString().replace(/[,\s]/g, "").toLowerCase() : search_value.toString().toLowerCase();
|
|
17
|
+
for (const item of text_content.items) {
|
|
18
|
+
if ("str" in item) {
|
|
19
|
+
const text_item = item;
|
|
20
|
+
const text = normalize ? text_item.str.replace(/[,\s]/g, "").toLowerCase() : text_item.str.toLowerCase();
|
|
21
|
+
if (text === normalized_search) {
|
|
22
|
+
const result = extract_text_position(text_item, padding_x, padding_y, y_offset);
|
|
23
|
+
return {
|
|
24
|
+
...result,
|
|
25
|
+
match_type: "exact",
|
|
26
|
+
matched_text: text_item.str
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (normalized_search.length >= min_partial_match_length) {
|
|
32
|
+
for (const item of text_content.items) {
|
|
33
|
+
if ("str" in item) {
|
|
34
|
+
const text_item = item;
|
|
35
|
+
const text = normalize ? text_item.str.replace(/[,\s]/g, "").toLowerCase() : text_item.str.toLowerCase();
|
|
36
|
+
if (text.includes(normalized_search)) {
|
|
37
|
+
const result = extract_text_position(text_item, padding_x, padding_y, y_offset);
|
|
38
|
+
return {
|
|
39
|
+
...result,
|
|
40
|
+
match_type: "partial",
|
|
41
|
+
matched_text: text_item.str
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
} catch (err) {
|
|
49
|
+
throw new Error(`Text search failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function extract_text_position(text_item, padding_x, padding_y, y_offset) {
|
|
53
|
+
const x = text_item.transform[4];
|
|
54
|
+
const y = text_item.transform[5];
|
|
55
|
+
const font_size = Math.abs(text_item.transform[0]) || 10;
|
|
56
|
+
const base_width = text_item.width || text_item.str.length * font_size * 0.55;
|
|
57
|
+
const base_height = text_item.height || font_size;
|
|
58
|
+
const width = base_width + padding_x * 2;
|
|
59
|
+
const height = base_height + padding_y * 2;
|
|
60
|
+
return {
|
|
61
|
+
x: x - padding_x,
|
|
62
|
+
y: y - padding_y + y_offset,
|
|
63
|
+
width,
|
|
64
|
+
height
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
find_text_in_pdf
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=chunk-FXOJ3DPX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/text_search.ts"],"sourcesContent":["/**\n * Text search utility for finding text in PDF documents\n * Extracted from test app to be reusable across the library\n */\n\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\n\n/**\n * Options for text search in PDF\n */\nexport interface TextSearchOptions {\n /** Page index to search (0-based) */\n page_index: number;\n\n /** Whether to normalize text by removing commas and spaces (default: true) */\n normalize?: boolean;\n\n /** Minimum length for partial matches (default: 3) */\n min_partial_match_length?: number;\n\n /** Horizontal padding around text (default: 2) */\n padding_x?: number;\n\n /** Vertical padding around text (default: 1) */\n padding_y?: number;\n\n /** Y-axis offset to adjust highlight position (default: -3) */\n y_offset?: number;\n}\n\n/**\n * Result of text search in PDF\n */\nexport interface TextSearchResult {\n /** X coordinate in PDF coordinate system */\n x: number;\n\n /** Y coordinate in PDF coordinate system */\n y: number;\n\n /** Width of the text box */\n width: number;\n\n /** Height of the text box */\n height: number;\n\n /** Type of match found */\n match_type: 'exact' | 'partial';\n\n /** The actual text that was matched */\n matched_text: string;\n}\n\n/**\n * Find text in a PDF document and return its position\n *\n * @param pdf - PDF document proxy from pdfjs\n * @param search_value - Text to search for\n * @param options - Search options\n * @returns Position and dimensions of the text, or null if not found\n */\nexport async function find_text_in_pdf(\n pdf: PDFDocumentProxy,\n search_value: string,\n options: TextSearchOptions\n): Promise<TextSearchResult | null> {\n try {\n const {\n page_index,\n normalize = true,\n min_partial_match_length = 3,\n padding_x = 2,\n padding_y = 1,\n y_offset = -3,\n } = options;\n\n // Get page (pdfjs uses 1-based indexing)\n const page = await pdf.getPage(page_index + 1);\n const text_content = await page.getTextContent();\n\n // Normalize search value if enabled\n const normalized_search = normalize\n ? search_value.toString().replace(/[,\\s]/g, '').toLowerCase()\n : search_value.toString().toLowerCase();\n\n // First pass: look for exact match\n for (const item of text_content.items) {\n if ('str' in item) {\n const text_item = item as { str: string; transform: number[]; width?: number; height?: number };\n const text = normalize\n ? text_item.str.replace(/[,\\s]/g, '').toLowerCase()\n : text_item.str.toLowerCase();\n\n // Only match if this text item equals our search value\n if (text === normalized_search) {\n const result = extract_text_position(text_item, padding_x, padding_y, y_offset);\n return {\n ...result,\n match_type: 'exact',\n matched_text: text_item.str,\n };\n }\n }\n }\n\n // Second pass: look for partial match (text item contains search value)\n if (normalized_search.length >= min_partial_match_length) {\n for (const item of text_content.items) {\n if ('str' in item) {\n const text_item = item as { str: string; transform: number[]; width?: number; height?: number };\n const text = normalize\n ? text_item.str.replace(/[,\\s]/g, '').toLowerCase()\n : text_item.str.toLowerCase();\n\n if (text.includes(normalized_search)) {\n const result = extract_text_position(text_item, padding_x, padding_y, y_offset);\n return {\n ...result,\n match_type: 'partial',\n matched_text: text_item.str,\n };\n }\n }\n }\n }\n\n return null;\n } catch (err) {\n throw new Error(`Text search failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n\n/**\n * Extract position and dimensions from a PDF text item\n * @private\n */\nfunction extract_text_position(\n text_item: { str: string; transform: number[]; width?: number; height?: number },\n padding_x: number,\n padding_y: number,\n y_offset: number\n): { x: number; y: number; width: number; height: number } {\n // Extract position from transform matrix [a, b, c, d, x, y]\n const x = text_item.transform[4];\n const y = text_item.transform[5];\n\n // Calculate dimensions\n const font_size = Math.abs(text_item.transform[0]) || 10;\n const base_width = text_item.width || (text_item.str.length * font_size * 0.55);\n const base_height = text_item.height || font_size;\n\n // Add padding for better visibility\n const width = base_width + (padding_x * 2);\n const height = base_height + (padding_y * 2);\n\n return {\n x: x - padding_x,\n y: y - padding_y + y_offset,\n width,\n height,\n };\n}\n"],"mappings":";;;AA6DA,eAAsB,iBACpB,KACA,cACA,SACkC;AAClC,MAAI;AACF,UAAM;AAAA,MACJ;AAAA,MACA,YAAY;AAAA,MACZ,2BAA2B;AAAA,MAC3B,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,WAAW;AAAA,IACb,IAAI;AAGJ,UAAM,OAAO,MAAM,IAAI,QAAQ,aAAa,CAAC;AAC7C,UAAM,eAAe,MAAM,KAAK,eAAe;AAG/C,UAAM,oBAAoB,YACtB,aAAa,SAAS,EAAE,QAAQ,UAAU,EAAE,EAAE,YAAY,IAC1D,aAAa,SAAS,EAAE,YAAY;AAGxC,eAAW,QAAQ,aAAa,OAAO;AACrC,UAAI,SAAS,MAAM;AACjB,cAAM,YAAY;AAClB,cAAM,OAAO,YACT,UAAU,IAAI,QAAQ,UAAU,EAAE,EAAE,YAAY,IAChD,UAAU,IAAI,YAAY;AAG9B,YAAI,SAAS,mBAAmB;AAC9B,gBAAM,SAAS,sBAAsB,WAAW,WAAW,WAAW,QAAQ;AAC9E,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,YAAY;AAAA,YACZ,cAAc,UAAU;AAAA,UAC1B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,kBAAkB,UAAU,0BAA0B;AACxD,iBAAW,QAAQ,aAAa,OAAO;AACrC,YAAI,SAAS,MAAM;AACjB,gBAAM,YAAY;AAClB,gBAAM,OAAO,YACT,UAAU,IAAI,QAAQ,UAAU,EAAE,EAAE,YAAY,IAChD,UAAU,IAAI,YAAY;AAE9B,cAAI,KAAK,SAAS,iBAAiB,GAAG;AACpC,kBAAM,SAAS,sBAAsB,WAAW,WAAW,WAAW,QAAQ;AAC9E,mBAAO;AAAA,cACL,GAAG;AAAA,cACH,YAAY;AAAA,cACZ,cAAc,UAAU;AAAA,YAC1B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,UAAM,IAAI,MAAM,uBAAuB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,EAC3F;AACF;AAMA,SAAS,sBACP,WACA,WACA,WACA,UACyD;AAEzD,QAAM,IAAI,UAAU,UAAU,CAAC;AAC/B,QAAM,IAAI,UAAU,UAAU,CAAC;AAG/B,QAAM,YAAY,KAAK,IAAI,UAAU,UAAU,CAAC,CAAC,KAAK;AACtD,QAAM,aAAa,UAAU,SAAU,UAAU,IAAI,SAAS,YAAY;AAC1E,QAAM,cAAc,UAAU,UAAU;AAGxC,QAAM,QAAQ,aAAc,YAAY;AACxC,QAAM,SAAS,cAAe,YAAY;AAE1C,SAAO;AAAA,IACL,GAAG,IAAI;AAAA,IACP,GAAG,IAAI,YAAY;AAAA,IACnB;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
|
-
__require,
|
|
4
3
|
default_config
|
|
5
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-CXHR3TT6.js";
|
|
5
|
+
import {
|
|
6
|
+
__require
|
|
7
|
+
} from "./chunk-AOSHQP7D.js";
|
|
6
8
|
|
|
7
9
|
// src/components/pdf_viewer/pdf_viewer.tsx
|
|
8
10
|
import { useState as useState13, useEffect as useEffect10, useRef as useRef11, useCallback as useCallback4, forwardRef, useImperativeHandle } from "react";
|
|
@@ -2790,6 +2792,40 @@ function build_config_from_ini(get_value) {
|
|
|
2790
2792
|
default_config.pdf_conversion.margin
|
|
2791
2793
|
)
|
|
2792
2794
|
},
|
|
2795
|
+
auto_highlight: {
|
|
2796
|
+
auto_highlight_border_color: parse_color(
|
|
2797
|
+
get_value("auto_highlight", "auto_highlight_border_color"),
|
|
2798
|
+
default_config.auto_highlight.auto_highlight_border_color
|
|
2799
|
+
),
|
|
2800
|
+
auto_highlight_background_color: parse_color(
|
|
2801
|
+
get_value("auto_highlight", "auto_highlight_background_color"),
|
|
2802
|
+
default_config.auto_highlight.auto_highlight_background_color
|
|
2803
|
+
),
|
|
2804
|
+
auto_highlight_background_opacity: parse_opacity(
|
|
2805
|
+
get_value("auto_highlight", "auto_highlight_background_opacity"),
|
|
2806
|
+
default_config.auto_highlight.auto_highlight_background_opacity
|
|
2807
|
+
),
|
|
2808
|
+
auto_highlight_border_width: parse_number(
|
|
2809
|
+
get_value("auto_highlight", "auto_highlight_border_width"),
|
|
2810
|
+
default_config.auto_highlight.auto_highlight_border_width
|
|
2811
|
+
),
|
|
2812
|
+
auto_highlight_normalize_text: parse_boolean(
|
|
2813
|
+
get_value("auto_highlight", "auto_highlight_normalize_text"),
|
|
2814
|
+
default_config.auto_highlight.auto_highlight_normalize_text
|
|
2815
|
+
),
|
|
2816
|
+
auto_highlight_padding_x: parse_number(
|
|
2817
|
+
get_value("auto_highlight", "auto_highlight_padding_x"),
|
|
2818
|
+
default_config.auto_highlight.auto_highlight_padding_x
|
|
2819
|
+
),
|
|
2820
|
+
auto_highlight_padding_y: parse_number(
|
|
2821
|
+
get_value("auto_highlight", "auto_highlight_padding_y"),
|
|
2822
|
+
default_config.auto_highlight.auto_highlight_padding_y
|
|
2823
|
+
),
|
|
2824
|
+
auto_highlight_y_offset: parse_number(
|
|
2825
|
+
get_value("auto_highlight", "auto_highlight_y_offset"),
|
|
2826
|
+
default_config.auto_highlight.auto_highlight_y_offset
|
|
2827
|
+
)
|
|
2828
|
+
},
|
|
2793
2829
|
file_button: {
|
|
2794
2830
|
icon_size: parse_number(
|
|
2795
2831
|
get_value("file_button", "icon_size"),
|
|
@@ -4434,7 +4470,11 @@ var PdfViewer = forwardRef(({
|
|
|
4434
4470
|
save_path,
|
|
4435
4471
|
// File info sidepanel data props
|
|
4436
4472
|
doc_data,
|
|
4437
|
-
highlight_fields_info
|
|
4473
|
+
highlight_fields_info,
|
|
4474
|
+
// Auto-highlight props
|
|
4475
|
+
auto_highlight_enabled,
|
|
4476
|
+
auto_highlight_options,
|
|
4477
|
+
auto_highlight_search_options
|
|
4438
4478
|
}, ref) => {
|
|
4439
4479
|
const [pdf_document, setPdfDocument] = useState13(null);
|
|
4440
4480
|
const [loading, setLoading] = useState13(true);
|
|
@@ -4451,6 +4491,7 @@ var PdfViewer = forwardRef(({
|
|
|
4451
4491
|
const [file_info_sidepanel_open, setFileInfoSidepanelOpen] = useState13(false);
|
|
4452
4492
|
const [file_info_sidepanel_width, setFileInfoSidepanelWidth] = useState13(300);
|
|
4453
4493
|
const [hazo_files_available, setHazoFilesAvailable] = useState13(null);
|
|
4494
|
+
const [auto_highlight_ids, setAutoHighlightIds] = useState13(/* @__PURE__ */ new Set());
|
|
4454
4495
|
const [page_rotations, setPageRotations] = useState13(/* @__PURE__ */ new Map());
|
|
4455
4496
|
const [current_visible_page, setCurrentVisiblePage] = useState13(0);
|
|
4456
4497
|
const [current_file, setCurrentFile] = useState13(
|
|
@@ -4798,8 +4839,23 @@ ${suffix_line}`;
|
|
|
4798
4839
|
setCachedPdfData(cached_copy);
|
|
4799
4840
|
pdf_data = loaded_data;
|
|
4800
4841
|
} else {
|
|
4801
|
-
logger2.debug("[PdfViewer] Loading PDF via fetch, caching for extraction");
|
|
4842
|
+
logger2.debug("[PdfViewer] Loading PDF via fetch, caching for extraction", { url: effective_url });
|
|
4802
4843
|
const response = await fetch(effective_url);
|
|
4844
|
+
if (!response.ok) {
|
|
4845
|
+
const error_text = await response.text().catch(() => "");
|
|
4846
|
+
const truncated = error_text.length > 200 ? error_text.slice(0, 200) + "..." : error_text;
|
|
4847
|
+
throw new Error(
|
|
4848
|
+
`Failed to fetch PDF (${response.status} ${response.statusText}): ${truncated || "No response body"}`
|
|
4849
|
+
);
|
|
4850
|
+
}
|
|
4851
|
+
const content_type = response.headers.get("content-type") || "";
|
|
4852
|
+
if (!content_type.includes("application/pdf") && !content_type.includes("application/octet-stream")) {
|
|
4853
|
+
logger2.warn("[PdfViewer] Unexpected content type for PDF", {
|
|
4854
|
+
url: effective_url,
|
|
4855
|
+
content_type,
|
|
4856
|
+
note: "Expected application/pdf or application/octet-stream"
|
|
4857
|
+
});
|
|
4858
|
+
}
|
|
4803
4859
|
const array_buffer = await response.arrayBuffer();
|
|
4804
4860
|
const cached_copy = array_buffer.slice(0);
|
|
4805
4861
|
setCachedPdfData(cached_copy);
|
|
@@ -5099,6 +5155,107 @@ ${suffix_line}`;
|
|
|
5099
5155
|
console.error("PdfViewer: Failed to popout:", err);
|
|
5100
5156
|
}
|
|
5101
5157
|
}, [files, current_file, viewer_title, on_popout, popout_route]);
|
|
5158
|
+
useEffect10(() => {
|
|
5159
|
+
const should_auto_highlight = auto_highlight_enabled !== false && // Default true
|
|
5160
|
+
highlight_fields_info && highlight_fields_info.length > 0 && pdf_document;
|
|
5161
|
+
if (!should_auto_highlight) {
|
|
5162
|
+
return;
|
|
5163
|
+
}
|
|
5164
|
+
const logger2 = get_logger();
|
|
5165
|
+
auto_highlight_ids.forEach((id) => {
|
|
5166
|
+
const annotation = annotations.find((a) => a.id === id);
|
|
5167
|
+
if (annotation) {
|
|
5168
|
+
handle_annotation_delete(id);
|
|
5169
|
+
}
|
|
5170
|
+
});
|
|
5171
|
+
setAutoHighlightIds(/* @__PURE__ */ new Set());
|
|
5172
|
+
const perform_auto_highlights = async () => {
|
|
5173
|
+
const { find_text_in_pdf } = await import("./text_search-GW2VYMU6.js");
|
|
5174
|
+
const new_ids = /* @__PURE__ */ new Set();
|
|
5175
|
+
const auto_config = config_ref.current?.auto_highlight || default_config.auto_highlight;
|
|
5176
|
+
const search_opts = {
|
|
5177
|
+
normalize: auto_config.auto_highlight_normalize_text,
|
|
5178
|
+
padding_x: auto_config.auto_highlight_padding_x,
|
|
5179
|
+
padding_y: auto_config.auto_highlight_padding_y,
|
|
5180
|
+
y_offset: auto_config.auto_highlight_y_offset,
|
|
5181
|
+
...auto_highlight_search_options
|
|
5182
|
+
};
|
|
5183
|
+
const highlight_opts = auto_highlight_options || {
|
|
5184
|
+
border_color: auto_config.auto_highlight_border_color,
|
|
5185
|
+
background_color: auto_config.auto_highlight_background_color,
|
|
5186
|
+
background_opacity: auto_config.auto_highlight_background_opacity,
|
|
5187
|
+
border_width: auto_config.auto_highlight_border_width
|
|
5188
|
+
};
|
|
5189
|
+
for (const field of highlight_fields_info) {
|
|
5190
|
+
try {
|
|
5191
|
+
const page_idx = field.page_index ?? 0;
|
|
5192
|
+
const result = await find_text_in_pdf(pdf_document, field.value, {
|
|
5193
|
+
page_index: page_idx,
|
|
5194
|
+
...search_opts
|
|
5195
|
+
});
|
|
5196
|
+
if (result) {
|
|
5197
|
+
logger2.debug("[AutoHighlight] Found text", {
|
|
5198
|
+
field: field.field_name,
|
|
5199
|
+
value: field.value,
|
|
5200
|
+
match_type: result.match_type,
|
|
5201
|
+
position: { x: result.x, y: result.y }
|
|
5202
|
+
});
|
|
5203
|
+
const rect = [
|
|
5204
|
+
result.x,
|
|
5205
|
+
result.y,
|
|
5206
|
+
result.x + result.width,
|
|
5207
|
+
result.y + result.height
|
|
5208
|
+
];
|
|
5209
|
+
const highlight_config = config_ref.current?.highlight_annotation || default_config.highlight_annotation;
|
|
5210
|
+
const annotation = {
|
|
5211
|
+
id: `auto_highlight_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
5212
|
+
type: "Highlight",
|
|
5213
|
+
page_index: page_idx,
|
|
5214
|
+
rect,
|
|
5215
|
+
author: "AutoHighlight",
|
|
5216
|
+
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5217
|
+
contents: field.field_name,
|
|
5218
|
+
// Store field name in contents
|
|
5219
|
+
color: highlight_opts.background_color || highlight_config.highlight_fill_color,
|
|
5220
|
+
flags: "api_highlight"
|
|
5221
|
+
// Marker to identify API-created highlights
|
|
5222
|
+
};
|
|
5223
|
+
annotation.subject = JSON.stringify({
|
|
5224
|
+
border_color: highlight_opts.border_color,
|
|
5225
|
+
background_color: highlight_opts.background_color,
|
|
5226
|
+
background_opacity: highlight_opts.background_opacity,
|
|
5227
|
+
border_width: highlight_opts.border_width
|
|
5228
|
+
});
|
|
5229
|
+
handle_annotation_create(annotation);
|
|
5230
|
+
new_ids.add(annotation.id);
|
|
5231
|
+
} else {
|
|
5232
|
+
logger2.warn("[AutoHighlight] Text not found in PDF", {
|
|
5233
|
+
field: field.field_name,
|
|
5234
|
+
value: field.value,
|
|
5235
|
+
page: page_idx
|
|
5236
|
+
});
|
|
5237
|
+
}
|
|
5238
|
+
} catch (err) {
|
|
5239
|
+
logger2.error("[AutoHighlight] Search failed", {
|
|
5240
|
+
field: field.field_name,
|
|
5241
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5242
|
+
});
|
|
5243
|
+
}
|
|
5244
|
+
}
|
|
5245
|
+
setAutoHighlightIds(new_ids);
|
|
5246
|
+
};
|
|
5247
|
+
perform_auto_highlights();
|
|
5248
|
+
}, [pdf_document, highlight_fields_info, auto_highlight_enabled]);
|
|
5249
|
+
useEffect10(() => {
|
|
5250
|
+
return () => {
|
|
5251
|
+
auto_highlight_ids.forEach((id) => {
|
|
5252
|
+
const annotation = annotations.find((a) => a.id === id);
|
|
5253
|
+
if (annotation) {
|
|
5254
|
+
handle_annotation_delete(id);
|
|
5255
|
+
}
|
|
5256
|
+
});
|
|
5257
|
+
};
|
|
5258
|
+
}, []);
|
|
5102
5259
|
const has_changes_to_save = annotations.length > 0 || page_rotations.size > 0;
|
|
5103
5260
|
const handle_save = async () => {
|
|
5104
5261
|
if (!has_changes_to_save) {
|
|
@@ -5115,7 +5272,7 @@ ${suffix_line}`;
|
|
|
5115
5272
|
const original_filename = is_multi_file_mode && current_file ? current_file.name : effective_url.split("/").pop() || "document.pdf";
|
|
5116
5273
|
const filename_without_ext = original_filename.replace(/\.pdf$/i, "");
|
|
5117
5274
|
const output_filename = `${filename_without_ext}_annotated.pdf`;
|
|
5118
|
-
const { save_annotations_to_pdf, download_pdf } = await import("./pdf_saver-
|
|
5275
|
+
const { save_annotations_to_pdf, download_pdf } = await import("./pdf_saver-JXAIRQVL.js");
|
|
5119
5276
|
const pdf_source = cached_pdf_data || effective_url;
|
|
5120
5277
|
logger2.debug("[PdfViewer] Saving PDF", { source_type: cached_pdf_data ? "cached ArrayBuffer" : "URL" });
|
|
5121
5278
|
const pdf_bytes = await save_annotations_to_pdf(pdf_source, annotations, output_filename, config_ref.current, page_rotations);
|
|
@@ -5995,4 +6152,4 @@ export {
|
|
|
5995
6152
|
PdfViewer,
|
|
5996
6153
|
pdf_viewer_default
|
|
5997
6154
|
};
|
|
5998
|
-
//# sourceMappingURL=chunk-
|
|
6155
|
+
//# sourceMappingURL=chunk-ROKBDX4O.js.map
|