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 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 allows external code to control highlights without user interaction.
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-GFRC6LEG.js.map
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":"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","names":[]}
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-GFRC6LEG.js";
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-IOWST6TO.js");
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-HT7OPS2V.js.map
6155
+ //# sourceMappingURL=chunk-ROKBDX4O.js.map