File Coverage

src/pdfmake_action.c
Criterion Covered Total %
statement 137 196 69.9
branch 41 88 46.5
condition n/a
subroutine n/a
pod n/a
total 178 284 62.6


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_action.c — PDF action system implementation
3             *
4             * Creates action dictionaries for link navigation, JavaScript, etc.
5             */
6              
7             #include "pdfmake_action.h"
8             #include "pdfmake_arena.h"
9             #include "pdfmake_annot.h"
10             #include
11             #include
12              
13             /*============================================================================
14             * Named action string mapping
15             *==========================================================================*/
16              
17             static const char *named_action_strings[] = {
18             "NextPage",
19             "PrevPage",
20             "FirstPage",
21             "LastPage",
22             "Print"
23             };
24              
25             static const char *highlight_mode_names[] = {
26             "N", /* None */
27             "I", /* Invert */
28             "O", /* Outline */
29             "P" /* Push */
30             };
31              
32             /*============================================================================
33             * Action allocation helper
34             *==========================================================================*/
35              
36 29           static pdfmake_action_t *alloc_action(pdfmake_doc_t *doc, pdfmake_action_type_t type)
37             {
38             pdfmake_action_t *action;
39 29 50         if (!doc) return NULL;
40            
41 29           action = pdfmake_arena_alloc(doc->arena, sizeof(pdfmake_action_t));
42 29 50         if (!action) return NULL;
43            
44 29           memset(action, 0, sizeof(*action));
45 29           action->type = type;
46 29           action->doc = doc;
47            
48 29           return action;
49             }
50              
51             /*============================================================================
52             * Action builders
53             *==========================================================================*/
54              
55 6           pdfmake_action_t *pdfmake_action_goto(pdfmake_doc_t *doc, pdfmake_dest_t dest)
56             {
57 6           pdfmake_action_t *action = alloc_action(doc, PDFMAKE_ACTION_GOTO);
58 6 50         if (!action) return NULL;
59            
60 6           action->data.go_to.dest = dest;
61 6           return action;
62             }
63              
64 5           pdfmake_action_t *pdfmake_action_gotor(pdfmake_doc_t *doc,
65             const char *file,
66             pdfmake_dest_t dest,
67             int new_window)
68             {
69             pdfmake_action_t *action;
70 5 50         if (!file) return NULL;
71            
72 5           action = alloc_action(doc, PDFMAKE_ACTION_GOTOR);
73 5 50         if (!action) return NULL;
74            
75 5           action->data.go_tor.file = pdfmake_arena_strdup(doc->arena, file);
76 5           action->data.go_tor.dest = dest;
77 5           action->data.go_tor.new_window = new_window;
78            
79 5 50         if (!action->data.go_tor.file) return NULL;
80 5           return action;
81             }
82              
83 5           pdfmake_action_t *pdfmake_action_uri(pdfmake_doc_t *doc, const char *uri)
84             {
85             pdfmake_action_t *action;
86 5 50         if (!uri) return NULL;
87            
88 5           action = alloc_action(doc, PDFMAKE_ACTION_URI);
89 5 50         if (!action) return NULL;
90            
91 5           action->data.uri.uri = pdfmake_arena_strdup(doc->arena, uri);
92 5           action->data.uri.is_map = 0;
93            
94 5 50         if (!action->data.uri.uri) return NULL;
95 5           return action;
96             }
97              
98 11           pdfmake_action_t *pdfmake_action_named(pdfmake_doc_t *doc, pdfmake_named_action_t name)
99             {
100 11           pdfmake_action_t *action = alloc_action(doc, PDFMAKE_ACTION_NAMED);
101 11 50         if (!action) return NULL;
102            
103 11           action->data.named.name = name;
104 11           return action;
105             }
106              
107 11           pdfmake_action_t *pdfmake_action_named_str(pdfmake_doc_t *doc, const char *name_str)
108             {
109             pdfmake_named_action_t name;
110 11 50         if (!name_str) return NULL;
111            
112 11 100         if (strcmp(name_str, "NextPage") == 0) {
113 6           name = PDFMAKE_NAMED_NEXTPAGE;
114 5 100         } else if (strcmp(name_str, "PrevPage") == 0) {
115 2           name = PDFMAKE_NAMED_PREVPAGE;
116 3 100         } else if (strcmp(name_str, "FirstPage") == 0) {
117 1           name = PDFMAKE_NAMED_FIRSTPAGE;
118 2 100         } else if (strcmp(name_str, "LastPage") == 0) {
119 1           name = PDFMAKE_NAMED_LASTPAGE;
120 1 50         } else if (strcmp(name_str, "Print") == 0) {
121 1           name = PDFMAKE_NAMED_PRINT;
122             } else {
123 0           return NULL; /* Unknown named action */
124             }
125            
126 11           return pdfmake_action_named(doc, name);
127             }
128              
129 2           pdfmake_action_t *pdfmake_action_javascript(pdfmake_doc_t *doc, const char *script)
130             {
131             pdfmake_action_t *action;
132 2 50         if (!script) return NULL;
133            
134 2           action = alloc_action(doc, PDFMAKE_ACTION_JAVASCRIPT);
135 2 50         if (!action) return NULL;
136            
137 2           action->data.javascript.script = pdfmake_arena_strdup(doc->arena, script);
138            
139 2 50         if (!action->data.javascript.script) return NULL;
140 2           return action;
141             }
142              
143 0           pdfmake_action_t *pdfmake_action_hide(pdfmake_doc_t *doc,
144             const char **targets,
145             size_t target_count,
146             int hide)
147             {
148             pdfmake_action_t *action;
149             size_t i;
150 0 0         if (target_count == 0) return NULL;
151            
152 0           action = alloc_action(doc, PDFMAKE_ACTION_HIDE);
153 0 0         if (!action) return NULL;
154            
155 0           action->data.hide.targets = pdfmake_arena_alloc(doc->arena, target_count * sizeof(char *));
156 0 0         if (!action->data.hide.targets) return NULL;
157            
158 0 0         for (i = 0; i < target_count; i++) {
159 0           action->data.hide.targets[i] = pdfmake_arena_strdup(doc->arena, targets[i]);
160 0 0         if (!action->data.hide.targets[i]) return NULL;
161             }
162            
163 0           action->data.hide.target_count = target_count;
164 0           action->data.hide.hide = hide ? 1 : 0;
165            
166 0           return action;
167             }
168              
169 1           pdfmake_err_t pdfmake_action_chain(pdfmake_action_t *action, pdfmake_action_t *next)
170             {
171 1 50         if (!action || !next) return PDFMAKE_EINVAL;
    50          
172 1           action->next = next;
173 1           return PDFMAKE_OK;
174             }
175              
176             /*============================================================================
177             * Action writing
178             *==========================================================================*/
179              
180 16           pdfmake_obj_t pdfmake_action_to_obj(pdfmake_action_t *action)
181             {
182             pdfmake_arena_t *arena;
183             pdfmake_obj_t dict;
184             uint32_t type_key;
185             uint32_t s_key;
186             uint32_t d_key;
187             pdfmake_obj_t dest;
188             uint32_t f_key;
189             pdfmake_obj_t dest_arr;
190             uint32_t nw_key;
191             uint32_t uri_key;
192             uint32_t map_key;
193             uint32_t n_key;
194             const char *name_str;
195             uint32_t js_key;
196             uint32_t t_key;
197             pdfmake_obj_t arr;
198             size_t i;
199             uint32_t h_key;
200             uint32_t next_key;
201             uint32_t next_num;
202 16 50         if (!action || !action->doc) return pdfmake_null();
    50          
203            
204 16           arena = action->doc->arena;
205 16           dict = pdfmake_dict_new(arena);
206            
207 16           type_key = pdfmake_arena_intern_name(arena, "Type", 4);
208 16           s_key = pdfmake_arena_intern_name(arena, "S", 1);
209            
210 16           pdfmake_dict_set(arena, &dict, type_key, pdfmake_name(arena, "Action", 6));
211            
212 16           switch (action->type) {
213 0           case PDFMAKE_ACTION_GOTO: {
214 0           pdfmake_dict_set(arena, &dict, s_key, pdfmake_name(arena, "GoTo", 4));
215            
216 0           d_key = pdfmake_arena_intern_name(arena, "D", 1);
217 0           dest = pdfmake_dest_to_obj(arena, action->doc, action->data.go_to.dest);
218 0           pdfmake_dict_set(arena, &dict, d_key, dest);
219 0           break;
220             }
221            
222 3           case PDFMAKE_ACTION_GOTOR: {
223 3           pdfmake_dict_set(arena, &dict, s_key, pdfmake_name(arena, "GoToR", 5));
224            
225 3           f_key = pdfmake_arena_intern_name(arena, "F", 1);
226 3           d_key = pdfmake_arena_intern_name(arena, "D", 1);
227            
228 3           pdfmake_dict_set(arena, &dict, f_key,
229 3           pdfmake_str(arena, action->data.go_tor.file, strlen(action->data.go_tor.file)));
230            
231             /* For external destinations, use page number as int */
232 3           dest_arr = pdfmake_array_new(arena);
233 3           pdfmake_array_push(arena, &dest_arr, pdfmake_int((int64_t)action->data.go_tor.dest.page_index));
234 3           pdfmake_array_push(arena, &dest_arr, pdfmake_name(arena, "Fit", 3));
235 3           pdfmake_dict_set(arena, &dict, d_key, dest_arr);
236            
237 3 100         if (action->data.go_tor.new_window) {
238 1           nw_key = pdfmake_arena_intern_name(arena, "NewWindow", 9);
239 1           pdfmake_dict_set(arena, &dict, nw_key, pdfmake_bool(1));
240             }
241 3           break;
242             }
243            
244 7           case PDFMAKE_ACTION_URI: {
245 7           pdfmake_dict_set(arena, &dict, s_key, pdfmake_name(arena, "URI", 3));
246            
247 7           uri_key = pdfmake_arena_intern_name(arena, "URI", 3);
248 7           pdfmake_dict_set(arena, &dict, uri_key,
249 7           pdfmake_str(arena, action->data.uri.uri, strlen(action->data.uri.uri)));
250            
251 7 50         if (action->data.uri.is_map) {
252 0           map_key = pdfmake_arena_intern_name(arena, "IsMap", 5);
253 0           pdfmake_dict_set(arena, &dict, map_key, pdfmake_bool(1));
254             }
255 7           break;
256             }
257            
258 6           case PDFMAKE_ACTION_NAMED: {
259 6           pdfmake_dict_set(arena, &dict, s_key, pdfmake_name(arena, "Named", 5));
260            
261 6           n_key = pdfmake_arena_intern_name(arena, "N", 1);
262 6           name_str = named_action_strings[action->data.named.name];
263 6           pdfmake_dict_set(arena, &dict, n_key,
264             pdfmake_name(arena, name_str, strlen(name_str)));
265 6           break;
266             }
267            
268 0           case PDFMAKE_ACTION_JAVASCRIPT: {
269 0           pdfmake_dict_set(arena, &dict, s_key, pdfmake_name(arena, "JavaScript", 10));
270            
271 0           js_key = pdfmake_arena_intern_name(arena, "JS", 2);
272 0           pdfmake_dict_set(arena, &dict, js_key,
273 0           pdfmake_str(arena, action->data.javascript.script,
274 0           strlen(action->data.javascript.script)));
275 0           break;
276             }
277            
278 0           case PDFMAKE_ACTION_HIDE: {
279 0           pdfmake_dict_set(arena, &dict, s_key, pdfmake_name(arena, "Hide", 4));
280            
281 0           t_key = pdfmake_arena_intern_name(arena, "T", 1);
282            
283 0 0         if (action->data.hide.target_count == 1) {
284             /* Single target as string */
285 0           pdfmake_dict_set(arena, &dict, t_key,
286 0           pdfmake_str(arena, action->data.hide.targets[0],
287 0           strlen(action->data.hide.targets[0])));
288             } else {
289             /* Multiple targets as array */
290 0           arr = pdfmake_array_new(arena);
291 0 0         for (i = 0; i < action->data.hide.target_count; i++) {
292 0           pdfmake_array_push(arena, &arr,
293 0           pdfmake_str(arena, action->data.hide.targets[i],
294 0           strlen(action->data.hide.targets[i])));
295             }
296 0           pdfmake_dict_set(arena, &dict, t_key, arr);
297             }
298            
299 0 0         if (!action->data.hide.hide) {
300             /* H defaults to true (hide), so only set if false (show) */
301 0           h_key = pdfmake_arena_intern_name(arena, "H", 1);
302 0           pdfmake_dict_set(arena, &dict, h_key, pdfmake_bool(0));
303             }
304 0           break;
305             }
306            
307 0           case PDFMAKE_ACTION_LAUNCH: {
308 0           pdfmake_dict_set(arena, &dict, s_key, pdfmake_name(arena, "Launch", 6));
309            
310 0           f_key = pdfmake_arena_intern_name(arena, "F", 1);
311 0           pdfmake_dict_set(arena, &dict, f_key,
312 0           pdfmake_str(arena, action->data.launch.file,
313 0           strlen(action->data.launch.file)));
314            
315 0 0         if (action->data.launch.new_window) {
316 0           nw_key = pdfmake_arena_intern_name(arena, "NewWindow", 9);
317 0           pdfmake_dict_set(arena, &dict, nw_key, pdfmake_bool(1));
318             }
319 0           break;
320             }
321             }
322            
323             /* Chain action */
324 16 50         if (action->next) {
325 0           next_key = pdfmake_arena_intern_name(arena, "Next", 4);
326 0           next_num = pdfmake_action_write(action->next);
327 0 0         if (next_num > 0) {
328 0           pdfmake_dict_set(arena, &dict, next_key, pdfmake_ref(next_num, 0));
329             }
330             }
331            
332 16           return dict;
333             }
334              
335 2           uint32_t pdfmake_action_write(pdfmake_action_t *action)
336             {
337             pdfmake_obj_t dict;
338 2 50         if (!action || !action->doc) return 0;
    50          
339            
340 2 100         if (action->obj_num > 0) {
341 1           return action->obj_num; /* Already written */
342             }
343            
344 1           dict = pdfmake_action_to_obj(action);
345 1 50         if (dict.kind != PDFMAKE_DICT) return 0;
346            
347 1           action->obj_num = pdfmake_doc_add(action->doc, dict);
348 1           return action->obj_num;
349             }
350              
351             /*============================================================================
352             * Extended link annotation with action
353             *==========================================================================*/
354              
355 15           uint32_t pdfmake_annot_link_action(pdfmake_doc_t *doc,
356             pdfmake_rect_t rect,
357             pdfmake_action_t *action,
358             pdfmake_highlight_mode_t highlight)
359             {
360             pdfmake_arena_t *arena;
361             pdfmake_obj_t dict;
362             uint32_t type_key;
363             uint32_t subtype_key;
364             uint32_t rect_key;
365             pdfmake_obj_t rect_arr;
366             uint32_t border_key;
367             pdfmake_obj_t border;
368             uint32_t action_key;
369             pdfmake_obj_t action_obj;
370 15 50         if (!doc || !action) return 0;
    50          
371            
372 15           arena = doc->arena;
373 15           dict = pdfmake_dict_new(arena);
374            
375             /* /Type /Annot */
376 15           type_key = pdfmake_arena_intern_name(arena, "Type", 4);
377 15           pdfmake_dict_set(arena, &dict, type_key, pdfmake_name(arena, "Annot", 5));
378            
379             /* /Subtype /Link */
380 15           subtype_key = pdfmake_arena_intern_name(arena, "Subtype", 7);
381 15           pdfmake_dict_set(arena, &dict, subtype_key, pdfmake_name(arena, "Link", 4));
382            
383             /* /Rect */
384 15           rect_key = pdfmake_arena_intern_name(arena, "Rect", 4);
385 15           rect_arr = pdfmake_array_new(arena);
386 15           pdfmake_array_push(arena, &rect_arr, pdfmake_real(rect.x1));
387 15           pdfmake_array_push(arena, &rect_arr, pdfmake_real(rect.y1));
388 15           pdfmake_array_push(arena, &rect_arr, pdfmake_real(rect.x2));
389 15           pdfmake_array_push(arena, &rect_arr, pdfmake_real(rect.y2));
390 15           pdfmake_dict_set(arena, &dict, rect_key, rect_arr);
391            
392             /* /Border [0 0 0] - no visible border */
393 15           border_key = pdfmake_arena_intern_name(arena, "Border", 6);
394 15           border = pdfmake_array_new(arena);
395 15           pdfmake_array_push(arena, &border, pdfmake_int(0));
396 15           pdfmake_array_push(arena, &border, pdfmake_int(0));
397 15           pdfmake_array_push(arena, &border, pdfmake_int(0));
398 15           pdfmake_dict_set(arena, &dict, border_key, border);
399            
400             /* /A (action) */
401 15           action_key = pdfmake_arena_intern_name(arena, "A", 1);
402 15           action_obj = pdfmake_action_to_obj(action);
403 15           pdfmake_dict_set(arena, &dict, action_key, action_obj);
404            
405             /* /H (highlight mode) - only if not default (Invert) */
406 15 100         if (highlight != PDFMAKE_HIGHLIGHT_INVERT) {
407 3           uint32_t h_key = pdfmake_arena_intern_name(arena, "H", 1);
408 3           const char *h_name = highlight_mode_names[highlight];
409 3           pdfmake_dict_set(arena, &dict, h_key, pdfmake_name(arena, h_name, strlen(h_name)));
410             }
411            
412 15           return pdfmake_doc_add(doc, dict);
413             }
414              
415 6           uint32_t pdfmake_annot_link_named(pdfmake_doc_t *doc,
416             pdfmake_rect_t rect,
417             const char *name,
418             pdfmake_highlight_mode_t highlight)
419             {
420 6           pdfmake_action_t *action = pdfmake_action_named_str(doc, name);
421 6 50         if (!action) return 0;
422            
423 6           return pdfmake_annot_link_action(doc, rect, action, highlight);
424             }