hazo_pdf 1.0.1 → 1.2.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
@@ -357,6 +357,13 @@ freetext_background_opacity = 0.1
357
357
  freetext_border_color = #003366
358
358
  freetext_border_width = 1
359
359
 
360
+ [toolbar]
361
+ toolbar_background_color = rgb(240, 248, 255)
362
+ toolbar_font_color = rgb(30, 58, 138)
363
+ toolbar_button_background_color = rgb(219, 234, 254)
364
+ toolbar_button_text_color = rgb(30, 64, 175)
365
+ toolbar_button_save_background_color = rgb(34, 197, 94)
366
+
360
367
  [context_menu]
361
368
  right_click_custom_stamps = [{"name":"Verified","text":"✅","order":1,"time_stamp_suffix_enabled":true,"fixed_text_suffix_enabled":true}]
362
369
  ```
@@ -410,6 +417,14 @@ See `hazo_pdf_config.ini` in the project root for all available configuration op
410
417
  | `append_timestamp_to_text_edits` | `boolean` | `false` | If `true`, automatically appends a timestamp to all FreeText annotations when created or edited. Format: `[YYYY-MM-DD h:mmam/pm]`. Overrides config file value. |
411
418
  | `annotation_text_suffix_fixed_text` | `string` | `""` | Fixed text string to append before the timestamp (if timestamps are enabled). This text will be enclosed in brackets. Overrides config file value. |
412
419
 
420
+ ##### Sidepanel Metadata
421
+
422
+ | Prop | Type | Default | Description |
423
+ |------|------|---------|-------------|
424
+ | `sidepanel_metadata_enabled` | `boolean` | `false` | If `true`, enables the metadata sidepanel on the right side of the viewer. The panel can be toggled from the toolbar or right edge button. |
425
+ | `metadata_input` | `MetadataInput` | `undefined` | Metadata structure with header, data (accordions), and footer sections. Each section supports different format types (h1-h5, body) and editable fields. See [Sidepanel Metadata](#sidepanel-metadata) section for details. |
426
+ | `on_metadata_change` | `(updatedRow: MetadataDataItem, allData: MetadataInput) => { updatedRow: MetadataDataItem; allData: MetadataInput }` | `undefined` | Callback when a metadata field is edited. Receives the updated row and complete metadata structure. Must return both parameters. Use this to persist metadata changes to your backend. |
427
+
413
428
  ##### Custom Stamps
414
429
 
415
430
  | Prop | Type | Default | Description |
@@ -489,6 +504,344 @@ Type from `pdfjs-dist`. Contains PDF metadata and page proxies.
489
504
 
490
505
  ---
491
506
 
507
+ ## Toolbar Configuration
508
+
509
+ The toolbar can be fully customized via the `[toolbar]` section in the configuration file. You can configure:
510
+
511
+ - **Colors**: Background, border, font, and button colors (background, hover, active, save)
512
+ - **Fonts**: Font family and size for toolbar text
513
+ - **Visibility**: Show/hide individual button groups (zoom, square, undo, redo, save)
514
+ - **Labels**: Customize all button labels and text
515
+
516
+ **All toolbar settings with defaults:**
517
+
518
+ | Setting | Type | Default | Description |
519
+ |---------|------|---------|-------------|
520
+ | `toolbar_background_color` | Color | `#f9fafb` | Toolbar background color |
521
+ | `toolbar_border_color` | Color | `#e5e7eb` | Toolbar border color |
522
+ | `toolbar_font_family` | String | `system-ui, -apple-system, sans-serif` | Font family for toolbar |
523
+ | `toolbar_font_size` | Number | `14` | Font size in pixels |
524
+ | `toolbar_font_color` | Color | `#111827` | Text color for toolbar |
525
+ | `toolbar_button_background_color` | Color | `#ffffff` | Regular button background |
526
+ | `toolbar_button_background_color_hover` | Color | `#f3f4f6` | Button hover background |
527
+ | `toolbar_button_text_color` | Color | `#374151` | Button text color |
528
+ | `toolbar_button_active_background_color` | Color | `#3b82f6` | Active button background |
529
+ | `toolbar_button_active_text_color` | Color | `#ffffff` | Active button text color |
530
+ | `toolbar_button_save_background_color` | Color | `#10b981` | Save button background |
531
+ | `toolbar_button_save_background_color_hover` | Color | `#059669` | Save button hover background |
532
+ | `toolbar_button_save_text_color` | Color | `#ffffff` | Save button text color |
533
+ | `toolbar_button_disabled_opacity` | Number | `0.5` | Opacity for disabled buttons (0.0-1.0) |
534
+ | `toolbar_show_zoom_controls` | Boolean | `true` | Show zoom controls |
535
+ | `toolbar_show_square_button` | Boolean | `true` | Show square annotation button |
536
+ | `toolbar_show_undo_button` | Boolean | `true` | Show undo button |
537
+ | `toolbar_show_redo_button` | Boolean | `true` | Show redo button |
538
+ | `toolbar_show_save_button` | Boolean | `true` | Show save button |
539
+ | `toolbar_show_metadata_button` | Boolean | `true` | Show metadata panel button (only shown when sidepanel is enabled) |
540
+ | `toolbar_zoom_out_label` | String | `"−"` | Zoom out button label |
541
+ | `toolbar_zoom_in_label` | String | `"+"` | Zoom in button label |
542
+ | `toolbar_zoom_reset_label` | String | `"Reset"` | Reset zoom button label |
543
+ | `toolbar_square_label` | String | `"Square"` | Square button label |
544
+ | `toolbar_undo_label` | String | `"Undo"` | Undo button label |
545
+ | `toolbar_redo_label` | String | `"Redo"` | Redo button label |
546
+ | `toolbar_save_label` | String | `"Save"` | Save button label |
547
+ | `toolbar_saving_label` | String | `"Saving..."` | Saving button label |
548
+ | `toolbar_metadata_label` | String | `"Metadata"` | Metadata panel button label |
549
+
550
+ **Example configuration:**
551
+
552
+ ```ini
553
+ [toolbar]
554
+ # Customize colors
555
+ toolbar_background_color = rgb(240, 248, 255)
556
+ toolbar_border_color = rgb(59, 130, 246)
557
+ toolbar_font_color = rgb(30, 58, 138)
558
+ toolbar_button_background_color = rgb(219, 234, 254)
559
+ toolbar_button_text_color = rgb(30, 64, 175)
560
+ toolbar_button_active_background_color = rgb(37, 99, 235)
561
+ toolbar_button_save_background_color = rgb(34, 197, 94)
562
+
563
+ # Hide some controls
564
+ toolbar_show_redo_button = false
565
+ toolbar_show_square_button = false
566
+ toolbar_show_metadata_button = false
567
+
568
+ # Customize labels
569
+ toolbar_save_label = Save PDF
570
+ toolbar_undo_label = ← Undo
571
+ ```
572
+
573
+ ## Toolbar Controls
574
+
575
+ The PDF viewer includes a toolbar at the top with the following controls:
576
+
577
+ ### Zoom Controls
578
+
579
+ - **Zoom In (+ button)**: Increases zoom level by 0.25x increments (max 3.0x)
580
+ - **Zoom Out (- button)**: Decreases zoom level by 0.25x increments (min 0.5x)
581
+ - **Reset Zoom button**: Resets zoom to 1.0x (100%)
582
+ - **Zoom level display**: Shows current zoom percentage (e.g., "125%")
583
+
584
+ **Usage:**
585
+ - Click the `+` or `-` buttons to adjust zoom
586
+ - Click "Reset" to return to default zoom
587
+ - Zoom affects the PDF page size but maintains aspect ratio
588
+
589
+ ### Annotation Tools
590
+
591
+ - **Square button**: Activates square/rectangle annotation tool
592
+ - Click and drag on the PDF to create a rectangle
593
+ - Right-click the rectangle to add a comment
594
+ - Button highlights when active
595
+
596
+ ### History Controls
597
+
598
+ - **Undo button**: Reverses the last annotation action
599
+ - Keyboard shortcut: `Ctrl+Z` (Windows/Linux) or `Cmd+Z` (Mac)
600
+ - Disabled when there are no actions to undo
601
+ - Shows history icon
602
+
603
+ - **Redo button**: Reapplies the last undone action
604
+ - Keyboard shortcut: `Ctrl+Y` (Windows/Linux) or `Cmd+Y` (Mac)
605
+ - Disabled when there are no actions to redo
606
+ - Shows redo icon
607
+
608
+ ### Save Control
609
+
610
+ - **Save button**: Saves all annotations directly into the PDF file
611
+ - Disabled when there are no annotations
612
+ - Shows save icon and "Saving..." text during save operation
613
+ - Triggers `on_save` callback with PDF bytes and filename
614
+
615
+ ### Metadata Panel Toggle
616
+
617
+ - **Metadata button**: Toggles the metadata sidepanel (only visible when `sidepanel_metadata_enabled={true}`)
618
+ - Shows panel icon
619
+ - Button highlights when panel is open
620
+ - Opens/closes the right-side metadata panel
621
+
622
+ ---
623
+
624
+ ## Sidepanel Metadata
625
+
626
+ The PDF viewer can display a retractable sidepanel on the right side showing JSON metadata with header, data (accordions), and footer sections.
627
+
628
+ ### Enabling the Sidepanel
629
+
630
+ To enable the metadata sidepanel, set `sidepanel_metadata_enabled={true}` and provide `metadata_input`:
631
+
632
+ ```tsx
633
+ import { PdfViewer } from 'hazo_pdf';
634
+ import type { MetadataInput } from 'hazo_pdf';
635
+
636
+ const metadata: MetadataInput = {
637
+ header: [
638
+ { style: 'h1', label: 'Document Information' },
639
+ { style: 'body', label: 'Last updated: 2025-01-15' }
640
+ ],
641
+ data: [
642
+ {
643
+ label: 'Document Title',
644
+ style: 'h3',
645
+ value: 'Annual Report 2024',
646
+ editable: true
647
+ },
648
+ {
649
+ label: 'Author',
650
+ style: 'h4',
651
+ value: 'John Doe',
652
+ editable: true
653
+ },
654
+ {
655
+ label: 'Status',
656
+ style: 'body',
657
+ value: 'Draft',
658
+ editable: false
659
+ }
660
+ ],
661
+ footer: [
662
+ { style: 'body', label: 'Version 1.0' }
663
+ ]
664
+ };
665
+
666
+ <PdfViewer
667
+ url="/document.pdf"
668
+ sidepanel_metadata_enabled={true}
669
+ metadata_input={metadata}
670
+ on_metadata_change={(updatedRow, allData) => {
671
+ console.log('Updated row:', updatedRow);
672
+ console.log('All data:', allData);
673
+ // Save to server, update state, etc.
674
+ return { updatedRow, allData };
675
+ }}
676
+ />
677
+ ```
678
+
679
+ ### Metadata Structure
680
+
681
+ The `MetadataInput` interface has three sections:
682
+
683
+ #### Header Section
684
+ - Array of `MetadataHeaderItem` objects
685
+ - Each item has `style` (format type) and `label` (text)
686
+ - Rendered at the top of the panel
687
+
688
+ #### Data Section
689
+ - Array of `MetadataDataItem` objects
690
+ - Each item is rendered as a collapsible accordion (starts collapsed)
691
+ - Properties:
692
+ - `label`: Title shown as accordion header (required)
693
+ - `style`: Format type for the label (h1-h5, body) (required)
694
+ - `value`: Value to display (required)
695
+ - `editable`: Whether field can be edited (boolean, required)
696
+
697
+ #### Footer Section
698
+ - Array of `MetadataFooterItem` objects (same structure as header)
699
+ - Rendered at the bottom of the panel
700
+
701
+ ### Format Types
702
+
703
+ The `style` property accepts the following format types:
704
+ - `h1`: Large heading (3xl, bold)
705
+ - `h2`: Heading (2xl, bold)
706
+ - `h3`: Subheading (xl, semibold)
707
+ - `h4`: Smaller subheading (lg, semibold)
708
+ - `h5`: Small heading (base, semibold)
709
+ - `body`: Regular text (base)
710
+
711
+ ### Editable Fields
712
+
713
+ When `editable: true`:
714
+ - A pencil icon appears next to the value
715
+ - Clicking the pencil enters edit mode
716
+ - Edit mode shows:
717
+ - Text input field (single-line)
718
+ - Green circle-check button (save)
719
+ - Red circle-x button (cancel)
720
+ - Pressing `Enter` saves, `Escape` cancels
721
+ - On save, `on_metadata_change` callback is called with:
722
+ - `updatedRow`: The updated `MetadataDataItem`
723
+ - `allData`: The complete `MetadataInput` with all updates
724
+ - Callback must return `{ updatedRow, allData }`
725
+
726
+ ### Panel Controls
727
+
728
+ - **Toggle from toolbar**: Click the "Metadata" button in the toolbar
729
+ - **Toggle from right edge**: Click the chevron button on the right edge (when closed)
730
+ - **Resize**: Drag the left edge of the panel to resize (200px - 800px on desktop)
731
+ - **Close**: Click the chevron button in the panel header or toggle button in toolbar
732
+
733
+ ### Responsive Behavior
734
+
735
+ - **Desktop (> 1024px)**: Panel appears side-by-side with PDF viewer, max width 800px
736
+ - **Tablet (768px - 1024px)**: Panel max width is 50% of screen, can overlay
737
+ - **Mobile (< 768px)**: Panel uses overlay mode, max width 90vw, full height
738
+
739
+ ### Example: Complex Metadata (Test App Example)
740
+
741
+ The test app includes comprehensive test data demonstrating all metadata variations. This example shows all format types (h1-h5, body), editable and non-editable fields:
742
+
743
+ ```tsx
744
+ import type { MetadataInput, MetadataDataItem } from 'hazo_pdf';
745
+
746
+ const test_metadata: MetadataInput = {
747
+ header: [
748
+ { style: 'h1', label: 'Document Information' },
749
+ { style: 'h3', label: 'Test Document Metadata' },
750
+ { style: 'body', label: 'Last updated: 2025-01-15' }
751
+ ],
752
+ data: [
753
+ {
754
+ label: 'Document Title',
755
+ style: 'h2',
756
+ value: 'Annual Report 2024',
757
+ editable: true
758
+ },
759
+ {
760
+ label: 'Author Information',
761
+ style: 'h3',
762
+ value: 'John Doe\nSenior Analyst\nDepartment of Finance',
763
+ editable: true
764
+ },
765
+ {
766
+ label: 'Document Status',
767
+ style: 'h4',
768
+ value: 'Approved',
769
+ editable: false
770
+ },
771
+ {
772
+ label: 'Version',
773
+ style: 'h5',
774
+ value: '1.0.0',
775
+ editable: true
776
+ },
777
+ {
778
+ label: 'Category',
779
+ style: 'body',
780
+ value: 'Financial Report',
781
+ editable: false
782
+ },
783
+ {
784
+ label: 'Keywords',
785
+ style: 'body',
786
+ value: 'financial, annual, report, 2024',
787
+ editable: true
788
+ },
789
+ {
790
+ label: 'Document ID',
791
+ style: 'h5',
792
+ value: 'DOC-2024-001',
793
+ editable: false
794
+ },
795
+ {
796
+ label: 'Notes',
797
+ style: 'body',
798
+ value: 'This document contains comprehensive financial data for the fiscal year 2024. Please review all sections carefully before finalizing.',
799
+ editable: true
800
+ },
801
+ {
802
+ label: 'Confidentiality Level',
803
+ style: 'h4',
804
+ value: 'Internal Use Only',
805
+ editable: false
806
+ },
807
+ {
808
+ label: 'Approval Date',
809
+ style: 'body',
810
+ value: '2025-01-10',
811
+ editable: false
812
+ }
813
+ ],
814
+ footer: [
815
+ { style: 'body', label: 'This is a test metadata example' },
816
+ { style: 'h5', label: 'Version 1.0 - Test App' }
817
+ ]
818
+ };
819
+
820
+ <PdfViewer
821
+ url="/document.pdf"
822
+ sidepanel_metadata_enabled={true}
823
+ metadata_input={test_metadata}
824
+ on_metadata_change={(updatedRow, allData) => {
825
+ console.log('Updated:', updatedRow);
826
+ console.log('All data:', allData);
827
+ // Update state, save to backend, etc.
828
+ return { updatedRow, allData };
829
+ }}
830
+ />
831
+ ```
832
+
833
+ **This example demonstrates:**
834
+ - **Header section**: Multiple format types (h1, h3, body)
835
+ - **Data section**: All format types (h2, h3, h4, h5, body) with both editable and non-editable fields
836
+ - **Footer section**: Multiple format types (body, h5)
837
+ - **Multi-line values**: Author Information includes newlines
838
+ - **Long text values**: Notes field contains a longer description
839
+ - **Mixed editability**: Some fields editable, others read-only
840
+
841
+ **Note:** This test data is used in the test app (`app/viewer/[filename]/page.tsx`) to demonstrate all sidepanel metadata features.
842
+
843
+ ---
844
+
492
845
  ## Usage Tips
