File Coverage

src/pdfmake_page.c
Criterion Covered Total %
statement 225 254 88.5
branch 99 174 56.9
condition n/a
subroutine n/a
pod n/a
total 324 428 75.7


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_page.c — Page and catalog construction.
3             */
4              
5             #include "pdfmake_page.h"
6             #include "pdfmake_ocg.h"
7             #include "pdfmake_attach.h"
8             #include "pdfmake_tag.h"
9             #include "pdfmake_arena.h"
10             #include "pdfmake_outline.h"
11             #include "pdfmake_form.h"
12             #include
13             #include
14             #include
15              
16             /*----------------------------------------------------------------------------
17             * Standard 14 font names (§9.6.2.2)
18             *--------------------------------------------------------------------------*/
19              
20             static const char *std14_names[] = {
21             "Helvetica",
22             "Helvetica-Bold",
23             "Helvetica-Oblique",
24             "Helvetica-BoldOblique",
25             "Times-Roman",
26             "Times-Bold",
27             "Times-Italic",
28             "Times-BoldItalic",
29             "Courier",
30             "Courier-Bold",
31             "Courier-Oblique",
32             "Courier-BoldOblique",
33             "Symbol",
34             "ZapfDingbats"
35             };
36              
37 237           const char *pdfmake_std14_name(pdfmake_std14_font_t font) {
38 237 50         if (font < 0 || font >= PDFMAKE_FONT_COUNT) return NULL;
39 237           return std14_names[font];
40             }
41              
42 469           int pdfmake_std14_lookup(const char *name) {
43             int i;
44 469 50         if (!name) return -1;
45 2857 100         for (i = 0; i < PDFMAKE_FONT_COUNT; i++) {
46 2809 100         if (strcmp(name, std14_names[i]) == 0) return i;
47             }
48 48           return -1;
49             }
50              
51             /*----------------------------------------------------------------------------
52             * Internal: grow pages array
53             *--------------------------------------------------------------------------*/
54              
55 273           static int doc_grow_pages(pdfmake_doc_t *doc) {
56 273 100         size_t new_cap = doc->page_cap == 0 ? 4 : doc->page_cap * 2;
57 273           pdfmake_page_t **new_arr = realloc(doc->pages, new_cap * sizeof(pdfmake_page_t *));
58 273 50         if (!new_arr) return 0;
59 273           doc->pages = new_arr;
60 273           doc->page_cap = new_cap;
61 273           return 1;
62             }
63              
64             /*----------------------------------------------------------------------------
65             * Page creation
66             *--------------------------------------------------------------------------*/
67              
68 370           pdfmake_page_t *pdfmake_doc_add_page(pdfmake_doc_t *doc, double width, double height) {
69             pdfmake_page_t *page;
70 370 50         if (!doc) return NULL;
71 370 50         if (doc->finalized) return NULL; /* Can't add pages after finalize */
72              
73             /* Grow pages array if needed */
74 370 100         if (doc->page_count >= doc->page_cap) {
75 273 50         if (!doc_grow_pages(doc)) return NULL;
76             }
77              
78             /* Allocate page structure from arena */
79 370           page = pdfmake_arena_calloc(doc->arena, sizeof(pdfmake_page_t));
80 370 50         if (!page) return NULL;
81              
82 370           page->doc = doc;
83 370           page->width = width;
84 370           page->height = height;
85 370           page->rotation = 0;
86 370           page->font_count = 0;
87 370           page->image_count = 0;
88 370           page->prop_count = 0;
89 370           page->extgstate_count = 0;
90 370           page->redactions = NULL;
91 370           page->redact_count = 0;
92 370           page->redact_cap = 0;
93 370           page->has_content = 0;
94 370           page->contents_num = 0;
95              
96             /* The /Page dict will be created during finalize when we know the /Pages ref */
97 370           page->page_num = 0;
98              
99             /* Add to document's page list */
100 370           doc->pages[doc->page_count++] = page;
101              
102 370           return page;
103             }
104              
105 116           size_t pdfmake_doc_page_count(pdfmake_doc_t *doc) {
106 116 50         return doc ? doc->page_count : 0;
107             }
108              
109 78           pdfmake_page_t *pdfmake_doc_get_page(pdfmake_doc_t *doc, size_t index) {
110 78 50         if (!doc || index >= doc->page_count) return NULL;
    100          
111 77           return doc->pages[index];
112             }
113              
114             /*----------------------------------------------------------------------------
115             * Page resources - fonts
116             *--------------------------------------------------------------------------*/
117              
118 243           uint32_t pdfmake_page_add_font(pdfmake_page_t *page,
119             const char *name,
120             const char *base_font) {
121             pdfmake_doc_t *doc;
122             pdfmake_arena_t *arena;
123             pdfmake_obj_t font_dict;
124             uint32_t type_key;
125             uint32_t subtype_key;
126             uint32_t basefont_key;
127             uint32_t font_num;
128             pdfmake_font_entry_t *entry;
129              
130 243 50         if (!page || !name || !base_font) return 0;
    50          
    50          
131 243 50         if (page->font_count >= PDFMAKE_MAX_PAGE_FONTS) return 0;
132              
133             /* Verify it's a Standard 14 font */
134 243 50         if (pdfmake_std14_lookup(base_font) < 0) return 0;
135              
136 243           doc = page->doc;
137 243           arena = pdfmake_doc_arena(doc);
138              
139             /* Create /Font dictionary:
140             * << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> */
141 243           font_dict = pdfmake_dict_new(arena);
142 243 50         if (font_dict.kind != PDFMAKE_DICT) return 0;
143              
144 243           type_key = pdfmake_arena_intern_name(arena, "Type", 4);
145 243           subtype_key = pdfmake_arena_intern_name(arena, "Subtype", 7);
146 243           basefont_key = pdfmake_arena_intern_name(arena, "BaseFont", 8);
147              
148 243           pdfmake_dict_set(arena, &font_dict, type_key, pdfmake_name_cstr(arena, "Font"));
149 243           pdfmake_dict_set(arena, &font_dict, subtype_key, pdfmake_name_cstr(arena, "Type1"));
150 243           pdfmake_dict_set(arena, &font_dict, basefont_key, pdfmake_name_cstr(arena, base_font));
151              
152             /* Add font dict as indirect object */
153 243           font_num = pdfmake_doc_add(doc, font_dict);
154 243 50         if (font_num == 0) return 0;
155              
156             /* Record in page's font list */
157 243           entry = &page->fonts[page->font_count++];
158 243           strncpy(entry->name, name, sizeof(entry->name) - 1);
159 243           entry->name[sizeof(entry->name) - 1] = '\0';
160 243           entry->font_num = font_num;
161              
162 243           return font_num;
163             }
164              
165 237           uint32_t pdfmake_page_add_std14_font(pdfmake_page_t *page,
166             const char *name,
167             pdfmake_std14_font_t font) {
168 237           const char *base_font = pdfmake_std14_name(font);
169 237 50         if (!base_font) return 0;
170 237           return pdfmake_page_add_font(page, name, base_font);
171             }
172              
173 3           int pdfmake_page_add_extgstate(pdfmake_page_t *page,
174             const char *name,
175             uint32_t extgstate_obj_num)
176             {
177             pdfmake_extgstate_entry_t *entry;
178              
179 3 50         if (!page || !name || extgstate_obj_num == 0) return -1;
    50          
    50          
180 3 50         if (page->extgstate_count >= PDFMAKE_MAX_PAGE_EXTGSTATES) return -1;
181              
182 3           entry = &page->extgstates[page->extgstate_count++];
183 3           strncpy(entry->name, name, sizeof(entry->name) - 1);
184 3           entry->name[sizeof(entry->name) - 1] = '\0';
185 3           entry->extgstate_num = extgstate_obj_num;
186              
187 3           return (int)(page->extgstate_count - 1);
188             }
189              
190             /*----------------------------------------------------------------------------
191             * Content stream
192             *--------------------------------------------------------------------------*/
193              
194 233           pdfmake_err_t pdfmake_page_set_content(pdfmake_page_t *page,
195             const uint8_t *data,
196             size_t len) {
197             pdfmake_doc_t *doc;
198             pdfmake_arena_t *arena;
199             pdfmake_obj_t stream;
200             uint32_t contents_num;
201              
202 233 50         if (!page || !data) return PDFMAKE_EINVAL;
    50          
203              
204 233           doc = page->doc;
205 233           arena = pdfmake_doc_arena(doc);
206              
207             /* Create a stream object with the content */
208 233           stream = pdfmake_stream_new(arena);
209 233 50         if (stream.kind != PDFMAKE_STREAM) return PDFMAKE_ENOMEM;
210              
211 233 50         if (!pdfmake_stream_set_data(arena, &stream, data, len)) {
212 0           return PDFMAKE_ENOMEM;
213             }
214              
215             /* Add as indirect object */
216 233           contents_num = pdfmake_doc_add(doc, stream);
217 233 50         if (contents_num == 0) return PDFMAKE_ENOMEM;
218              
219 233           page->contents_num = contents_num;
220 233           page->has_content = 1;
221              
222 233           return PDFMAKE_OK;
223             }
224              
225 0           pdfmake_err_t pdfmake_page_set_content_str(pdfmake_page_t *page,
226             const char *content) {
227 0 0         if (!content) return PDFMAKE_EINVAL;
228 0           return pdfmake_page_set_content(page, (const uint8_t *)content, strlen(content));
229             }
230              
231 0           pdfmake_err_t pdfmake_page_append_content(pdfmake_page_t *page,
232             const uint8_t *data,
233             size_t len)
234             {
235             pdfmake_doc_t *doc;
236             pdfmake_arena_t *arena;
237             pdfmake_obj_t *existing;
238             pdfmake_stream_t *stm;
239             const uint8_t *old_bytes;
240             size_t old_len;
241             size_t cap;
242             uint8_t *combined;
243             size_t off;
244              
245 0 0         if (!page || !data) return PDFMAKE_EINVAL;
    0          
246 0 0         if (!page->has_content || page->contents_num == 0) {
    0          
247 0           return pdfmake_page_set_content(page, data, len);
248             }
249              
250 0           doc = page->doc;
251 0           arena = pdfmake_doc_arena(doc);
252              
253             /* Fetch the existing content stream so we can concatenate. Its raw
254             * bytes may be compressed (e.g. /Filter /FlateDecode imported from a
255             * source PDF); we decompress before concatenating so the combined
256             * stream is plain and we don't have to synthesize the filter chain. */
257 0           existing = pdfmake_doc_get(doc, page->contents_num);
258 0 0         if (!existing || existing->kind != PDFMAKE_STREAM) {
    0          
259 0           return pdfmake_page_set_content(page, data, len);
260             }
261              
262             /* Use the stream's raw bytes directly. Pages created via
263             * pdfmake_page_set_content always store plain (unfiltered) content,
264             * and pdfmake_doc_import_page feeds pre-decoded bytes in through the
265             * same path — so the raw pointer is the content we need, regardless
266             * of how the page was built. */
267 0           stm = existing->as.stream;
268 0           old_bytes = stm->raw;
269 0           old_len = stm->raw_len;
270              
271             /* Build `<>\nq\n<>\nQ\n` so overlay starts from a
272             * clean graphics state but the existing content is preserved. */
273 0           cap = old_len + 1 + 2 + 1 + len + 1 + 2 + 1;
274 0           combined = pdfmake_arena_alloc(arena, cap);
275 0 0         if (!combined) return PDFMAKE_ENOMEM;
276              
277 0           off = 0;
278 0           memcpy(combined + off, old_bytes, old_len); off += old_len;
279 0 0         if (old_len == 0 || old_bytes[old_len - 1] != '\n') combined[off++] = '\n';
    0          
280 0           combined[off++] = 'q'; combined[off++] = '\n';
281 0           memcpy(combined + off, data, len); off += len;
282 0 0         if (len == 0 || data[len - 1] != '\n') combined[off++] = '\n';
    0          
283 0           combined[off++] = 'Q'; combined[off++] = '\n';
284              
285 0           return pdfmake_page_set_content(page, combined, off);
286             }
287              
288             /*----------------------------------------------------------------------------
289             * Internal: build Resources dictionary for a page
290             *--------------------------------------------------------------------------*/
291              
292 207           static pdfmake_obj_t build_resources_dict(pdfmake_page_t *page) {
293 207           pdfmake_doc_t *doc = page->doc;
294 207           pdfmake_arena_t *arena = pdfmake_doc_arena(doc);
295             pdfmake_obj_t resources;
296             pdfmake_obj_t font_dict;
297             pdfmake_obj_t xobj_dict;
298             pdfmake_obj_t prop_dict;
299             pdfmake_obj_t gs_dict;
300             size_t i;
301             uint32_t font_key;
302             uint32_t xobj_key;
303             uint32_t prop_key;
304             uint32_t gs_key;
305              
306 207           resources = pdfmake_dict_new(arena);
307 207 50         if (resources.kind != PDFMAKE_DICT) return resources;
308              
309             /* Build /Font subdictionary if page has fonts */
310 207 100         if (page->font_count > 0) {
311 142           font_dict = pdfmake_dict_new(arena);
312 142 50         if (font_dict.kind != PDFMAKE_DICT) return font_dict;
313              
314 368 100         for (i = 0; i < page->font_count; i++) {
315 226           pdfmake_font_entry_t *entry = &page->fonts[i];
316 226           uint32_t key = pdfmake_arena_intern_name(arena, entry->name, strlen(entry->name));
317 226           pdfmake_obj_t ref = pdfmake_ref(entry->font_num, 0);
318 226           pdfmake_dict_set(arena, &font_dict, key, ref);
319             }
320              
321 142           font_key = pdfmake_arena_intern_name(arena, "Font", 4);
322 142           pdfmake_dict_set(arena, &resources, font_key, font_dict);
323             }
324              
325             /* Build /XObject subdictionary if page has images */
326 207 100         if (page->image_count > 0) {
327 12           xobj_dict = pdfmake_dict_new(arena);
328 12 50         if (xobj_dict.kind != PDFMAKE_DICT) return xobj_dict;
329              
330 29 100         for (i = 0; i < page->image_count; i++) {
331 17           pdfmake_image_entry_t *entry = &page->images[i];
332 17           uint32_t key = pdfmake_arena_intern_name(arena, entry->name, strlen(entry->name));
333 17           pdfmake_obj_t ref = pdfmake_ref(entry->image_num, 0);
334 17           pdfmake_dict_set(arena, &xobj_dict, key, ref);
335             }
336              
337 12           xobj_key = pdfmake_arena_intern_name(arena, "XObject", 7);
338 12           pdfmake_dict_set(arena, &resources, xobj_key, xobj_dict);
339             }
340              
341             /* Build /Properties subdictionary if page has OCG references */
342 207 100         if (page->prop_count > 0) {
343 5           prop_dict = pdfmake_dict_new(arena);
344 5 50         if (prop_dict.kind != PDFMAKE_DICT) return prop_dict;
345              
346 13 100         for (i = 0; i < page->prop_count; i++) {
347 8           pdfmake_prop_entry_t *entry = &page->properties[i];
348 8           uint32_t key = pdfmake_arena_intern_name(arena, entry->name, strlen(entry->name));
349 8           pdfmake_obj_t ref = pdfmake_ref(entry->prop_num, 0);
350 8           pdfmake_dict_set(arena, &prop_dict, key, ref);
351             }
352              
353 5           prop_key = pdfmake_arena_intern_name(arena, "Properties", 10);
354 5           pdfmake_dict_set(arena, &resources, prop_key, prop_dict);
355             }
356              
357             /* Build /ExtGState subdictionary if page has graphics states */
358 207 100         if (page->extgstate_count > 0) {
359 1           gs_dict = pdfmake_dict_new(arena);
360 1 50         if (gs_dict.kind != PDFMAKE_DICT) return gs_dict;
361              
362 2 100         for (i = 0; i < page->extgstate_count; i++) {
363 1           pdfmake_extgstate_entry_t *entry = &page->extgstates[i];
364 1           uint32_t key = pdfmake_arena_intern_name(arena, entry->name, strlen(entry->name));
365 1           pdfmake_obj_t ref = pdfmake_ref(entry->extgstate_num, 0);
366 1           pdfmake_dict_set(arena, &gs_dict, key, ref);
367             }
368              
369 1           gs_key = pdfmake_arena_intern_name(arena, "ExtGState", 9);
370 1           pdfmake_dict_set(arena, &resources, gs_key, gs_dict);
371             }
372              
373 207           return resources;
374             }
375              
376             /*----------------------------------------------------------------------------
377             * Document finalization
378             *--------------------------------------------------------------------------*/
379              
380 0           int pdfmake_doc_is_finalized(pdfmake_doc_t *doc) {
381 0 0         return doc ? doc->finalized : 0;
382             }
383              
384 163           pdfmake_err_t pdfmake_doc_finalize(pdfmake_doc_t *doc) {
385             pdfmake_arena_t *arena;
386             uint32_t type_key;
387             uint32_t kids_key;
388             uint32_t count_key;
389             uint32_t parent_key;
390             uint32_t mediabox_key;
391             uint32_t resources_key;
392             uint32_t contents_key;
393             uint32_t pages_key;
394             pdfmake_obj_t pages_dict;
395             pdfmake_obj_t kids;
396             uint32_t pages_num;
397             pdfmake_obj_t pages_ref;
398             size_t i;
399             size_t j;
400             pdfmake_page_t *page;
401             pdfmake_obj_t page_dict;
402             pdfmake_obj_t mediabox;
403             pdfmake_obj_t resources;
404             pdfmake_obj_t contents_ref;
405             uint32_t rotate_key;
406             uint32_t page_num;
407             pdfmake_obj_t *pages_obj;
408             pdfmake_obj_t catalog;
409             pdfmake_outline_item_t *outline;
410             uint32_t outlines_num;
411             uint32_t outlines_key;
412             pdfmake_form_t *form;
413             uint32_t acroform_key;
414             uint32_t catalog_num;
415             pdfmake_err_t ocerr;
416             pdfmake_err_t aterr;
417             pdfmake_err_t tagerr;
418             pdfmake_obj_t *page_obj;
419             pdfmake_obj_t annots_arr;
420             uint32_t annots_key;
421              
422 163 50         if (!doc) return PDFMAKE_EINVAL;
423 163 50         if (doc->finalized) return PDFMAKE_OK; /* Already finalized */
424              
425 163           arena = pdfmake_doc_arena(doc);
426              
427             /* Intern common keys */
428 163           type_key = pdfmake_arena_intern_name(arena, "Type", 4);
429 163           kids_key = pdfmake_arena_intern_name(arena, "Kids", 4);
430 163           count_key = pdfmake_arena_intern_name(arena, "Count", 5);
431 163           parent_key = pdfmake_arena_intern_name(arena, "Parent", 6);
432 163           mediabox_key = pdfmake_arena_intern_name(arena, "MediaBox", 8);
433 163           resources_key = pdfmake_arena_intern_name(arena, "Resources", 9);
434 163           contents_key = pdfmake_arena_intern_name(arena, "Contents", 8);
435 163           pages_key = pdfmake_arena_intern_name(arena, "Pages", 5);
436              
437             /* Create /Pages dictionary first (we need its object number for /Parent) */
438 163           pages_dict = pdfmake_dict_new(arena);
439 163 50         if (pages_dict.kind != PDFMAKE_DICT) return PDFMAKE_ENOMEM;
440              
441 163           pdfmake_dict_set(arena, &pages_dict, type_key, pdfmake_name_cstr(arena, "Pages"));
442              
443             /* Placeholder Kids array - will be filled below */
444 163           kids = pdfmake_array_new(arena);
445 163 50         if (kids.kind != PDFMAKE_ARRAY) return PDFMAKE_ENOMEM;
446              
447             /* Add /Pages as indirect object */
448 163           pages_num = pdfmake_doc_add(doc, pages_dict);
449 163 50         if (pages_num == 0) return PDFMAKE_ENOMEM;
450 163           doc->pages_num = pages_num;
451              
452 163           pages_ref = pdfmake_ref(pages_num, 0);
453              
454             /* Create /Page dicts for each page */
455 404 100         for (i = 0; i < doc->page_count; i++) {
456 241           page = doc->pages[i];
457              
458 241           page_dict = pdfmake_dict_new(arena);
459 241 50         if (page_dict.kind != PDFMAKE_DICT) return PDFMAKE_ENOMEM;
460              
461             /* /Type /Page */
462 241           pdfmake_dict_set(arena, &page_dict, type_key, pdfmake_name_cstr(arena, "Page"));
463              
464             /* /Parent -> /Pages ref */
465 241           pdfmake_dict_set(arena, &page_dict, parent_key, pages_ref);
466              
467             /* /MediaBox [0 0 width height] */
468 241           mediabox = pdfmake_array_new(arena);
469 241           pdfmake_array_push(arena, &mediabox, pdfmake_int(0));
470 241           pdfmake_array_push(arena, &mediabox, pdfmake_int(0));
471 241           pdfmake_array_push(arena, &mediabox, pdfmake_real(page->width));
472 241           pdfmake_array_push(arena, &mediabox, pdfmake_real(page->height));
473 241           pdfmake_dict_set(arena, &page_dict, mediabox_key, mediabox);
474              
475             /* /Resources — imported pages carry a verbatim dict; otherwise
476             * compose one from per-page resource arrays. */
477 241 100         if (page->imported_resources) {
478 34           resources.kind = PDFMAKE_DICT;
479 34           resources.as.dict = page->imported_resources;
480             } else {
481 207           resources = build_resources_dict(page);
482             }
483 241           pdfmake_dict_set(arena, &page_dict, resources_key, resources);
484              
485             /* /Contents (if set) */
486 241 100         if (page->has_content) {
487 214           contents_ref = pdfmake_ref(page->contents_num, 0);
488 214           pdfmake_dict_set(arena, &page_dict, contents_key, contents_ref);
489             }
490              
491             /* /Rotate (if non-zero) */
492 241 100         if (page->rotation != 0) {
493 1           rotate_key = pdfmake_arena_intern_name(arena, "Rotate", 6);
494 1           pdfmake_dict_set(arena, &page_dict, rotate_key,
495 1           pdfmake_int(page->rotation));
496             }
497              
498             /* Add /Page as indirect object */
499 241           page_num = pdfmake_doc_add(doc, page_dict);
500 241 50         if (page_num == 0) return PDFMAKE_ENOMEM;
501 241           page->page_num = page_num;
502              
503             /* Add to Kids array */
504 241           pdfmake_array_push(arena, &kids, pdfmake_ref(page_num, 0));
505             }
506              
507             /* Update /Pages dict with Kids and Count */
508 163           pages_obj = pdfmake_doc_get(doc, pages_num);
509 163 50         if (!pages_obj || pages_obj->kind != PDFMAKE_DICT) return PDFMAKE_EINVAL;
    50          
510              
511 163           pdfmake_dict_set(arena, pages_obj, kids_key, kids);
512 163           pdfmake_dict_set(arena, pages_obj, count_key, pdfmake_int((int64_t)doc->page_count));
513              
514             /* Create /Catalog dictionary */
515 163           catalog = pdfmake_dict_new(arena);
516 163 50         if (catalog.kind != PDFMAKE_DICT) return PDFMAKE_ENOMEM;
517              
518 163           pdfmake_dict_set(arena, &catalog, type_key, pdfmake_name_cstr(arena, "Catalog"));
519 163           pdfmake_dict_set(arena, &catalog, pages_key, pages_ref);
520              
521             /* Add outline if present */
522 163           outline = pdfmake_doc_get_outline(doc);
523 163 100         if (outline) {
524 5           outlines_num = pdfmake_outline_finalize(doc, outline);
525 5 50         if (outlines_num > 0) {
526 5           outlines_key = pdfmake_arena_intern_name(arena, "Outlines", 8);
527 5           pdfmake_dict_set(arena, &catalog, outlines_key, pdfmake_ref(outlines_num, 0));
528             }
529             }
530              
531             /* Add AcroForm if present */
532 163           form = pdfmake_doc_get_form(doc);
533 163 100         if (form && form->form_num > 0) {
    100          
534 11           acroform_key = pdfmake_arena_intern_name(arena, "AcroForm", 8);
535 11           pdfmake_dict_set(arena, &catalog, acroform_key, pdfmake_ref(form->form_num, 0));
536             }
537              
538             /* Add /Catalog as indirect object */
539 163           catalog_num = pdfmake_doc_add(doc, catalog);
540 163 50         if (catalog_num == 0) return PDFMAKE_ENOMEM;
541              
542             /* Set as document root */
543 163           pdfmake_doc_set_root(doc, catalog_num, 0);
544              
545             /* Add /OCProperties if document has layers */
546 163 100         if (doc->ocg_count > 0) {
547 5           ocerr = pdfmake_doc_write_ocproperties(doc);
548 5 50         if (ocerr != PDFMAKE_OK) return ocerr;
549             }
550              
551             /* Add /Names/EmbeddedFiles if document has attachments */
552 163 100         if (doc->attach_count > 0) {
553 3           aterr = pdfmake_doc_write_attachments(doc);
554 3 50         if (aterr != PDFMAKE_OK) return aterr;
555             }
556              
557             /* Add structure tree if tagged */
558             {
559 163           tagerr = pdfmake_doc_write_struct_tree(doc);
560 163 50         if (tagerr != PDFMAKE_OK) return tagerr;
561             }
562              
563             /* Add /Annots arrays to pages that have annotations */
564 404 100         for (i = 0; i < doc->page_count; i++) {
565 241           page = doc->pages[i];
566 241 100         if (page->annot_count > 0 && page->page_num > 0) {
    50          
567 26           page_obj = pdfmake_doc_get(doc, page->page_num);
568 26 50         if (page_obj && page_obj->kind == PDFMAKE_DICT) {
    50          
569 26           annots_arr = pdfmake_array_new(arena);
570 96 100         for (j = 0; j < page->annot_count; j++) {
571 70           pdfmake_array_push(arena, &annots_arr,
572 70           pdfmake_ref(page->annots[j], 0));
573             }
574 26           annots_key = pdfmake_arena_intern_name(arena, "Annots", 6);
575 26           pdfmake_dict_set(arena, page_obj, annots_key, annots_arr);
576             }
577             }
578             }
579              
580 163           doc->finalized = 1;
581 163           return PDFMAKE_OK;
582             }