hazo_pdf 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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
+ - 🔍 **Programmatic Highlights** - Ref-based API for creating and managing highlights programmatically
9
10
  - 🎨 **Customizable Styling** - Extensive configuration options via INI file
10
11
  - ⏰ **Timestamp Support** - Automatic timestamp appending to annotations
11
12
  - 🏷️ **Custom Stamps** - Add quick-insert stamps via right-click menu
@@ -19,6 +20,47 @@ A React component library for viewing and annotating PDF documents with support
19
20
  npm install hazo_pdf
20
21
  ```
21
22
 
23
+ ## CSS Import Options
24
+
25
+ The library provides two CSS files to choose from:
26
+
27
+ ### For apps with existing styles (Recommended)
28
+
29
+ Use `styles.css` - this does NOT include Tailwind preflight/base resets, so it won't interfere with your existing styles:
30
+
31
+ ```tsx
32
+ import 'hazo_pdf/styles.css';
33
+ ```
34
+
35
+ ### For standalone apps
36
+
37
+ Use `styles-full.css` - includes Tailwind preflight/base styles for apps without existing CSS resets:
38
+
39
+ ```tsx
40
+ import 'hazo_pdf/styles-full.css';
41
+ ```
42
+
43
+ ## Container Requirements
44
+
45
+ The PDF viewer requires its parent container to have explicit dimensions:
46
+
47
+ ```tsx
48
+ // Good - explicit dimensions
49
+ <div style={{ width: '100%', height: '600px' }}>
50
+ <PdfViewer url="/document.pdf" />
51
+ </div>
52
+
53
+ // Bad - no explicit height
54
+ <div>
55
+ <PdfViewer url="/document.pdf" />
56
+ </div>
57
+ ```
58
+
59
+ **Requirements:**
60
+ - Parent must have explicit `width` and `height` (CSS or inline style)
61
+ - Recommended minimum size: 400x400px
62
+ - Parent should NOT have `overflow: hidden` (the viewer handles its own scrolling)
63
+
22
64
  ## Quick Start
23
65
 
24
66
  ```tsx
