File Coverage

src/pdfmake_doc.c
Criterion Covered Total %
statement 248 272 91.1
branch 102 172 59.3
condition n/a
subroutine n/a
pod n/a
total 350 444 78.8


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_doc.c — PDF document structure and file emission.
3             */
4              
5             #include "pdfmake_doc.h"
6             #include "pdfmake_meta.h"
7             #include "pdfmake_page.h"
8             #include "pdfmake_writer.h"
9             #include "pdfmake_attach.h"
10             #include
11             #include
12             #include
13             #include
14              
15             /*----------------------------------------------------------------------------
16             * FNV-1a hash for ID generation
17             *--------------------------------------------------------------------------*/
18              
19             #define FNV_OFFSET_BASIS 0xcbf29ce484222325ULL
20             #define FNV_PRIME 0x100000001b3ULL
21              
22 350           static uint64_t fnv1a_64(const void *data, size_t len) {
23 350           uint64_t hash = FNV_OFFSET_BASIS;
24 350           const uint8_t *p = (const uint8_t *)data;
25             size_t i;
26 11550 100         for (i = 0; i < len; i++) {
27 11200           hash ^= p[i];
28 11200           hash *= FNV_PRIME;
29             }
30 350           return hash;
31             }
32              
33             /*----------------------------------------------------------------------------
34             * Lifecycle
35             *--------------------------------------------------------------------------*/
36              
37 457           pdfmake_doc_t *pdfmake_doc_new(void) {
38 457           pdfmake_doc_t *doc = calloc(1, sizeof(pdfmake_doc_t));
39 457 50         if (!doc) return NULL;
40              
41 457           doc->arena = pdfmake_arena_new();
42 457 50         if (!doc->arena) {
43 0           free(doc);
44 0           return NULL;
45             }
46              
47 457           doc->objects = calloc(PDFMAKE_DOC_INIT_CAP, sizeof(pdfmake_indirect_t));
48 457 50         if (!doc->objects) {
49 0           pdfmake_arena_free(doc->arena);
50 0           free(doc);
51 0           return NULL;
52             }
53 457           doc->obj_cap = PDFMAKE_DOC_INIT_CAP;
54 457           doc->obj_count = 0;
55              
56 457           doc->pages = NULL;
57 457           doc->page_count = 0;
58 457           doc->page_cap = 0;
59 457           doc->pages_num = 0;
60 457           doc->finalized = 0;
61              
62 457           return doc;
63             }
64              
65 347           void pdfmake_doc_free(pdfmake_doc_t *doc) {
66             size_t i;
67 347 50         if (!doc) return;
68             /* Free page annotation arrays (malloc'd, not arena) before arena is freed */
69 717 100         for (i = 0; i < doc->page_count; i++) {
70 370 50         if (doc->pages[i] && doc->pages[i]->annots) {
    100          
71 26           free(doc->pages[i]->annots);
72             }
73             }
74 347           pdfmake_arena_free(doc->arena);
75 347           free(doc->objects);
76 347           free(doc->pages); /* Page structs are in arena, just free the pointer array */
77 347           free(doc->ocgs); /* OCG pointers in arena, just free the pointer array */
78             /* Free individual attachments (data + struct) */
79 347 100         if (doc->attachments) {
80 31 100         for (i = 0; i < doc->attach_count; i++) {
81 25           pdfmake_attachment_t *att = doc->attachments[i];
82 25 50         if (att) {
83 25           free(att->data);
84 25           free(att);
85             }
86             }
87 6           free(doc->attachments);
88             }
89 347           free(doc);
90             }
91              
92 2312           pdfmake_arena_t *pdfmake_doc_arena(pdfmake_doc_t *doc) {
93 2312 50         return doc ? doc->arena : NULL;
94             }
95              
96             /*----------------------------------------------------------------------------
97             * Indirect object management
98             *--------------------------------------------------------------------------*/
99              
100 0           static int doc_grow_objects(pdfmake_doc_t *doc) {
101 0           size_t new_cap = doc->obj_cap * 2;
102 0           pdfmake_indirect_t *new_arr = realloc(doc->objects,
103             new_cap * sizeof(pdfmake_indirect_t));
104 0 0         if (!new_arr) return 0;
105              
106             /* Zero out new entries */
107 0           memset(new_arr + doc->obj_cap, 0,
108 0           (new_cap - doc->obj_cap) * sizeof(pdfmake_indirect_t));
109              
110 0           doc->objects = new_arr;
111 0           doc->obj_cap = new_cap;
112 0           return 1;
113             }
114              
115 1707           uint32_t pdfmake_doc_add(pdfmake_doc_t *doc, pdfmake_obj_t obj) {
116             uint32_t num;
117             size_t idx;
118 1707 50         if (!doc) return 0;
119              
120             /* Grow if needed */
121 1707 50         if (doc->obj_count >= doc->obj_cap) {
122 0 0         if (!doc_grow_objects(doc)) return 0;
123             }
124              
125             /* Object numbers are 1-based; index 0 corresponds to object 1 */
126 1707           num = (uint32_t)(doc->obj_count + 1);
127 1707           idx = doc->obj_count;
128              
129 1707           doc->objects[idx].num = num;
130 1707           doc->objects[idx].gen = 0;
131 1707           doc->objects[idx].byte_offset = 0;
132 1707           doc->objects[idx].obj = obj;
133 1707           doc->objects[idx].in_use = 1;
134 1707           doc->obj_count++;
135              
136 1707           return num;
137             }
138              
139 12763           pdfmake_obj_t *pdfmake_doc_get(pdfmake_doc_t *doc, uint32_t num) {
140             size_t idx;
141 12763 50         if (!doc || num == 0 || num > doc->obj_count) return NULL;
    100          
    100          
142 12751           idx = num - 1;
143 12751 50         if (!doc->objects[idx].in_use) return NULL;
144 12751           return &doc->objects[idx].obj;
145             }
146              
147 0           pdfmake_obj_t pdfmake_doc_ref(pdfmake_doc_t *doc, uint32_t num) {
148 0           uint16_t gen = 0;
149 0 0         if (doc && num > 0 && num <= doc->obj_count) {
    0          
    0          
150 0           gen = doc->objects[num - 1].gen;
151             }
152 0           return pdfmake_ref(num, gen);
153             }
154              
155             /*----------------------------------------------------------------------------
156             * Trailer setup
157             *--------------------------------------------------------------------------*/
158              
159 387           void pdfmake_doc_set_root(pdfmake_doc_t *doc, uint32_t num, uint16_t gen) {
160 387 50         if (!doc) return;
161 387           doc->root_num = num;
162 387           doc->root_gen = gen;
163             }
164              
165 348           void pdfmake_doc_set_info(pdfmake_doc_t *doc, uint32_t num, uint16_t gen) {
166 348 50         if (!doc) return;
167 348           doc->info_num = num;
168 348           doc->info_gen = gen;
169             }
170              
171 175           void pdfmake_doc_generate_id(pdfmake_doc_t *doc) {
172             /*
173             * Generate ID using FNV-1a hash of:
174             * - Current time
175             * - Document pointer (randomness)
176             * - Object count
177             */
178             struct {
179             time_t t;
180             void *ptr;
181             size_t count;
182             uint64_t counter;
183             } seed;
184             static uint64_t counter = 0;
185             uint64_t h1;
186             uint64_t h2;
187              
188 175 50         if (!doc) return;
189              
190 175           seed.t = time(NULL);
191 175           seed.ptr = doc;
192 175           seed.count = doc->obj_count;
193 175           seed.counter = ++counter;
194              
195 175           h1 = fnv1a_64(&seed, sizeof(seed));
196 175           seed.counter = ++counter;
197 175           h2 = fnv1a_64(&seed, sizeof(seed));
198              
199             /* Pack two 64-bit hashes into two 16-byte IDs */
200 175           memcpy(doc->id1, &h1, 8);
201 175           memcpy(doc->id1 + 8, &h2, 8);
202              
203             /* For initial creation, id2 == id1 */
204 175           memcpy(doc->id2, doc->id1, 16);
205              
206 175           doc->id_set = 1;
207             }
208              
209             /*----------------------------------------------------------------------------
210             * Encryption request and /Encrypt dict construction
211             *--------------------------------------------------------------------------*/
212              
213 5           pdfmake_err_t pdfmake_doc_set_encryption(pdfmake_doc_t *doc,
214             pdfmake_crypt_algo_t algorithm,
215             const char *user_passwd,
216             const char *owner_passwd,
217             int32_t permissions)
218             {
219             const char *u;
220             const char *o;
221              
222 5 50         if (!doc) return PDFMAKE_EINVAL;
223 5 50         if (algorithm < PDFMAKE_CRYPT_RC4_40 || algorithm > PDFMAKE_CRYPT_AES_256) {
    50          
224 0           return PDFMAKE_EINVAL;
225             }
226              
227 5 50         u = user_passwd ? user_passwd : "";
228 5 50         o = owner_passwd ? owner_passwd : u;
229              
230 5           doc->enc_algo = algorithm;
231 5           doc->enc_user_pw = pdfmake_arena_strdup(doc->arena, u);
232 5           doc->enc_owner_pw = pdfmake_arena_strdup(doc->arena, o);
233 5           doc->enc_permissions = permissions;
234 5           doc->enc_requested = 1;
235 5           doc->finalized = 0;
236              
237 5           return PDFMAKE_OK;
238             }
239              
240             /* Build the /Encrypt dict and add it as an indirect object. Assumes the
241             * doc has a generated /ID and that doc->encryption has been set up. */
242 5           static pdfmake_err_t build_encrypt_dict(pdfmake_doc_t *doc) {
243 5           pdfmake_arena_t *a = doc->arena;
244 5           pdfmake_crypt_ctx_t *ctx = doc->encryption;
245             pdfmake_obj_t d;
246             uint32_t k;
247             int ou_len;
248             pdfmake_obj_t std_cf;
249             uint32_t cfm_k;
250             uint32_t len_k;
251             uint32_t auth_k;
252             uint32_t type_k;
253             pdfmake_obj_t cf;
254             uint32_t stdcf_k;
255             uint32_t num;
256 5 50         if (!ctx) return PDFMAKE_EINVAL;
257              
258 5           d = pdfmake_dict_new(a);
259 5 50         if (d.kind != PDFMAKE_DICT) return PDFMAKE_ENOMEM;
260              
261 5           k = pdfmake_arena_intern_name(a, "Filter", 6);
262 5           pdfmake_dict_set(a, &d, k, pdfmake_name_cstr(a, "Standard"));
263              
264 5           k = pdfmake_arena_intern_name(a, "V", 1);
265 5           pdfmake_dict_set(a, &d, k, pdfmake_int(ctx->V));
266              
267 5           k = pdfmake_arena_intern_name(a, "R", 1);
268 5           pdfmake_dict_set(a, &d, k, pdfmake_int(ctx->R));
269              
270 5           k = pdfmake_arena_intern_name(a, "Length", 6);
271 5           pdfmake_dict_set(a, &d, k, pdfmake_int(ctx->key_length * 8));
272              
273 5           k = pdfmake_arena_intern_name(a, "P", 1);
274 5           pdfmake_dict_set(a, &d, k, pdfmake_int((int64_t)(int32_t)ctx->P));
275              
276             /* O and U keys as hex strings — binary bytes survive unchanged.
277             * (R2-R4: 32 bytes, R6: 48 bytes including salts.) */
278 5 100         ou_len = (ctx->R == 6) ? 48 : 32;
279 5           k = pdfmake_arena_intern_name(a, "O", 1);
280 5           pdfmake_dict_set(a, &d, k, pdfmake_hexstr(a, ctx->O, ou_len));
281 5           k = pdfmake_arena_intern_name(a, "U", 1);
282 5           pdfmake_dict_set(a, &d, k, pdfmake_hexstr(a, ctx->U, ou_len));
283              
284 5 100         if (ctx->R == 6) {
285             /* R6 extras: OE, UE, Perms (each 32/32/16 bytes) */
286 3           k = pdfmake_arena_intern_name(a, "OE", 2);
287 3           pdfmake_dict_set(a, &d, k, pdfmake_hexstr(a, ctx->OE, 32));
288 3           k = pdfmake_arena_intern_name(a, "UE", 2);
289 3           pdfmake_dict_set(a, &d, k, pdfmake_hexstr(a, ctx->UE, 32));
290 3           k = pdfmake_arena_intern_name(a, "Perms", 5);
291 3           pdfmake_dict_set(a, &d, k, pdfmake_hexstr(a, ctx->Perms, 16));
292             }
293              
294             /* V=4/5 need /CF + /StmF + /StrF */
295 5 100         if (ctx->V >= 4) {
296 4           std_cf = pdfmake_dict_new(a);
297 4           cfm_k = pdfmake_arena_intern_name(a, "CFM", 3);
298 4           len_k = pdfmake_arena_intern_name(a, "Length", 6);
299 4           auth_k = pdfmake_arena_intern_name(a, "AuthEvent", 9);
300 4           type_k = pdfmake_arena_intern_name(a, "Type", 4);
301 4           pdfmake_dict_set(a, &std_cf, type_k, pdfmake_name_cstr(a, "CryptFilter"));
302 4           pdfmake_dict_set(a, &std_cf, cfm_k,
303 4 100         pdfmake_name_cstr(a, ctx->V == 5 ? "AESV3" : "AESV2"));
304 4           pdfmake_dict_set(a, &std_cf, auth_k, pdfmake_name_cstr(a, "DocOpen"));
305 4           pdfmake_dict_set(a, &std_cf, len_k, pdfmake_int(ctx->key_length));
306              
307 4           cf = pdfmake_dict_new(a);
308 4           stdcf_k = pdfmake_arena_intern_name(a, "StdCF", 5);
309 4           pdfmake_dict_set(a, &cf, stdcf_k, std_cf);
310              
311 4           k = pdfmake_arena_intern_name(a, "CF", 2);
312 4           pdfmake_dict_set(a, &d, k, cf);
313 4           k = pdfmake_arena_intern_name(a, "StmF", 4);
314 4           pdfmake_dict_set(a, &d, k, pdfmake_name_cstr(a, "StdCF"));
315 4           k = pdfmake_arena_intern_name(a, "StrF", 4);
316 4           pdfmake_dict_set(a, &d, k, pdfmake_name_cstr(a, "StdCF"));
317             }
318              
319 5           num = pdfmake_doc_add(doc, d);
320 5 50         if (num == 0) return PDFMAKE_ENOMEM;
321 5           doc->encrypt_num = num;
322 5           return PDFMAKE_OK;
323             }
324              
325             /* Prepare encryption for write: generate ID if needed, allocate+setup the
326             * crypt ctx, add the /Encrypt indirect dict. */
327 174           pdfmake_err_t pdfmake_doc_prepare_encryption(pdfmake_doc_t *doc) {
328 174 50         if (!doc || !doc->enc_requested || doc->encrypt_num != 0) {
    100          
    50          
329 169           return PDFMAKE_OK;
330             }
331 5 50         if (!doc->id_set) pdfmake_doc_generate_id(doc);
332              
333 5           doc->encryption = pdfmake_arena_calloc(doc->arena,
334             sizeof(pdfmake_crypt_ctx_t));
335 5 50         if (!doc->encryption) return PDFMAKE_ENOMEM;
336              
337 5 50         if (pdfmake_crypt_setup(doc->encryption, doc->enc_algo,
338 5           doc->enc_user_pw, doc->enc_owner_pw,
339             doc->enc_permissions,
340 5           doc->id1, 16) != 0) {
341 0           return PDFMAKE_EINVAL;
342             }
343              
344 5           return build_encrypt_dict(doc);
345             }
346              
347             /*----------------------------------------------------------------------------
348             * File emission - Header
349             *--------------------------------------------------------------------------*/
350              
351 174           static pdfmake_err_t emit_header(pdfmake_buf_t *out) {
352             /*
353             * §7.5.2: %PDF-x.y followed by a binary comment.
354             * The binary comment ensures file transfer programs treat the file
355             * as binary. It must contain at least 4 bytes, each >= 128.
356             */
357 174           return pdfmake_buf_append_cstr(out,
358             "%PDF-" PDFMAKE_PDF_VERSION "\n"
359             "%\xE2\xE3\xCF\xD3\n");
360             }
361              
362             /*----------------------------------------------------------------------------
363             * File emission - Body
364             *--------------------------------------------------------------------------*/
365              
366 174           static pdfmake_err_t emit_body(pdfmake_doc_t *doc, pdfmake_buf_t *out) {
367             pdfmake_err_t err;
368 174           const pdfmake_crypt_ctx_t *crypt =
369 174 100         (doc->enc_requested && doc->encryption) ? doc->encryption : NULL;
    50          
370             size_t i;
371             pdfmake_indirect_t *ind;
372              
373 1796 100         for (i = 0; i < doc->obj_count; i++) {
374 1622           ind = &doc->objects[i];
375 1622 50         if (!ind->in_use) continue;
376              
377             /* Record byte offset of this object */
378 1622           ind->byte_offset = out->len;
379              
380             /* Emit: N G obj\n */
381 1622           err = pdfmake_buf_appendf(out, "%u %u obj\n",
382 1622           (unsigned)ind->num, (unsigned)ind->gen);
383 1622 50         if (err != PDFMAKE_OK) return err;
384              
385             /* Emit the object value. The /Encrypt dict itself must remain
386             * plaintext (its strings are special-cased by the spec). */
387 1622 100         if (crypt) {
388 39           int skip = (ind->num == doc->encrypt_num);
389 39           err = pdfmake_write_obj_encrypted(out, doc->arena, crypt,
390 39           ind->num, skip, &ind->obj);
391             } else {
392 1583           err = pdfmake_write_obj(out, doc->arena, &ind->obj);
393             }
394 1622 50         if (err != PDFMAKE_OK) return err;
395              
396             /* Emit: \nendobj\n */
397 1622           err = pdfmake_buf_append_cstr(out, "\nendobj\n");
398 1622 50         if (err != PDFMAKE_OK) return err;
399             }
400              
401 174           return PDFMAKE_OK;
402             }
403              
404             /*----------------------------------------------------------------------------
405             * File emission - Cross-reference table
406             *--------------------------------------------------------------------------*/
407              
408 174           static pdfmake_err_t emit_xref(pdfmake_doc_t *doc, pdfmake_buf_t *out) {
409             pdfmake_err_t err;
410             size_t n_entries;
411             size_t i;
412             pdfmake_indirect_t *ind;
413             char entry[21];
414              
415             /* Record xref offset for startxref */
416 174           doc->xref_offset = out->len;
417              
418             /*
419             * Classic xref table format (§7.5.4):
420             * xref
421             * 0 N
422             * 0000000000 65535 f
423             * nnnnnnnnnn ggggg n
424             * ...
425             *
426             * Each entry is exactly 20 bytes: 10-digit offset, space, 5-digit gen,
427             * space, 'f' or 'n', space, newline (we use space + \n = 2 chars).
428             * Actually the spec says "space or EOL" after the letter, and requires
429             * exactly 20 bytes including the line terminator. We emit:
430             * "OOOOOOOOOO GGGGG X \n" (space before \n for Windows compat).
431             */
432              
433             /* Number of entries = obj_count + 1 (for entry 0) */
434 174           n_entries = doc->obj_count + 1;
435              
436 174           err = pdfmake_buf_appendf(out, "xref\n0 %zu\n", n_entries);
437 174 50         if (err != PDFMAKE_OK) return err;
438              
439             /* Entry 0: head of free list, gen 65535 */
440 174           err = pdfmake_buf_append_cstr(out, "0000000000 65535 f \n");
441 174 50         if (err != PDFMAKE_OK) return err;
442              
443             /* Entries for each object */
444 1796 100         for (i = 0; i < doc->obj_count; i++) {
445 1622           ind = &doc->objects[i];
446              
447 1622 50         if (ind->in_use) {
448 1622           snprintf(entry, sizeof(entry), "%010lu %05u n \n",
449 1622           (unsigned long)ind->byte_offset,
450 1622           (unsigned)ind->gen);
451             } else {
452             /* Free entry: offset points to next free (0 = end of list) */
453 0           snprintf(entry, sizeof(entry), "%010u %05u f \n",
454 0           0, (unsigned)(ind->gen + 1));
455             }
456              
457 1622           err = pdfmake_buf_append(out, entry, 20);
458 1622 50         if (err != PDFMAKE_OK) return err;
459             }
460              
461 174           return PDFMAKE_OK;
462             }
463              
464             /*----------------------------------------------------------------------------
465             * File emission - Trailer
466             *--------------------------------------------------------------------------*/
467              
468 174           static pdfmake_err_t emit_trailer(pdfmake_doc_t *doc, pdfmake_buf_t *out) {
469             pdfmake_err_t err;
470             int i;
471              
472             /* Generate ID if not already done */
473 174 100         if (!doc->id_set) {
474 165           pdfmake_doc_generate_id(doc);
475             }
476              
477 174           err = pdfmake_buf_append_cstr(out, "trailer\n<<");
478 174 50         if (err != PDFMAKE_OK) return err;
479              
480             /* /Size: total number of entries in xref (obj_count + 1) */
481 174           err = pdfmake_buf_appendf(out, "/Size %zu", doc->obj_count + 1);
482 174 50         if (err != PDFMAKE_OK) return err;
483              
484             /* /Root: required reference to document catalog */
485 174 50         if (doc->root_num > 0) {
486 174           err = pdfmake_buf_appendf(out, "/Root %u %u R",
487 174           (unsigned)doc->root_num,
488 174           (unsigned)doc->root_gen);
489 174 50         if (err != PDFMAKE_OK) return err;
490             }
491              
492             /* /Info: optional reference to info dictionary */
493 174 50         if (doc->info_num > 0) {
494 174           err = pdfmake_buf_appendf(out, "/Info %u %u R",
495 174           (unsigned)doc->info_num,
496 174           (unsigned)doc->info_gen);
497 174 50         if (err != PDFMAKE_OK) return err;
498             }
499              
500             /* /Encrypt: reference to the Standard security handler dict */
501 174 100         if (doc->encrypt_num > 0) {
502 5           err = pdfmake_buf_appendf(out, "/Encrypt %u 0 R",
503 5           (unsigned)doc->encrypt_num);
504 5 50         if (err != PDFMAKE_OK) return err;
505             }
506              
507             /* /ID: array of two byte strings */
508 174           err = pdfmake_buf_append_cstr(out, "/ID[<");
509 174 50         if (err != PDFMAKE_OK) return err;
510              
511             /* Emit id1 as hex */
512 2958 100         for (i = 0; i < 16; i++) {
513 2784           err = pdfmake_buf_appendf(out, "%02X", doc->id1[i]);
514 2784 50         if (err != PDFMAKE_OK) return err;
515             }
516              
517 174           err = pdfmake_buf_append_cstr(out, "><");
518 174 50         if (err != PDFMAKE_OK) return err;
519              
520             /* Emit id2 as hex */
521 2958 100         for (i = 0; i < 16; i++) {
522 2784           err = pdfmake_buf_appendf(out, "%02X", doc->id2[i]);
523 2784 50         if (err != PDFMAKE_OK) return err;
524             }
525              
526 174           err = pdfmake_buf_append_cstr(out, ">]");
527 174 50         if (err != PDFMAKE_OK) return err;
528              
529             /* Close trailer dict */
530 174           err = pdfmake_buf_append_cstr(out, ">>\n");
531 174 50         if (err != PDFMAKE_OK) return err;
532              
533             /* startxref */
534 174           err = pdfmake_buf_appendf(out, "startxref\n%lu\n",
535 174           (unsigned long)doc->xref_offset);
536 174 50         if (err != PDFMAKE_OK) return err;
537              
538             /* %%EOF */
539 174           err = pdfmake_buf_append_cstr(out, "%%EOF\n");
540 174 50         if (err != PDFMAKE_OK) return err;
541              
542 174           return PDFMAKE_OK;
543             }
544              
545             /*----------------------------------------------------------------------------
546             * File emission - Main entry point
547             *--------------------------------------------------------------------------*/
548              
549 174           pdfmake_err_t pdfmake_doc_write(pdfmake_doc_t *doc, pdfmake_buf_t *out) {
550             pdfmake_err_t err;
551 174 50         if (!doc || !out) return PDFMAKE_EINVAL;
    50          
552              
553             /* 0a. Finalize document structure if not already done */
554 174 100         if (doc->page_count > 0 && !doc->finalized) {
    50          
555 163           err = pdfmake_doc_finalize(doc);
556 163 50         if (err != PDFMAKE_OK) return err;
557             }
558              
559             /* 0b. Auto-fill metadata (Producer, CreationDate, ModDate) */
560 174           pdfmake_meta_auto_fill(doc);
561              
562             /* 0c. Materialize encryption ctx + /Encrypt dict (no-op if no
563             * encryption was requested). Must happen after all other indirects
564             * are in place so obj numbers for real content don't collide with
565             * the encrypt dict's number. */
566 174           err = pdfmake_doc_prepare_encryption(doc);
567 174 50         if (err != PDFMAKE_OK) return err;
568              
569             /* 1. Header */
570 174           err = emit_header(out);
571 174 50         if (err != PDFMAKE_OK) return err;
572              
573             /* 2. Body (indirect objects) */
574 174           err = emit_body(doc, out);
575 174 50         if (err != PDFMAKE_OK) return err;
576              
577             /* 3. Cross-reference table */
578 174           err = emit_xref(doc, out);
579 174 50         if (err != PDFMAKE_OK) return err;
580              
581             /* 4. Trailer */
582 174           err = emit_trailer(doc, out);
583 174 50         if (err != PDFMAKE_OK) return err;
584              
585 174           return PDFMAKE_OK;
586             }