diff options
author | yzrh <yzrh@tuta.io> | 2020-08-14 22:04:26 +0000 |
---|---|---|
committer | yzrh <yzrh@tuta.io> | 2020-08-14 22:04:26 +0000 |
commit | 12ecdd71592eccf7bdb6214edbc7318246469c1c (patch) | |
tree | fda27e41c37a2345702ad3e90480154d975e426f /src/pdf_writer.c | |
download | melon-12ecdd71592eccf7bdb6214edbc7318246469c1c.tar.gz melon-12ecdd71592eccf7bdb6214edbc7318246469c1c.tar.zst |
Initial commit.
Diffstat (limited to 'src/pdf_writer.c')
-rw-r--r-- | src/pdf_writer.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/src/pdf_writer.c b/src/pdf_writer.c new file mode 100644 index 0000000..3cf4f7c --- /dev/null +++ b/src/pdf_writer.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2020, yzrh <yzrh@tuta.io> + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <openssl/md5.h> + +#include "pdf.h" + +int +pdf_dump_obj(pdf_object_t **pdf, FILE **fp) +{ + if (*pdf == NULL || *fp == NULL) + return 1; + + long cur; + + pdf_object_t *ptr = (*pdf)->next; + while (ptr != NULL) { + ptr->address = cur = ftell(*fp); + + fprintf(*fp, "%d 0 obj\n", ptr->id); + + if (ptr->dictionary != NULL) + fputs(ptr->dictionary, *fp); + else if (ptr->object != NULL) + fputs(ptr->object, *fp); + else if (ptr->stream == NULL) + fputs("null\n", *fp); + + if (ptr->stream != NULL) { + fputs("stream\r\n", *fp); + fwrite(ptr->stream, ptr->stream_size, 1, *fp); + fputs("endstream\n", *fp); + } + + fputs("endobj\n", *fp); + + ptr->size = ftell(*fp) - cur; + + ptr = ptr->next; + } + + return 0; +} + +int +pdf_dump_header(pdf_object_t **pdf, FILE **fp) +{ + if (*pdf == NULL || *fp == NULL) + return 1; + + fputs("%PDF-1.7\n", *fp); + + const unsigned char bin[4] = { + 0xf6, + 0xe4, + 0xfc, + 0xdf, + }; + + fputs("%", *fp); + fwrite(bin, 4, 1, *fp); + fputs("\n", *fp); + + return 0; +} + +int +pdf_dump_xref(pdf_object_t **pdf, FILE **fp) +{ + if (*pdf == NULL || *fp == NULL) + return 1; + + fputs("xref\n", *fp); + + pdf_object_t *ptr = *pdf; + + pdf_object_t *start = ptr; + int count = 1; + + while (ptr != NULL) { + if (ptr->next == NULL || + (ptr->next != NULL && ptr->next->id != ptr->id + 1)) { + fprintf(*fp, "%d %d\n", start->id, count); + + for (; count > 0; count--) { + fprintf(*fp, "%010d %05d %s\r\n", + start->address, + start->address > 0 ? 0 : 65535, + start->size > 0 ? "n" : "f"); + start = start->next; + } + + if (ptr->next != NULL) + start = ptr->next; + } + + ptr = ptr->next; + count++; + } + + return 0; +} + +int +pdf_dump_trailer(pdf_object_t **pdf, FILE **fp, int xref) +{ + if (*pdf == NULL || *fp == NULL) + return 1; + + fputs("trailer\n", *fp); + + fputs("<<\n", *fp); + + /* + * File identifiers should be generated using + * (a) Current time + * (b) File path + * (c) Size of file + * (d) Values of all entries in the + * file's document information dictionary + * + * It is recommended to be computed according to RFC 1321 + */ + + time_t timestamp = time(NULL); + int size = pdf_get_size(pdf); + + int buf_size; + char buf[64]; + + buf_size = snprintf(buf, 64, "%lx%x", timestamp, size); + + unsigned char str[64]; + memcpy(str, buf, 64); + + unsigned char fid[MD5_DIGEST_LENGTH]; + MD5(str, buf_size, fid); + + pdf_object_t *ptr = *pdf; + while (ptr->next != NULL) + ptr = ptr->next; + + /* + * TODO: Document information dictionary + * `"/Producer (Melon)"' + * `"/CreationDate (D:YYYYMMDDHHmmSS+00'00')"' + * + * Trailer dictionary + * `"/Info %d 0 R"' + */ + fprintf(*fp, + "/Size %d\n/Root %d 0 R\n", + ptr->id + 1, + pdf_get_catalog_id(pdf)); + + fputs("/ID [", *fp); + + for (int i = 0; i < 2; i++) { + fputs("<", *fp); + + for (int j = 0; j < MD5_DIGEST_LENGTH; j++) + fprintf(*fp, "%02x", fid[j]); + + fputs(">", *fp); + + if (i < 1) + fputs(" ", *fp); + } + + fputs("]\n", *fp); + + fputs(">>\n", *fp); + + fputs("startxref\n", *fp); + + fprintf(*fp, "%d\n", xref); + + fputs("%%EOF\n", *fp); + + return 0; +} |