493
846
 
494
847
  ### Styling
@@ -561,6 +914,102 @@ The position and bracket style can be configured via the config file:
561
914
  - `suffix_enclosing_brackets`: Two-character string like `"[]"`, `"()"`, `"{}"` (default: `"[]"`)
562
915
  - `add_enclosing_brackets_to_suffixes`: `true` or `false` (default: `true`)
563
916
 
917
+ ### Responsive Design
918
+
919
+ The PDF viewer is fully responsive and adapts to different screen sizes:
920
+
921
+ **Desktop (> 1024px):**
922
+ - Full feature set available
923
+ - Sidepanel appears side-by-side with PDF (when enabled)
924
+ - All toolbar buttons visible
925
+ - Optimal viewing experience
926
+
927
+ **Tablet (768px - 1024px):**
928
+ - Sidepanel max width is 50% of screen (when enabled)
929
+ - Toolbar buttons may wrap to multiple rows
930
+ - Touch-friendly button sizes
931
+ - Horizontal scrolling available for zoomed PDFs
932
+
933
+ **Mobile (< 768px):**
934
+ - Sidepanel uses overlay mode (when enabled), max 90vw width
935
+ - Toolbar buttons wrap and use touch-friendly sizes (min 44px height)
936
+ - PDF viewer maintains full functionality
937
+ - Pan tool works with touch gestures
938
+ - All annotation tools remain accessible
939
+
940
+ **Breakpoints:**
941
+ - Mobile: `< 768px`
942
+ - Tablet: `768px - 1024px`
943
+ - Desktop: `> 1024px`
944
+
945
+ The viewer uses CSS media queries to adjust layout and component sizes automatically. All interactive elements are touch-friendly on mobile devices.
946
+
947
+ ---
948
+
949
+ ### Annotation Coordinate System
950
+
951
+ - Annotations use PDF coordinate space (points), not screen pixels.
952
+ - PDF coordinates start at the bottom-left (Y increases upward).
953
+ - The component handles coordinate conversion automatically.
954
+
955
+ ### Pan Tool
956
+
957
+ - Pan is the default tool (`current_tool = null`).
958
+ - Users can drag to scroll/pan the document.
959
+ - The cursor changes to a hand icon when panning.
960
+
961
+ ### Square Annotations
962
+
963
+ 1. Click the "Square" button in the toolbar.
964
+ 2. Click and drag on the PDF to create a rectangle.
965
+ 3. Right-click the rectangle to add a comment.
966
+
967
+ ### FreeText Annotations
968
+
969
+ 1. Right-click anywhere on the PDF.
970
+ 2. Select "Annotate" from the context menu.
971
+ 3. Enter text in the dialog.
972
+ 4. Click the checkmark to save.
973
+ 5. Left-click an existing FreeText annotation to edit or delete it.
974
+
975
+ ### Custom Stamps
976
+
977
+ 1. Configure stamps via `right_click_custom_stamps` prop or config file.
978
+ 2. Right-click on the PDF.
979
+ 3. Select a stamp from the bottom of the menu.
980
+ 4. The stamp text is added at the click position with optional timestamp/fixed text.
981
+
982
+ ### Saving Annotations
983
+
984
+ - Click the "Save" button in the toolbar.
985
+ - The `on_save` callback receives PDF bytes with annotations embedded using `pdf-lib`.
986
+ - You can download directly or upload to a server.
987
+
988
+ **Example download:**
989
+
990
+ ```tsx
991
+ const handleSave = (pdfBytes: Uint8Array, filename: string) => {
992
+ const blob = new Blob([pdfBytes], { type: 'application/pdf' });
993
+ const url = URL.createObjectURL(blob);
994
+ const a = document.createElement('a');
995
+ a.href = url;
996
+ a.download = filename || 'document.pdf';
997
+ a.click();
998
+ URL.revokeObjectURL(url);
999
+ };
1000
+ ```
1001
+
1002
+ ### Timestamp Formatting
1003
+
1004
+ When `append_timestamp_to_text_edits` is enabled, timestamps are formatted as:
1005
+ - Format: `YYYY-MM-DD h:mmam/pm`
1006
+ - Example: `2025-11-17 2:24pm`
1007
+
1008
+ The position and bracket style can be configured via the config file:
1009
+ - `suffix_text_position`: `"adjacent"`, `"below_single_line"`, or `"below_multi_line"` (default)
1010
+ - `suffix_enclosing_brackets`: Two-character string like `"[]"`, `"()"`, `"{}"` (default: `"[]"`)
1011
+ - `add_enclosing_brackets_to_suffixes`: `true` or `false` (default: `true`)
1012
+
564
1013
  ---