@@ -307,7 +349,7 @@ function ProductionViewer() {
307
349
  <div style={{ flex: 1, overflow: 'hidden' }}>
308
350
  <PdfViewer
309
351
  url="/api/documents/contract.pdf"
310
- config_file="hazo_pdf_config.ini"
352
+ config_file="config/hazo_pdf_config.ini"
311
353
  className="h-full w-full"
312
354
  scale={initialScale}
313
355
  annotations={annotations}
@@ -327,7 +369,7 @@ function ProductionViewer() {
327
369
  ```
328
370
 
329
371
  **Features demonstrated:**
330
- - Configuration file integration (`hazo_pdf_config.ini`)
372
+ - Configuration file integration (`config/hazo_pdf_config.ini`)
331
373
  - Custom stamps with styling
332
374
  - Timestamp and fixed text suffixes
333
375
  - Responsive scaling based on screen size
@@ -340,7 +382,7 @@ function ProductionViewer() {
340
382
 
341
383
  ## Configuration File
342
384
 
343
- The PDF viewer can be configured via an INI file (default: `hazo_pdf_config.ini`). This allows you to customize styling, colors, fonts, and behavior without modifying code.
385
+ The PDF viewer can be configured via an INI file (default: `config/hazo_pdf_config.ini`). This allows you to customize styling, colors, fonts, and behavior without modifying code.
344
386
 
345
387
  **Basic setup:**
346
388
 
@@ -368,7 +410,205 @@ toolbar_button_save_background_color = rgb(34, 197, 94)
368
410
  right_click_custom_stamps = [{"name":"Verified","text":"✅","order":1,"time_stamp_suffix_enabled":true,"fixed_text_suffix_enabled":true}]
369
411
  ```
370
412
 
371
- See `hazo_pdf_config.ini` in the project root for all available configuration options.
413
+ See `config/hazo_pdf_config.ini` in the project root for all available configuration options.
414
+
415
+ ---
416
+
417
+ ## Programmatic Highlight API
418
+
419
+ 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.
420
+
421
+ ### Basic Usage
422
+
423
+ ```tsx
424
+ import { PdfViewer, PdfViewerRef } from 'hazo_pdf';
425
+ import { useRef } from 'react';
426
+ import 'hazo_pdf/styles.css';
427
+
428
+ function HighlightExample() {
429
+ const viewer_ref = useRef<PdfViewerRef>(null);
430
+
431
+ const add_highlight = () => {
432
+ // Highlight region on page 0 at PDF coordinates [100, 500, 300, 550]
433
+ const id = viewer_ref.current?.highlight_region(0, [100, 500, 300, 550], {
434
+ border_color: '#FF0000',
435
+ background_color: '#FFFF00',
436
+ background_opacity: 0.4
437
+ });
438
+ console.log('Created highlight:', id);
439
+ };
440
+
441
+ const remove_specific_highlight = (id: string) => {
442
+ const removed = viewer_ref.current?.remove_highlight(id);
443
+ console.log('Highlight removed:', removed);
444
+ };
445
+
446
+ const clear_all = () => {
447
+ viewer_ref.current?.clear_all_highlights();
448
+ };
449
+
450
+ return (
451
+ <div style={{ width: '100%', height: '800px' }}>
452
+ <button onClick={add_highlight}>Add Highlight</button>
453
+ <button onClick={clear_all}>Clear All Highlights</button>
454
+
455
+ <PdfViewer
456
+ ref={viewer_ref}
457
+ url="/document.pdf"
458
+ />
459
+ </div>
460
+ );
461
+ }
462
+ ```
463
+
464
+ ### PdfViewerRef Interface
465
+
466
+ The ref exposes three methods for highlight management:
467
+
468
+ #### `highlight_region(page_index, rect, options?)`
469
+
470
+ Creates a new highlight annotation on the specified page.
471
+
472
+ **Parameters:**
473
+ - `page_index` (number): Zero-based page index where the highlight should appear
474
+ - `rect` ([number, number, number, number]): Rectangle coordinates in PDF space [x1, y1, x2, y2]
475
+ - `options` (HighlightOptions, optional): Styling options
476
+
477
+ **Returns:** `string` - The unique ID of the created highlight annotation
478
+
479
+ **HighlightOptions:**
480
+ ```typescript
481
+ {
482
+ border_color?: string; // Hex color (e.g., "#FF0000")
483
+ background_color?: string; // Hex color (e.g., "#FFFF00")
484
+ background_opacity?: number; // 0-1 (e.g., 0.4)
485
+ }
486
+ ```
487
+
488
+ **Example:**
489
+ ```tsx
490
+ const id = viewer_ref.current?.highlight_region(
491
+ 0, // Page 0
492
+ [100, 500, 300, 550], // PDF coordinates
493
+ {
494
+ border_color: '#FF0000',
495
+ background_color: '#FFFF00',
496
+ background_opacity: 0.4
497
+ }
498
+ );
499
+ ```
500
+
501
+ #### `remove_highlight(id)`
502
+
503
+ Removes a specific highlight by its ID.
504
+
505
+ **Parameters:**
506
+ - `id` (string): The highlight ID returned from `highlight_region()`
507
+
508
+ **Returns:** `boolean` - `true` if the highlight was found and removed, `false` otherwise
509
+
510
+ **Example:**
511
+ ```tsx
512
+ const removed = viewer_ref.current?.remove_highlight('highlight-123');
513
+ if (removed) {
514
+ console.log('Highlight removed successfully');
515
+ }
516
+ ```
517
+
518
+ #### `clear_all_highlights()`
519
+
520
+ Removes all highlights created via the `highlight_region()` API. Does not affect user-created annotations.
521
+
522
+ **Example:**
523
+ ```tsx
524
+ viewer_ref.current?.clear_all_highlights();
525
+ ```
526
+
527
+ ### Advanced Example: Search Results Highlighting
528
+
529
+ A common use case is highlighting search results in a PDF:
530
+
531
+ ```tsx
532
+ import { useState, useRef } from 'react';
533
+ import { PdfViewer, PdfViewerRef } from 'hazo_pdf';
534
+ import 'hazo_pdf/styles.css';
535
+
536
+ interface SearchResult {
537
+ page: number;
538
+ rect: [number, number, number, number];
539
+ text: string;
540
+ }
541
+
542
+ function SearchableViewer() {
543
+ const viewer_ref = useRef<PdfViewerRef>(null);
544
+ const [highlight_ids, set_highlight_ids] = useState<string[]>([]);
545
+
546
+ // Simulated search results
547
+ const search_results: SearchResult[] = [
548
+ { page: 0, rect: [100, 500, 300, 550], text: 'Result 1' },
549
+ { page: 0, rect: [100, 400, 300, 450], text: 'Result 2' },
550
+ { page: 1, rect: [150, 600, 350, 650], text: 'Result 3' }
551
+ ];
552
+
553
+ const highlight_search_results = () => {
554
+ // Clear previous highlights
555
+ viewer_ref.current?.clear_all_highlights();
556
+
557
+ // Add new highlights
558
+ const ids = search_results.map(result =>
559
+ viewer_ref.current?.highlight_region(result.page, result.rect, {
560
+ border_color: '#3B82F6',
561
+ background_color: '#DBEAFE',
562
+ background_opacity: 0.3
563
+ })
564
+ ).filter(Boolean) as string[];
565
+
566
+ set_highlight_ids(ids);
567
+ };
568
+
569
+ const clear_search = () => {
570
+ viewer_ref.current?.clear_all_highlights();
571
+ set_highlight_ids([]);
572
+ };
573
+
574
+ return (
575
+ <div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
576
+ <div style={{ padding: '1rem', background: '#f0f0f0' }}>
577
+ <button onClick={highlight_search_results}>
578
+ Highlight Search Results ({search_results.length})
579
+ </button>
580
+ <button onClick={clear_search}>Clear Highlights</button>
581
+ <span>Highlighted: {highlight_ids.length} results</span>
582
+ </div>
583
+
584
+ <div style={{ flex: 1 }}>
585
+ <PdfViewer
586
+ ref={viewer_ref}
587
+ url="/document.pdf"
588
+ />
589
+ </div>
590
+ </div>
591
+ );
592
+ }
593
+ ```
594
+
595
+ ### Coordinate System Notes
596
+
597
+ - Highlights use PDF coordinate space (points), not screen pixels
598
+ - PDF coordinates start at the bottom-left corner (Y increases upward)
599
+ - The `rect` parameter format is `[x1, y1, x2, y2]` where:
600
+ - `x1, y1`: Bottom-left corner
601
+ - `x2, y2`: Top-right corner
602
+ - If you need to convert screen coordinates to PDF coordinates, you'll need to use the PDF page viewport (see the test app for examples)
603
+
604
+ ### Styling Defaults
605
+
606
+ If no `options` are provided to `highlight_region()`, the highlight uses default colors from the configuration file:
607
+ - `border_color`: From `highlight_border_color` config setting
608
+ - `background_color`: From `highlight_fill_color` config setting
609
+ - `background_opacity`: From `highlight_fill_opacity` config setting
610
+
611
+ You can override any or all of these on a per-highlight basis.
372
612
 
373
613
  ---
374
614
 
@@ -391,7 +631,7 @@ See `hazo_pdf_config.ini` in the project root for all available configuration op
391
631
  | `className` | `string` | `""` | Additional CSS classes to apply to the viewer container. |
392
632
  | `scale` | `number` | `1.0` | Initial zoom level. Values > 1.0 zoom in, < 1.0 zoom out. |
393
633
  | `background_color` | `string` | `"#2d2d2d"` | Background color for areas outside PDF pages (hex format: `#RRGGBB`). Overrides config file value. |
394
- | `config_file` | `string` | `undefined` | Path to configuration INI file (e.g., `"hazo_pdf_config.ini"`). If not provided, uses default configuration. |
634
+ | `config_file` | `string` | `undefined` | Path to configuration INI file (e.g., `"config/hazo_pdf_config.ini"`). If not provided, uses default configuration. |
395
635
 
396
636
  ##### Event Callbacks
397
637
 
@@ -469,12 +709,50 @@ const stamps = JSON.stringify([
469
709
  }
470
710
  ]);
471
711
 
472
- <PdfViewer
712
+ <PdfViewer
473
713
  url="/document.pdf"
474
714
  right_click_custom_stamps={stamps}
475
715
  />
476
716
  ```
477
717
 
718
+ ##### Toolbar Visibility
719
+
720
+ Props to control toolbar button visibility. These override config file values.
721
+
722
+ | Prop | Type | Default | Description |
723
+ |------|------|---------|-------------|
724
+ | `toolbar_enabled` | `boolean` | `true` | Master toggle to show/hide the entire toolbar. |
725
+ | `show_zoom_controls` | `boolean` | `true` | Show zoom in/out/reset buttons. |
726
+ | `show_square_button` | `boolean` | `true` | Show square annotation button. |
727
+ | `show_undo_button` | `boolean` | `true` | Show undo button. |
728
+ | `show_redo_button` | `boolean` | `true` | Show redo button. |
729
+ | `show_save_button` | `boolean` | `true` | Show save button. |
730
+ | `show_metadata_button` | `boolean` | `true` | Show metadata panel button (only visible when `sidepanel_metadata_enabled` is true). |
731
+ | `on_close` | `() => void` | `undefined` | Callback when close button is clicked. When provided, shows a close button (X) in the toolbar. Useful for modal/dialog usage. |
732
+
733
+ **Example - Minimal toolbar:**
734
+
735
+ ```tsx
736
+ <PdfViewer
737
+ url="/document.pdf"
738
+ toolbar_enabled={true}
739
+ show_zoom_controls={true}
740
+ show_square_button={false}
741
+ show_undo_button={false}
742
+ show_redo_button={false}
743
+ show_save_button={false}
744
+ />
745
+ ```
746
+
747
+ **Example - Dialog with close button:**
748
+
749
+ ```tsx
750
+ <PdfViewer
751
+ url="/document.pdf"
752
+ on_close={() => setDialogOpen(false)}
753
+ />
754
+ ```
755
+
478
756
  ### PdfAnnotation Interface
479
757
 
480
758
  Represents a PDF annotation in the standard PDF coordinate space.
@@ -494,6 +772,40 @@ interface PdfAnnotation {
494
772
  }
495
773
  ```
496
774
 
775
+ **Note:** Highlights created via the programmatic API (`PdfViewerRef.highlight_region()`) will have `type: 'Highlight'` and `flags: 'api_highlight'` to distinguish them from user-created annotations.
776
+
777
+ ### PdfViewerRef Interface
778
+
779
+ Interface for the ref exposed by `PdfViewer`. Use with `useRef<PdfViewerRef>()` to access programmatic highlight methods.
780
+
781
+ ```typescript
782
+ interface PdfViewerRef {
783
+ highlight_region: (
784
+ page_index: number,
785
+ rect: [number, number, number, number],
786
+ options?: HighlightOptions
787
+ ) => string;
788
+
789
+ remove_highlight: (id: string) => boolean;
790
+
791
+ clear_all_highlights: () => void;
792
+ }
793
+ ```
794
+
795
+ See [Programmatic Highlight API](#programmatic-highlight-api) for detailed usage.
796
+
797
+ ### HighlightOptions Interface
798
+
799
+ Options for customizing highlights created via the API.
800
+
801
+ ```typescript
802
+ interface HighlightOptions {
803
+ border_color?: string; // Hex format (e.g., "#FF0000")
804
+ background_color?: string; // Hex format (e.g., "#FFFF00")
805
+ background_opacity?: number; // 0-1 (e.g., 0.4)
806
+ }
807
+ ```
808
+
497
809
  ### PDFDocumentProxy
498
810
 
499
811
  Type from `pdfjs-dist`. Contains PDF metadata and page proxies.
@@ -95,6 +95,7 @@ var default_config = {
95
95
  toolbar_show_redo_button: true,
96
96
  toolbar_show_save_button: true,
97
97
  toolbar_show_metadata_button: true,
98
+ toolbar_show_annotate_button: true,
98
99
  toolbar_zoom_out_label: "\u2212",
99
100
  toolbar_zoom_in_label: "+",
100
101
  toolbar_zoom_reset_label: "Reset",
@@ -311,4 +312,4 @@ export {
311
312
  download_pdf,
312
313
  save_and_download_pdf
313
314
  };
314
- //# sourceMappingURL=chunk-ZQMZT7HV.js.map
315
+ //# sourceMappingURL=chunk-S3AJUZ7D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/default_config.ts","../src/utils/pdf_saver.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_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_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","/**\n * PDF Saver Utility\n * Saves annotations directly into PDF documents using pdf-lib\n * This allows annotations to be embedded in the PDF file itself\n */\n\nimport type { PdfAnnotation, PdfViewerConfig } from '../types';\nimport { default_config } from '../config/default_config';\n\n/**\n * Save annotations into a PDF document\n * This function fetches the PDF, adds annotations, and downloads the modified PDF\n * @param pdf_url - URL of the PDF file\n * @param annotations - Array of annotations to save\n * @param output_filename - Name for the saved PDF file (default: original filename with '_annotated' suffix)\n * @param config - Optional configuration for styling values\n * @returns Promise that resolves when the PDF is saved\n */\nexport async function save_annotations_to_pdf(\n pdf_url: string,\n annotations: PdfAnnotation[],\n _output_filename?: string, // Currently unused, kept for API compatibility\n config?: PdfViewerConfig | null\n): Promise<Uint8Array> {\n try {\n // Dynamically import pdf-lib\n const { PDFDocument, rgb } = await import('pdf-lib');\n \n // Fetch the original PDF\n console.log('[PDF Saver] Fetching PDF from:', pdf_url);\n const pdf_response = await fetch(pdf_url);\n if (!pdf_response.ok) {\n throw new Error(`Failed to fetch PDF: ${pdf_response.status} ${pdf_response.statusText}`);\n }\n \n const pdf_bytes = await pdf_response.arrayBuffer();\n console.log('[PDF Saver] PDF fetched, size:', pdf_bytes.byteLength, 'bytes');\n \n // Load the PDF document\n const pdf_doc = await PDFDocument.load(pdf_bytes);\n console.log('[PDF Saver] PDF loaded, pages:', pdf_doc.getPageCount());\n \n // Get config values or use defaults\n const fonts_config = config?.fonts || default_config.fonts;\n const highlight_config = config?.highlight_annotation || default_config.highlight_annotation;\n const square_config = config?.square_annotation || default_config.square_annotation;\n const freetext_config = config?.freetext_annotation || default_config.freetext_annotation;\n \n // Add annotations to each page\n for (const annotation of annotations) {\n if (annotation.page_index >= pdf_doc.getPageCount()) {\n console.warn(`[PDF Saver] Annotation ${annotation.id} references page ${annotation.page_index}, but PDF only has ${pdf_doc.getPageCount()} pages. Skipping.`);\n continue;\n }\n \n const page = pdf_doc.getPage(annotation.page_index);\n const [x1, y1, x2, y2] = annotation.rect;\n \n // PDF coordinate system: bottom-left is origin (pdfjs-dist)\n // pdf-lib uses bottom-left as origin too, so coordinates should be fine\n // But we need to ensure rect is normalized\n const rect_x = Math.min(x1, x2);\n const rect_y = Math.min(y1, y2);\n const rect_width = Math.abs(x2 - x1);\n const rect_height = Math.abs(y2 - y1);\n \n // Parse color (hex or rgb to RGB for pdf-lib)\n // Supports both hex (#RRGGBB) and rgb(r, g, b) formats\n let color = rgb(0, 0, 0); // Default black\n if (annotation.color) {\n const color_str = annotation.color.trim();\n \n // Handle rgb(r, g, b) format\n const rgb_pattern = /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i;\n const rgb_match = color_str.match(rgb_pattern);\n if (rgb_match) {\n const r = parseInt(rgb_match[1], 10);\n const g = parseInt(rgb_match[2], 10);\n const b = parseInt(rgb_match[3], 10);\n // Validate RGB values (0-255) and convert to 0-1 range for pdf-lib\n if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {\n color = rgb(r / 255, g / 255, b / 255);\n } else {\n console.warn(`[PDF Saver] Invalid RGB values for annotation ${annotation.id}: r=${r}, g=${g}, b=${b}`);\n }\n } else if (color_str.startsWith('#')) {\n // Handle hex format (#RRGGBB)\n const hex = color_str.slice(1).trim();\n if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {\n const r = parseInt(hex.substring(0, 2), 16) / 255;\n const g = parseInt(hex.substring(2, 4), 16) / 255;\n const b = parseInt(hex.substring(4, 6), 16) / 255;\n color = rgb(r, g, b);\n } else {\n console.warn(`[PDF Saver] Invalid hex color format for annotation ${annotation.id}: ${color_str}`);\n }\n } else {\n console.warn(`[PDF Saver] Unrecognized color format for annotation ${annotation.id}: ${color_str}`);\n }\n }\n \n // Create annotation based on type using pdf-lib's annotation methods\n try {\n switch (annotation.type) {\n case 'Square': {\n // Create a rectangle/square annotation\n page.drawRectangle({\n x: rect_x,\n y: rect_y,\n width: rect_width,\n height: rect_height,\n borderColor: color,\n borderWidth: 2,\n borderOpacity: 0.8,\n });\n \n // Add text comment if present\n if (annotation.contents) {\n const comment_color_str = (annotation.color || square_config.square_border_color).trim();\n let comment_r = 0, comment_g = 0, comment_b = 0;\n \n // Parse color (hex or rgb format)\n const rgb_pattern = /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i;\n const rgb_match = comment_color_str.match(rgb_pattern);\n if (rgb_match) {\n comment_r = parseInt(rgb_match[1], 10) / 255;\n comment_g = parseInt(rgb_match[2], 10) / 255;\n comment_b = parseInt(rgb_match[3], 10) / 255;\n } else if (comment_color_str.startsWith('#')) {\n const hex = comment_color_str.slice(1).trim();\n if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {\n comment_r = parseInt(hex.substring(0, 2), 16) / 255;\n comment_g = parseInt(hex.substring(2, 4), 16) / 255;\n comment_b = parseInt(hex.substring(4, 6), 16) / 255;\n }\n }\n \n page.drawText(annotation.contents, {\n x: rect_x,\n y: rect_y + rect_height + 5,\n size: fonts_config.freetext_font_size_default,\n color: rgb(comment_r, comment_g, comment_b),\n });\n }\n break;\n }\n \n case 'Highlight': {\n // Create a highlight annotation (semi-transparent rectangle)\n const highlight_color_str = (annotation.color || highlight_config.highlight_fill_color).trim();\n let highlight_r = 255 / 255, highlight_g = 255 / 255, highlight_b = 0 / 255; // Default yellow\n \n // Parse color (hex or rgb format)\n const rgb_pattern = /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i;\n const rgb_match = highlight_color_str.match(rgb_pattern);\n if (rgb_match) {\n highlight_r = parseInt(rgb_match[1], 10) / 255;\n highlight_g = parseInt(rgb_match[2], 10) / 255;\n highlight_b = parseInt(rgb_match[3], 10) / 255;\n } else if (highlight_color_str.startsWith('#')) {\n const hex = highlight_color_str.slice(1).trim();\n if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {\n highlight_r = parseInt(hex.substring(0, 2), 16) / 255;\n highlight_g = parseInt(hex.substring(2, 4), 16) / 255;\n highlight_b = parseInt(hex.substring(4, 6), 16) / 255;\n }\n }\n const highlight_color = rgb(highlight_r, highlight_g, highlight_b);\n \n page.drawRectangle({\n x: rect_x,\n y: rect_y,\n width: rect_width,\n height: rect_height,\n color: highlight_color,\n opacity: highlight_config.highlight_fill_opacity,\n });\n \n // Add text comment if present\n if (annotation.contents) {\n page.drawText(annotation.contents, {\n x: rect_x,\n y: rect_y + rect_height + 5,\n size: fonts_config.freetext_font_size_default,\n color: rgb(0, 0, 0),\n });\n }\n break;\n }\n \n case 'FreeText': {\n // Create a free text annotation\n if (annotation.contents) {\n // Text color priority: annotation.color > freetext_text_color > font_foreground_color > default black\n const text_color_str = (annotation.color || \n freetext_config.freetext_text_color || \n fonts_config.font_foreground_color || \n '#000000').trim();\n let text_r = 0, text_g = 0, text_b = 0;\n \n // Parse color (hex or rgb format)\n const rgb_pattern = /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i;\n const rgb_match = text_color_str.match(rgb_pattern);\n if (rgb_match) {\n text_r = parseInt(rgb_match[1], 10) / 255;\n text_g = parseInt(rgb_match[2], 10) / 255;\n text_b = parseInt(rgb_match[3], 10) / 255;\n } else if (text_color_str.startsWith('#')) {\n const hex = text_color_str.slice(1).trim();\n if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {\n text_r = parseInt(hex.substring(0, 2), 16) / 255;\n text_g = parseInt(hex.substring(2, 4), 16) / 255;\n text_b = parseInt(hex.substring(4, 6), 16) / 255;\n }\n }\n \n page.drawText(annotation.contents, {\n x: rect_x,\n y: rect_y,\n size: fonts_config.freetext_font_size_default,\n color: rgb(text_r, text_g, text_b),\n maxWidth: rect_width,\n });\n }\n break;\n }\n \n default:\n console.warn(`[PDF Saver] Unsupported annotation type: ${annotation.type}`);\n }\n } catch (annotation_error) {\n console.error(`[PDF Saver] Error adding annotation ${annotation.id}:`, annotation_error);\n // Continue with other annotations\n }\n }\n \n // Save the PDF\n const modified_pdf_bytes = await pdf_doc.save();\n console.log('[PDF Saver] PDF modified, size:', modified_pdf_bytes.length, 'bytes');\n \n return modified_pdf_bytes;\n } catch (error) {\n if (error && typeof error === 'object' && 'message' in error && \n typeof error.message === 'string' && error.message.includes('pdf-lib')) {\n throw new Error('pdf-lib is required to save annotations to PDF. Please install it: npm install pdf-lib');\n }\n console.error('[PDF Saver] Error saving PDF:', error);\n throw error;\n }\n}\n\n/**\n * Download PDF bytes as a file\n * @param pdf_bytes - PDF file bytes\n * @param filename - Name for the downloaded file\n */\nexport function download_pdf(pdf_bytes: Uint8Array, filename: string): void {\n // Create a new ArrayBuffer view to ensure compatibility with Blob constructor\n // Uint8Array.buffer can be SharedArrayBuffer, which Blob doesn't accept directly\n const buffer = new Uint8Array(pdf_bytes).buffer;\n const blob = new Blob([buffer], { type: 'application/pdf' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n}\n\n/**\n * Save annotations to PDF and download\n * @param pdf_url - URL of the PDF file\n * @param annotations - Array of annotations to save\n * @param output_filename - Name for the saved PDF file (default: original filename with '_annotated' suffix)\n * @param config - Optional configuration for styling values\n */\nexport async function save_and_download_pdf(\n pdf_url: string,\n annotations: PdfAnnotation[],\n output_filename?: string,\n config?: PdfViewerConfig | null\n): Promise<void> {\n const pdf_bytes = await save_annotations_to_pdf(pdf_url, annotations, output_filename, config);\n \n // Generate output filename\n const original_filename = pdf_url.split('/').pop() || 'document.pdf';\n const filename_without_ext = original_filename.replace(/\\.pdf$/i, '');\n const final_filename = output_filename || `${filename_without_ext}_annotated.pdf`;\n \n // Download the modified PDF\n download_pdf(pdf_bytes, final_filename);\n console.log('[PDF Saver] PDF saved as:', final_filename);\n}\n\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import React from 'react';
1
+ import * as React from 'react';
2
+ import React__default from 'react';
2
3
  import { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';
3
4
 
4
5
  /**
@@ -168,6 +169,8 @@ interface PdfViewerConfig {
168
169
  toolbar_show_save_button: boolean;
169
170
  /** Whether to show metadata panel button (true/false) */
170
171
  toolbar_show_metadata_button: boolean;
172
+ /** Whether to show annotate (FreeText) button (true/false) */
173
+ toolbar_show_annotate_button: boolean;
171
174
  /** Label for zoom out button (default: "−") */
172
175
  toolbar_zoom_out_label: string;
173
176
  /** Label for zoom in button (default: "+") */
@@ -220,7 +223,7 @@ interface PdfViewerProps {
220
223
  on_save?: (pdf_bytes: Uint8Array, filename: string) => void;
221
224
  /** Background color for areas outside PDF pages (default: dark grey) */
222
225
  background_color?: string;
223
- /** Optional path to configuration INI file (e.g., "hazo_pdf_config.ini") */
226
+ /** Optional path to configuration INI file (e.g., "config/hazo_pdf_config.ini") */
224
227
  config_file?: string;
225
228
  /** Whether to append timestamp to annotated text edits (default: false) */
226
229
  append_timestamp_to_text_edits?: boolean;
@@ -241,6 +244,24 @@ interface PdfViewerProps {
241
244
  updatedRow: MetadataDataItem;
242
245
  allData: MetadataInput;
243
246
  };
247
+ /** Whether to show the toolbar at all (default: true) */
248
+ toolbar_enabled?: boolean;
249
+ /** Whether to show zoom controls (default: true) */
250
+ show_zoom_controls?: boolean;
251
+ /** Whether to show square annotation button (default: true) */
252
+ show_square_button?: boolean;
253
+ /** Whether to show undo button (default: true) */
254
+ show_undo_button?: boolean;
255
+ /** Whether to show redo button (default: true) */
256
+ show_redo_button?: boolean;
257
+ /** Whether to show save button (default: true) */
258
+ show_save_button?: boolean;
259
+ /** Whether to show metadata panel button (default: true, only visible when sidepanel_metadata_enabled) */
260
+ show_metadata_button?: boolean;
261
+ /** Whether to show annotate (FreeText) button (default: true) */
262
+ show_annotate_button?: boolean;
263
+ /** Callback when close button is clicked (shows close button in toolbar when provided) */
264
+ on_close?: () => void;
244
265
  }
245
266
  /**
246
267
  * PDF Annotation interface matching PDF standard
@@ -383,18 +404,48 @@ interface MetadataInput {
383
404
  /** Array of footer items */
384
405
  footer: MetadataFooterItem[];
385
406
  }
386
-
387
407
  /**
388
- * PDF Viewer Component
389
- * Main component for displaying and interacting with PDF documents
390
- * Integrates PDF rendering, annotation overlay, and layout management
408
+ * Options for programmatic highlight creation via PdfViewerRef
391
409
  */
410
+ interface HighlightOptions {
411
+ /** Border color in hex format (default: from config highlight_border_color) */
412
+ border_color?: string;
413
+ /** Background/fill color in hex format (default: from config highlight_fill_color) */
414
+ background_color?: string;
415
+ /** Background opacity 0-1 (default: from config highlight_fill_opacity) */
416
+ background_opacity?: number;
417
+ }
418
+ /**
419
+ * Ref interface for programmatic PDF viewer control
420
+ * Use with useRef<PdfViewerRef>() to get access to imperative methods
421
+ */
422
+ interface PdfViewerRef {
423
+ /**
424
+ * Create a highlight on a specific page region
425
+ * @param page_index - Zero-based page index
426
+ * @param rect - Rectangle coordinates in PDF space [x1, y1, x2, y2]
427
+ * @param options - Optional styling overrides (border_color, background_color, background_opacity)
428
+ * @returns The highlight annotation ID
429
+ */
430
+ highlight_region: (page_index: number, rect: [number, number, number, number], options?: HighlightOptions) => string;
431
+ /**
432
+ * Remove a specific highlight by ID
433
+ * @param id - The highlight annotation ID returned from highlight_region
434
+ * @returns true if highlight was found and removed, false otherwise
435
+ */
436
+ remove_highlight: (id: string) => boolean;
437
+ /**
438
+ * Remove all highlights created via the highlight_region API
439
+ * Does not affect user-created annotations
440
+ */
441
+ clear_all_highlights: () => void;
442
+ }
392
443
 
393
444
  /**
394
445
  * PDF Viewer Component
395
446
  * Main entry point for PDF viewing and annotation
396
447
  */
397
- declare const PdfViewer: React.FC<PdfViewerProps>;
448
+ declare const PdfViewer: React.ForwardRefExoticComponent<PdfViewerProps & React.RefAttributes<PdfViewerRef>>;
398
449
 
399
450
  /**
400
451
  * PDF Viewer Layout Component
@@ -411,8 +462,9 @@ interface PdfViewerLayoutProps {
411
462
  annotations: PdfAnnotation[];
412
463
  current_tool: 'Square' | 'Highlight' | 'FreeText' | 'CustomBookmark' | null;
413
464
  on_annotation_create: (annotation: PdfAnnotation) => void;
414
- on_context_menu: (e: React.MouseEvent, page_index: number, screen_x: number, screen_y: number, mapper: CoordinateMapper) => void;
465
+ on_context_menu: (e: React__default.MouseEvent, page_index: number, screen_x: number, screen_y: number, mapper: CoordinateMapper) => void;
415
466
  on_annotation_click: (annotation: PdfAnnotation, screen_x: number, screen_y: number, mapper: CoordinateMapper) => void;
467
+ on_freetext_click?: (page_index: number, screen_x: number, screen_y: number, mapper: CoordinateMapper) => void;
416
468
  background_color?: string;
417
469
  config: PdfViewerConfig | null;
418
470
  className?: string;
@@ -421,7 +473,7 @@ interface PdfViewerLayoutProps {
421
473
  * PDF Viewer Layout Component
422
474
  * Manages page rendering and annotation overlay coordination
423
475
  */
424
- declare const PdfViewerLayout: React.FC<PdfViewerLayoutProps>;
476
+ declare const PdfViewerLayout: React__default.FC<PdfViewerLayoutProps>;
425
477
 
426
478
  /**
427
479
  * PDF Page Renderer Component
@@ -450,7 +502,7 @@ interface PdfPageRendererProps {
450
502
  * PDF Page Renderer Component
451
503
  * Handles rendering of a single PDF page to canvas
452
504
  */
453
- declare const PdfPageRenderer: React.FC<PdfPageRendererProps>;
505
+ declare const PdfPageRenderer: React__default.FC<PdfPageRendererProps>;
454
506
 
455
507
  /**
456
508
  * Annotation Overlay Component
@@ -476,9 +528,11 @@ interface AnnotationOverlayProps {
476
528
  /** Callback when annotation is created */
477
529
  on_annotation_create?: (annotation: PdfAnnotation) => void;
478
530
  /** Callback when right-click occurs */
479
- on_context_menu?: (event: React.MouseEvent, screen_x: number, screen_y: number) => void;
531
+ on_context_menu?: (event: React__default.MouseEvent, screen_x: number, screen_y: number) => void;
480
532
  /** Callback when annotation is clicked */
481
533
  on_annotation_click?: (annotation: PdfAnnotation, screen_x: number, screen_y: number) => void;
534
+ /** Callback when FreeText tool is active and user clicks on empty area */
535
+ on_freetext_click?: (screen_x: number, screen_y: number) => void;
482
536
  /** Configuration object for styling */
483
537
  config?: PdfViewerConfig | null;
484
538
  /** Optional class name */
@@ -488,7 +542,7 @@ interface AnnotationOverlayProps {
488
542
  * Annotation Overlay Component
489
543
  * Handles mouse interactions for creating annotations
490
544
  */
491
- declare const AnnotationOverlay: React.FC<AnnotationOverlayProps>;
545
+ declare const AnnotationOverlay: React__default.FC<AnnotationOverlayProps>;
492
546
 
493
547
  /**
494
548
  * PDF Worker Setup
@@ -706,4 +760,4 @@ declare function load_pdf_config(config_file?: string): PdfViewerConfig;
706
760
  */
707
761
  declare const default_config: PdfViewerConfig;
708
762
 
709
- export { AnnotationOverlay, type CoordinateMapper, type CustomStamp, type MetadataDataItem, type MetadataFooterItem, type MetadataFormatType, type MetadataHeaderItem, type MetadataInput, type PageDimensions, type PdfAnnotation, type PdfBookmark, type PdfDocument, type PdfPage, PdfPageRenderer, PdfViewer, type PdfViewerConfig, PdfViewerLayout, type PdfViewerProps, build_config_from_ini, calculate_rectangle_coords, create_coordinate_mapper, default_config, download_pdf, download_xfdf, export_annotations_to_xfdf, generate_xfdf, get_viewport_dimensions, is_rectangle_too_small, load_pdf_config, load_pdf_config_async, load_pdf_document, parse_color, parse_number, parse_opacity, parse_string, pdf_rect_to_rectangle, rectangle_to_pdf_rect, save_and_download_pdf, save_annotations_to_pdf };
763
+ export { AnnotationOverlay, type CoordinateMapper, type CustomStamp, type HighlightOptions, type MetadataDataItem, type MetadataFooterItem, type MetadataFormatType, type MetadataHeaderItem, type MetadataInput, type PageDimensions, type PdfAnnotation, type PdfBookmark, type PdfDocument, type PdfPage, PdfPageRenderer, PdfViewer, type PdfViewerConfig, PdfViewerLayout, type PdfViewerProps, type PdfViewerRef, build_config_from_ini, calculate_rectangle_coords, create_coordinate_mapper, default_config, download_pdf, download_xfdf, export_annotations_to_xfdf, generate_xfdf, get_viewport_dimensions, is_rectangle_too_small, load_pdf_config, load_pdf_config_async, load_pdf_document, parse_color, parse_number, parse_opacity, parse_string, pdf_rect_to_rectangle, rectangle_to_pdf_rect, save_and_download_pdf, save_annotations_to_pdf };