File Coverage

src/pdfmake_annot.c
Criterion Covered Total %
statement 86 137 62.7
branch 23 74 31.0
condition n/a
subroutine n/a
pod n/a
total 109 211 51.6


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_annot.c — PDF annotation builders implementation
3             */
4              
5             #include "pdfmake_annot.h"
6             #include "pdfmake_arena.h"
7             #include
8             #include
9              
10             /*============================================================================
11             * Helper: Build common annotation dict entries
12             *==========================================================================*/
13              
14 33           static pdfmake_obj_t build_annot_base(pdfmake_arena_t *arena,
15             const char *subtype,
16             pdfmake_rect_t rect) {
17             pdfmake_obj_t dict;
18             uint32_t type_key;
19             uint32_t subtype_key;
20             uint32_t rect_key;
21             pdfmake_obj_t rect_arr;
22              
23 33           dict = pdfmake_dict_new(arena);
24 33 50         if (dict.kind != PDFMAKE_DICT) return dict;
25            
26 33           type_key = pdfmake_arena_intern_name(arena, "Type", 4);
27 33           subtype_key = pdfmake_arena_intern_name(arena, "Subtype", 7);
28 33           rect_key = pdfmake_arena_intern_name(arena, "Rect", 4);
29            
30 33           pdfmake_dict_set(arena, &dict, type_key, pdfmake_name_cstr(arena, "Annot"));
31 33           pdfmake_dict_set(arena, &dict, subtype_key, pdfmake_name_cstr(arena, subtype));
32            
33             /* Build /Rect array */
34 33           rect_arr = pdfmake_array_new(arena);
35 33           pdfmake_array_push(arena, &rect_arr, pdfmake_real(rect.x1));
36 33           pdfmake_array_push(arena, &rect_arr, pdfmake_real(rect.y1));
37 33           pdfmake_array_push(arena, &rect_arr, pdfmake_real(rect.x2));
38 33           pdfmake_array_push(arena, &rect_arr, pdfmake_real(rect.y2));
39 33           pdfmake_dict_set(arena, &dict, rect_key, rect_arr);
40            
41 33           return dict;
42             }
43              
44             /*============================================================================
45             * Text annotation
46             *==========================================================================*/
47              
48             static const char *icon_names[] = {
49             "Note",
50             "Comment",
51             "Key",
52             "Help",
53             "Paragraph",
54             "NewParagraph",
55             "Insert"
56             };
57              
58 8           uint32_t pdfmake_annot_text(pdfmake_doc_t *doc,
59             pdfmake_rect_t rect,
60             const char *contents,
61             pdfmake_annot_icon_t icon,
62             int open) {
63             pdfmake_arena_t *arena;
64             pdfmake_obj_t dict;
65             uint32_t contents_key;
66             uint32_t open_key;
67             uint32_t name_key;
68              
69 8 50         if (!doc || !contents) return 0;
    50          
70 8 50         if (icon > PDFMAKE_ANNOT_ICON_INSERT) return 0;
71            
72 8           arena = pdfmake_doc_arena(doc);
73            
74 8           dict = build_annot_base(arena, "Text", rect);
75 8 50         if (dict.kind != PDFMAKE_DICT) return 0;
76            
77 8           contents_key = pdfmake_arena_intern_name(arena, "Contents", 8);
78 8           open_key = pdfmake_arena_intern_name(arena, "Open", 4);
79 8           name_key = pdfmake_arena_intern_name(arena, "Name", 4);
80            
81 8           pdfmake_dict_set(arena, &dict, contents_key,
82             pdfmake_str(arena, contents, strlen(contents)));
83 8           pdfmake_dict_set(arena, &dict, open_key, pdfmake_bool(open));
84 8           pdfmake_dict_set(arena, &dict, name_key,
85             pdfmake_name_cstr(arena, icon_names[icon]));
86            
87 8           return pdfmake_doc_add(doc, dict);
88             }
89              
90             /*============================================================================
91             * Markup annotations (Highlight, Underline, etc.)
92             *==========================================================================*/
93              
94             static const char *markup_subtypes[] = {
95             "Highlight",
96             "Underline",
97             "Squiggly",
98             "StrikeOut"
99             };
100              
101 0           uint32_t pdfmake_annot_markup(pdfmake_doc_t *doc,
102             pdfmake_rect_t rect,
103             const double *quads,
104             size_t quad_count,
105             pdfmake_markup_type_t type,
106             const double *color) {
107             pdfmake_arena_t *arena;
108             pdfmake_obj_t dict;
109             uint32_t qp_key;
110             pdfmake_obj_t qp_arr;
111             size_t num_values;
112             size_t i;
113             uint32_t c_key;
114             pdfmake_obj_t c_arr;
115              
116 0 0         if (!doc || !quads || quad_count == 0) return 0;
    0          
    0          
117 0 0         if (type > PDFMAKE_MARKUP_STRIKEOUT) return 0;
118            
119 0           arena = pdfmake_doc_arena(doc);
120            
121 0           dict = build_annot_base(arena, markup_subtypes[type], rect);
122 0 0         if (dict.kind != PDFMAKE_DICT) return 0;
123            
124             /* Build /QuadPoints array */
125 0           qp_key = pdfmake_arena_intern_name(arena, "QuadPoints", 10);
126 0           qp_arr = pdfmake_array_new(arena);
127            
128 0           num_values = quad_count * 8; /* 8 values per quad */
129 0 0         for (i = 0; i < num_values; i++) {
130 0           pdfmake_array_push(arena, &qp_arr, pdfmake_real(quads[i]));
131             }
132 0           pdfmake_dict_set(arena, &dict, qp_key, qp_arr);
133            
134             /* Set color if provided */
135 0 0         if (color) {
136 0           c_key = pdfmake_arena_intern_name(arena, "C", 1);
137 0           c_arr = pdfmake_array_new(arena);
138 0           pdfmake_array_push(arena, &c_arr, pdfmake_real(color[0]));
139 0           pdfmake_array_push(arena, &c_arr, pdfmake_real(color[1]));
140 0           pdfmake_array_push(arena, &c_arr, pdfmake_real(color[2]));
141 0           pdfmake_dict_set(arena, &dict, c_key, c_arr);
142             }
143            
144 0           return pdfmake_doc_add(doc, dict);
145             }
146              
147 0           uint32_t pdfmake_annot_highlight(pdfmake_doc_t *doc,
148             pdfmake_rect_t rect,
149             const double *quads,
150             size_t quad_count,
151             const double *color) {
152 0           return pdfmake_annot_markup(doc, rect, quads, quad_count,
153             PDFMAKE_MARKUP_HIGHLIGHT, color);
154             }
155              
156             /*============================================================================
157             * Link annotation
158             *==========================================================================*/
159              
160 5           uint32_t pdfmake_annot_link_uri(pdfmake_doc_t *doc,
161             pdfmake_rect_t rect,
162             const char *uri) {
163             pdfmake_arena_t *arena;
164             pdfmake_obj_t dict;
165             pdfmake_obj_t action;
166             uint32_t s_key;
167             uint32_t uri_key;
168             uint32_t a_key;
169             uint32_t border_key;
170             pdfmake_obj_t border;
171              
172 5 50         if (!doc || !uri) return 0;
    50          
173            
174 5           arena = pdfmake_doc_arena(doc);
175            
176 5           dict = build_annot_base(arena, "Link", rect);
177 5 50         if (dict.kind != PDFMAKE_DICT) return 0;
178            
179             /* Build URI action dict */
180 5           action = pdfmake_dict_new(arena);
181 5           s_key = pdfmake_arena_intern_name(arena, "S", 1);
182 5           uri_key = pdfmake_arena_intern_name(arena, "URI", 3);
183            
184 5           pdfmake_dict_set(arena, &action, s_key, pdfmake_name_cstr(arena, "URI"));
185 5           pdfmake_dict_set(arena, &action, uri_key,
186             pdfmake_str(arena, uri, strlen(uri)));
187            
188             /* Set /A (action) in link annotation */
189 5           a_key = pdfmake_arena_intern_name(arena, "A", 1);
190 5           pdfmake_dict_set(arena, &dict, a_key, action);
191            
192             /* /Border [0 0 0] - no visible border */
193 5           border_key = pdfmake_arena_intern_name(arena, "Border", 6);
194 5           border = pdfmake_array_new(arena);
195 5           pdfmake_array_push(arena, &border, pdfmake_int(0));
196 5           pdfmake_array_push(arena, &border, pdfmake_int(0));
197 5           pdfmake_array_push(arena, &border, pdfmake_int(0));
198 5           pdfmake_dict_set(arena, &dict, border_key, border);
199            
200 5           return pdfmake_doc_add(doc, dict);
201             }
202              
203 15           uint32_t pdfmake_annot_link_goto(pdfmake_doc_t *doc,
204             pdfmake_rect_t rect,
205             size_t dest_page) {
206             pdfmake_arena_t *arena;
207             pdfmake_obj_t dict;
208             uint32_t dest_key;
209             pdfmake_obj_t dest_arr;
210             pdfmake_page_t *page;
211             uint32_t border_key;
212             pdfmake_obj_t border;
213              
214 15 50         if (!doc) return 0;
215 15 50         if (dest_page >= pdfmake_doc_page_count(doc)) return 0;
216            
217 15           arena = pdfmake_doc_arena(doc);
218            
219 15           dict = build_annot_base(arena, "Link", rect);
220 15 50         if (dict.kind != PDFMAKE_DICT) return 0;
221            
222             /* Build /Dest array: [page /Fit] */
223 15           dest_key = pdfmake_arena_intern_name(arena, "Dest", 4);
224 15           dest_arr = pdfmake_array_new(arena);
225            
226             /* Get page reference */
227 15           page = pdfmake_doc_get_page(doc, dest_page);
228 15 50         if (page && page->page_num > 0) {
    50          
229 0           pdfmake_array_push(arena, &dest_arr, pdfmake_ref(page->page_num, 0));
230             } else {
231             /* Page not yet finalized - use page index */
232 15           pdfmake_array_push(arena, &dest_arr, pdfmake_int((int64_t)dest_page));
233             }
234 15           pdfmake_array_push(arena, &dest_arr, pdfmake_name_cstr(arena, "Fit"));
235            
236 15           pdfmake_dict_set(arena, &dict, dest_key, dest_arr);
237            
238             /* /Border [0 0 0] */
239 15           border_key = pdfmake_arena_intern_name(arena, "Border", 6);
240 15           border = pdfmake_array_new(arena);
241 15           pdfmake_array_push(arena, &border, pdfmake_int(0));
242 15           pdfmake_array_push(arena, &border, pdfmake_int(0));
243 15           pdfmake_array_push(arena, &border, pdfmake_int(0));
244 15           pdfmake_dict_set(arena, &dict, border_key, border);
245            
246 15           return pdfmake_doc_add(doc, dict);
247             }
248              
249             /*============================================================================
250             * Stamp annotation
251             *==========================================================================*/
252              
253             static const char *stamp_names[] = {
254             "Approved",
255             "Experimental",
256             "NotApproved",
257             "AsIs",
258             "Expired",
259             "NotForPublicRelease",
260             "Confidential",
261             "Final",
262             "Sold",
263             "Departmental",
264             "ForLegalReview",
265             "TopSecret",
266             "Draft",
267             "ForComment"
268             };
269              
270 5           uint32_t pdfmake_annot_stamp(pdfmake_doc_t *doc,
271             pdfmake_rect_t rect,
272             pdfmake_stamp_type_t type) {
273             pdfmake_arena_t *arena;
274             pdfmake_obj_t dict;
275             uint32_t name_key;
276              
277 5 50         if (!doc) return 0;
278 5 50         if (type > PDFMAKE_STAMP_FORCOMMENT) return 0;
279            
280 5           arena = pdfmake_doc_arena(doc);
281            
282 5           dict = build_annot_base(arena, "Stamp", rect);
283 5 50         if (dict.kind != PDFMAKE_DICT) return 0;
284            
285 5           name_key = pdfmake_arena_intern_name(arena, "Name", 4);
286 5           pdfmake_dict_set(arena, &dict, name_key,
287             pdfmake_name_cstr(arena, stamp_names[type]));
288            
289 5           return pdfmake_doc_add(doc, dict);
290             }
291              
292             /*============================================================================
293             * Ink annotation
294             *==========================================================================*/
295              
296 0           uint32_t pdfmake_annot_ink(pdfmake_doc_t *doc,
297             pdfmake_rect_t rect,
298             const double **paths,
299             const size_t *path_counts,
300             size_t num_paths,
301             const double *color,
302             double width) {
303             pdfmake_arena_t *arena;
304             pdfmake_obj_t dict;
305             uint32_t inklist_key;
306             pdfmake_obj_t inklist;
307             size_t p;
308             pdfmake_obj_t path_arr;
309             size_t num_coords;
310             size_t i;
311             uint32_t c_key;
312             pdfmake_obj_t c_arr;
313             uint32_t bs_key;
314             pdfmake_obj_t bs;
315             uint32_t w_key;
316              
317 0 0         if (!doc || !paths || !path_counts || num_paths == 0) return 0;
    0          
    0          
    0          
318            
319 0           arena = pdfmake_doc_arena(doc);
320            
321 0           dict = build_annot_base(arena, "Ink", rect);
322 0 0         if (dict.kind != PDFMAKE_DICT) return 0;
323            
324             /* Build /InkList array of arrays */
325 0           inklist_key = pdfmake_arena_intern_name(arena, "InkList", 7);
326 0           inklist = pdfmake_array_new(arena);
327            
328 0 0         for (p = 0; p < num_paths; p++) {
329 0           path_arr = pdfmake_array_new(arena);
330 0           num_coords = path_counts[p] * 2; /* x,y pairs */
331 0 0         for (i = 0; i < num_coords; i++) {
332 0           pdfmake_array_push(arena, &path_arr, pdfmake_real(paths[p][i]));
333             }
334 0           pdfmake_array_push(arena, &inklist, path_arr);
335             }
336 0           pdfmake_dict_set(arena, &dict, inklist_key, inklist);
337            
338             /* Set color if provided */
339 0 0         if (color) {
340 0           c_key = pdfmake_arena_intern_name(arena, "C", 1);
341 0           c_arr = pdfmake_array_new(arena);
342 0           pdfmake_array_push(arena, &c_arr, pdfmake_real(color[0]));
343 0           pdfmake_array_push(arena, &c_arr, pdfmake_real(color[1]));
344 0           pdfmake_array_push(arena, &c_arr, pdfmake_real(color[2]));
345 0           pdfmake_dict_set(arena, &dict, c_key, c_arr);
346             }
347            
348             /* Set border style for line width */
349 0 0         if (width > 0) {
350 0           bs_key = pdfmake_arena_intern_name(arena, "BS", 2);
351 0           bs = pdfmake_dict_new(arena);
352 0           w_key = pdfmake_arena_intern_name(arena, "W", 1);
353 0           pdfmake_dict_set(arena, &bs, w_key, pdfmake_real(width));
354 0           pdfmake_dict_set(arena, &dict, bs_key, bs);
355             }
356            
357 0           return pdfmake_doc_add(doc, dict);
358             }
359              
360             /*============================================================================
361             * Page annotation attachment
362             *==========================================================================*/
363              
364 70           pdfmake_err_t pdfmake_page_add_annot(pdfmake_page_t *page, uint32_t annot_num) {
365             size_t new_cap;
366             uint32_t *new_arr;
367              
368 70 50         if (!page || annot_num == 0) return PDFMAKE_EINVAL;
    50          
369              
370             /* Grow annots array if needed */
371 70 100         if (page->annot_count >= page->annot_cap) {
372 28 100         new_cap = page->annot_cap ? page->annot_cap * 2 : 8;
373 28           new_arr = realloc(page->annots, new_cap * sizeof(uint32_t));
374 28 50         if (!new_arr) return PDFMAKE_ENOMEM;
375 28           page->annots = new_arr;
376 28           page->annot_cap = new_cap;
377             }
378 70           page->annots[page->annot_count++] = annot_num;
379 70           return PDFMAKE_OK;
380             }