hazo_pdf 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 pub12
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # Hazo PDF
2
+
3
+ A React component library for viewing and annotating PDF documents.
4
+
5
+ ## Features
6
+
7
+ - PDF viewing
8
+ - Annotation support
9
+ - Built with React, TypeScript, and TailwindCSS
10
+ - Lightweight and customizable
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install hazo_pdf
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ import { PdfViewer } from 'hazo_pdf';
22
+ import 'hazo_pdf/styles.css';
23
+
24
+ function App() {
25
+ return (
26
+ <PdfViewer
27
+ url="/path/to/document.pdf"
28
+ />
29
+ );
30
+ }
31
+ ```
32
+
33
+ ## Development
34
+
35
+ ### Setup
36
+
37
+ ```bash
38
+ npm install
39
+ ```
40
+
41
+ ### Build
42
+
43
+ ```bash
44
+ npm run build
45
+ ```
46
+
47
+ ### Watch Mode
48
+
49
+ ```bash
50
+ npm run dev
51
+ ```
52
+
53
+ ## License
54
+
55
+ MIT © Pubs Abayasiri
56
+
@@ -0,0 +1,283 @@
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
+ // src/config/default_config.ts
10
+ var default_config = {
11
+ fonts: {
12
+ freetext_font_family: "Arial, sans-serif",
13
+ freetext_font_size_min: 12,
14
+ freetext_font_size_max: 24,
15
+ freetext_font_size_default: 14,
16
+ font_foreground_color: "#000000"
17
+ // Black by default
18
+ },
19
+ highlight_annotation: {
20
+ highlight_fill_color: "#FFFF00",
21
+ highlight_fill_opacity: 0.3,
22
+ highlight_border_color: "#FFD700",
23
+ highlight_border_color_hover: "#FFD700",
24
+ highlight_fill_opacity_hover: 0.4
25
+ },
26
+ square_annotation: {
27
+ square_fill_color: "#FF0000",
28
+ square_fill_opacity: 0.2,
29
+ square_border_color: "#FF0000",
30
+ square_border_color_hover: "#CC0000",
31
+ square_fill_opacity_hover: 0.3
32
+ },
33
+ freetext_annotation: {
34
+ freetext_text_color: "#000000",
35
+ freetext_text_color_hover: "#000000",
36
+ freetext_border_color: "#003366",
37
+ freetext_border_width: 1,
38
+ freetext_background_color: "#E6F3FF",
39
+ freetext_background_opacity: 0.1,
40
+ freetext_font_weight: "normal",
41
+ freetext_font_style: "normal",
42
+ freetext_text_decoration: "none",
43
+ freetext_padding_horizontal: 4,
44
+ freetext_padding_vertical: 2
45
+ },
46
+ page_styling: {
47
+ page_border_color: "#999999",
48
+ page_box_shadow: "0 2px 8px rgba(0, 0, 0, 0.3)",
49
+ page_background_color: "#ffffff"
50
+ },
51
+ viewer: {
52
+ viewer_background_color: "#2d2d2d",
53
+ append_timestamp_to_text_edits: false,
54
+ annotation_text_suffix_fixed_text: "",
55
+ add_enclosing_brackets_to_suffixes: true,
56
+ suffix_enclosing_brackets: "[]",
57
+ suffix_text_position: "below_multi_line"
58
+ },
59
+ context_menu: {
60
+ context_menu_background_color: "#ffffff",
61
+ context_menu_border_color: "#d1d5db",
62
+ context_menu_item_hover_background: "#f3f4f6",
63
+ context_menu_item_disabled_opacity: 0.5,
64
+ right_click_custom_stamps: ""
65
+ // Empty string means no custom stamps
66
+ },
67
+ dialog: {
68
+ dialog_backdrop_opacity: 0.2,
69
+ dialog_background_color: "#ffffff",
70
+ dialog_border_color: "#d1d5db",
71
+ dialog_button_submit_color: "#16a34a",
72
+ dialog_button_submit_color_hover: "#15803d",
73
+ dialog_button_cancel_color: "#6b7280",
74
+ dialog_button_cancel_color_hover: "#4b5563",
75
+ dialog_button_disabled_opacity: 0.4
76
+ }
77
+ };
78
+
79
+ // src/utils/pdf_saver.ts
80
+ async function save_annotations_to_pdf(pdf_url, annotations, _output_filename, config) {
81
+ try {
82
+ const { PDFDocument, rgb } = await import("pdf-lib");
83
+ console.log("[PDF Saver] Fetching PDF from:", pdf_url);
84
+ const pdf_response = await fetch(pdf_url);
85
+ if (!pdf_response.ok) {
86
+ throw new Error(`Failed to fetch PDF: ${pdf_response.status} ${pdf_response.statusText}`);
87
+ }
88
+ const pdf_bytes = await pdf_response.arrayBuffer();
89
+ console.log("[PDF Saver] PDF fetched, size:", pdf_bytes.byteLength, "bytes");
90
+ const pdf_doc = await PDFDocument.load(pdf_bytes);
91
+ console.log("[PDF Saver] PDF loaded, pages:", pdf_doc.getPageCount());
92
+ const fonts_config = config?.fonts || default_config.fonts;
93
+ const highlight_config = config?.highlight_annotation || default_config.highlight_annotation;
94
+ const square_config = config?.square_annotation || default_config.square_annotation;
95
+ const freetext_config = config?.freetext_annotation || default_config.freetext_annotation;
96
+ for (const annotation of annotations) {
97
+ if (annotation.page_index >= pdf_doc.getPageCount()) {
98
+ console.warn(`[PDF Saver] Annotation ${annotation.id} references page ${annotation.page_index}, but PDF only has ${pdf_doc.getPageCount()} pages. Skipping.`);
99
+ continue;
100
+ }
101
+ const page = pdf_doc.getPage(annotation.page_index);
102
+ const [x1, y1, x2, y2] = annotation.rect;
103
+ const rect_x = Math.min(x1, x2);
104
+ const rect_y = Math.min(y1, y2);
105
+ const rect_width = Math.abs(x2 - x1);
106
+ const rect_height = Math.abs(y2 - y1);
107
+ let color = rgb(0, 0, 0);
108
+ if (annotation.color) {
109
+ const color_str = annotation.color.trim();
110
+ const rgb_pattern = /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i;
111
+ const rgb_match = color_str.match(rgb_pattern);
112
+ if (rgb_match) {
113
+ const r = parseInt(rgb_match[1], 10);
114
+ const g = parseInt(rgb_match[2], 10);
115
+ const b = parseInt(rgb_match[3], 10);
116
+ if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
117
+ color = rgb(r / 255, g / 255, b / 255);
118
+ } else {
119
+ console.warn(`[PDF Saver] Invalid RGB values for annotation ${annotation.id}: r=${r}, g=${g}, b=${b}`);
120
+ }
121
+ } else if (color_str.startsWith("#")) {
122
+ const hex = color_str.slice(1).trim();
123
+ if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {
124
+ const r = parseInt(hex.substring(0, 2), 16) / 255;
125
+ const g = parseInt(hex.substring(2, 4), 16) / 255;
126
+ const b = parseInt(hex.substring(4, 6), 16) / 255;
127
+ color = rgb(r, g, b);
128
+ } else {
129
+ console.warn(`[PDF Saver] Invalid hex color format for annotation ${annotation.id}: ${color_str}`);
130
+ }
131
+ } else {
132
+ console.warn(`[PDF Saver] Unrecognized color format for annotation ${annotation.id}: ${color_str}`);
133
+ }
134
+ }
135
+ try {
136
+ switch (annotation.type) {
137
+ case "Square": {
138
+ page.drawRectangle({
139
+ x: rect_x,
140
+ y: rect_y,
141
+ width: rect_width,
142
+ height: rect_height,
143
+ borderColor: color,
144
+ borderWidth: 2,
145
+ borderOpacity: 0.8
146
+ });
147
+ if (annotation.contents) {
148
+ const comment_color_str = (annotation.color || square_config.square_border_color).trim();
149
+ let comment_r = 0, comment_g = 0, comment_b = 0;
150
+ const rgb_pattern = /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i;
151
+ const rgb_match = comment_color_str.match(rgb_pattern);
152
+ if (rgb_match) {
153
+ comment_r = parseInt(rgb_match[1], 10) / 255;
154
+ comment_g = parseInt(rgb_match[2], 10) / 255;
155
+ comment_b = parseInt(rgb_match[3], 10) / 255;
156
+ } else if (comment_color_str.startsWith("#")) {
157
+ const hex = comment_color_str.slice(1).trim();
158
+ if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {
159
+ comment_r = parseInt(hex.substring(0, 2), 16) / 255;
160
+ comment_g = parseInt(hex.substring(2, 4), 16) / 255;
161
+ comment_b = parseInt(hex.substring(4, 6), 16) / 255;
162
+ }
163
+ }
164
+ page.drawText(annotation.contents, {
165
+ x: rect_x,
166
+ y: rect_y + rect_height + 5,
167
+ size: fonts_config.freetext_font_size_default,
168
+ color: rgb(comment_r, comment_g, comment_b)
169
+ });
170
+ }
171
+ break;
172
+ }
173
+ case "Highlight": {
174
+ const highlight_color_str = (annotation.color || highlight_config.highlight_fill_color).trim();
175
+ let highlight_r = 255 / 255, highlight_g = 255 / 255, highlight_b = 0 / 255;
176
+ const rgb_pattern = /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i;
177
+ const rgb_match = highlight_color_str.match(rgb_pattern);
178
+ if (rgb_match) {
179
+ highlight_r = parseInt(rgb_match[1], 10) / 255;
180
+ highlight_g = parseInt(rgb_match[2], 10) / 255;
181
+ highlight_b = parseInt(rgb_match[3], 10) / 255;
182
+ } else if (highlight_color_str.startsWith("#")) {
183
+ const hex = highlight_color_str.slice(1).trim();
184
+ if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {
185
+ highlight_r = parseInt(hex.substring(0, 2), 16) / 255;
186
+ highlight_g = parseInt(hex.substring(2, 4), 16) / 255;
187
+ highlight_b = parseInt(hex.substring(4, 6), 16) / 255;
188
+ }
189
+ }
190
+ const highlight_color = rgb(highlight_r, highlight_g, highlight_b);
191
+ page.drawRectangle({
192
+ x: rect_x,
193
+ y: rect_y,
194
+ width: rect_width,
195
+ height: rect_height,
196
+ color: highlight_color,
197
+ opacity: highlight_config.highlight_fill_opacity
198
+ });
199
+ if (annotation.contents) {
200
+ page.drawText(annotation.contents, {
201
+ x: rect_x,
202
+ y: rect_y + rect_height + 5,
203
+ size: fonts_config.freetext_font_size_default,
204
+ color: rgb(0, 0, 0)
205
+ });
206
+ }
207
+ break;
208
+ }
209
+ case "FreeText": {
210
+ if (annotation.contents) {
211
+ const text_color_str = (annotation.color || freetext_config.freetext_text_color || fonts_config.font_foreground_color || "#000000").trim();
212
+ let text_r = 0, text_g = 0, text_b = 0;
213
+ const rgb_pattern = /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i;
214
+ const rgb_match = text_color_str.match(rgb_pattern);
215
+ if (rgb_match) {
216
+ text_r = parseInt(rgb_match[1], 10) / 255;
217
+ text_g = parseInt(rgb_match[2], 10) / 255;
218
+ text_b = parseInt(rgb_match[3], 10) / 255;
219
+ } else if (text_color_str.startsWith("#")) {
220
+ const hex = text_color_str.slice(1).trim();
221
+ if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {
222
+ text_r = parseInt(hex.substring(0, 2), 16) / 255;
223
+ text_g = parseInt(hex.substring(2, 4), 16) / 255;
224
+ text_b = parseInt(hex.substring(4, 6), 16) / 255;
225
+ }
226
+ }
227
+ page.drawText(annotation.contents, {
228
+ x: rect_x,
229
+ y: rect_y,
230
+ size: fonts_config.freetext_font_size_default,
231
+ color: rgb(text_r, text_g, text_b),
232
+ maxWidth: rect_width
233
+ });
234
+ }
235
+ break;
236
+ }
237
+ default:
238
+ console.warn(`[PDF Saver] Unsupported annotation type: ${annotation.type}`);
239
+ }
240
+ } catch (annotation_error) {
241
+ console.error(`[PDF Saver] Error adding annotation ${annotation.id}:`, annotation_error);
242
+ }
243
+ }
244
+ const modified_pdf_bytes = await pdf_doc.save();
245
+ console.log("[PDF Saver] PDF modified, size:", modified_pdf_bytes.length, "bytes");
246
+ return modified_pdf_bytes;
247
+ } catch (error) {
248
+ if (error && typeof error === "object" && "message" in error && typeof error.message === "string" && error.message.includes("pdf-lib")) {
249
+ throw new Error("pdf-lib is required to save annotations to PDF. Please install it: npm install pdf-lib");
250
+ }
251
+ console.error("[PDF Saver] Error saving PDF:", error);
252
+ throw error;
253
+ }
254
+ }
255
+ function download_pdf(pdf_bytes, filename) {
256
+ const buffer = new Uint8Array(pdf_bytes).buffer;
257
+ const blob = new Blob([buffer], { type: "application/pdf" });
258
+ const url = URL.createObjectURL(blob);
259
+ const link = document.createElement("a");
260
+ link.href = url;
261
+ link.download = filename;
262
+ document.body.appendChild(link);
263
+ link.click();
264
+ document.body.removeChild(link);
265
+ URL.revokeObjectURL(url);
266
+ }
267
+ async function save_and_download_pdf(pdf_url, annotations, output_filename, config) {
268
+ const pdf_bytes = await save_annotations_to_pdf(pdf_url, annotations, output_filename, config);
269
+ const original_filename = pdf_url.split("/").pop() || "document.pdf";
270
+ const filename_without_ext = original_filename.replace(/\.pdf$/i, "");
271
+ const final_filename = output_filename || `${filename_without_ext}_annotated.pdf`;
272
+ download_pdf(pdf_bytes, final_filename);
273
+ console.log("[PDF Saver] PDF saved as:", final_filename);
274
+ }
275
+
276
+ export {
277
+ __require,
278
+ default_config,
279
+ save_annotations_to_pdf,
280
+ download_pdf,
281
+ save_and_download_pdf
282
+ };
283
+ //# sourceMappingURL=chunk-2POHPGR3.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","/**\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":[]}