565
1014
 
566
1015
  ## Development
@@ -73,6 +73,37 @@ var default_config = {
73
73
  dialog_button_cancel_color: "#6b7280",
74
74
  dialog_button_cancel_color_hover: "#4b5563",
75
75
  dialog_button_disabled_opacity: 0.4
76
+ },
77
+ toolbar: {
78
+ toolbar_background_color: "#f9fafb",
79
+ toolbar_border_color: "#e5e7eb",
80
+ toolbar_font_family: "system-ui, -apple-system, sans-serif",
81
+ toolbar_font_size: 14,
82
+ toolbar_font_color: "#111827",
83
+ toolbar_button_background_color: "#ffffff",
84
+ toolbar_button_background_color_hover: "#f3f4f6",
85
+ toolbar_button_text_color: "#374151",
86
+ toolbar_button_active_background_color: "#3b82f6",
87
+ toolbar_button_active_text_color: "#ffffff",
88
+ toolbar_button_save_background_color: "#10b981",
89
+ toolbar_button_save_background_color_hover: "#059669",
90
+ toolbar_button_save_text_color: "#ffffff",
91
+ toolbar_button_disabled_opacity: 0.5,
92
+ toolbar_show_zoom_controls: true,
93
+ toolbar_show_square_button: true,
94
+ toolbar_show_undo_button: true,
95
+ toolbar_show_redo_button: true,
96
+ toolbar_show_save_button: true,
97
+ toolbar_show_metadata_button: true,
98
+ toolbar_zoom_out_label: "\u2212",
99
+ toolbar_zoom_in_label: "+",
100
+ toolbar_zoom_reset_label: "Reset",
101
+ toolbar_square_label: "Square",
102
+ toolbar_undo_label: "Undo",
103
+ toolbar_redo_label: "Redo",
104
+ toolbar_save_label: "Save",
105
+ toolbar_saving_label: "Saving...",
106
+ toolbar_metadata_label: "Metadata"
76
107
  }
77
108
  };
78
109
 
@@ -280,4 +311,4 @@ export {
280
311
  download_pdf,
281
312
  save_and_download_pdf
282
313
  };
283
- //# sourceMappingURL=chunk-2POHPGR3.js.map
314
+ //# sourceMappingURL=chunk-ZQMZT7HV.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_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":[]}