#include #include #include #include #include #include #include #include "studio/geometry.h" #include "studio/pdf.h" #define maximumoffset 10000000000u static_assert(maximumoffset <= SIZE_MAX, ""); const vector a0 = {.x = 841.0 mm, .y = 1189.0 mm}; const vector a1 = {.x = 594.0 mm, .y = 841.0 mm}; const vector a2 = {.x = 420.0 mm, .y = 594.0 mm}; const vector a3 = {.x = 297.0 mm, .y = 420.0 mm}; const vector a4 = {.x = 210.0 mm, .y = 297.0 mm}; const vector a5 = {.x = 148.0 mm, .y = 210.0 mm}; const vector a6 = {.x = 105.0 mm, .y = 148.0 mm}; const vector a7 = {.x = 74.0 mm, .y = 105.0 mm}; const vector a8 = {.x = 52.0 mm, .y = 74.0 mm}; const vector usletter = {.x = 8.5 * 72, .y = 11.0 * 72}; struct pdf offset(struct pdf pdf, const size_t bytes) { assert(pdf.offset <= maximumoffset); assert(maximumoffset - pdf.offset >= (size_t)bytes); pdf.offset += (size_t)bytes; return pdf; } struct pdf generate(const struct pdf pdf, const char * const format, ...) { assert(format); va_list arguments = {0}; va_start(arguments, format); const size_t bytes = (assert(pdf.write), pdf.write(pdf.device, format, arguments)); va_end(arguments); return offset(pdf, bytes); } static struct pdf startobject(struct pdf pdf) { assert(pdf.object.number < sizeof pdf.object.offset / sizeof *pdf.object.offset); pdf.object.offset[pdf.object.number++] = pdf.offset; return generate(pdf, "%lu 0 obj ", pdf.object.number); } static struct pdf startpages(struct pdf pdf) { assert(pdf.page.parent == 0); pdf.page.parent = pdf.object.number; return pdf; } struct pdf startpdf(struct pdf pdf) { assert(pdf.object.number == 0); pdf = startobject(generate(pdf, "%%PDF-1.2\n" "%%%c%c%c%c\n", 128, 128, 128, 128)); return startpages(generate(pdf, "<> endobj\n", ++pdf.object.number)); } struct pdf dictionary(struct pdf pdf, const char * const entries, ...) { assert(entries); va_list arguments = {0}; va_start(arguments, entries); pdf = generate(startobject(pdf), "<<"); pdf = offset(pdf, (assert(pdf.write), pdf.write(pdf.device, entries, arguments))); pdf = generate(pdf, ">> endobj\n"); va_end(arguments); return pdf; } struct pdf startpage(struct pdf pdf, const vector pagesize, const size_t resources) { assert(pdf.page.parent); pdf = generate(startobject(generate(startobject(pdf), "<<" "/Type /Page /Parent %lu 0 R /Resources %lu 0 R /MediaBox [0 0 %.2f %.2f] /Contents %lu 0 R" ">> endobj\n", pdf.page.parent, resources, pagesize.x, pagesize.y, pdf.object.number + 2 )), "<> stream\n", pdf.object.number + 3); assert(pdf.page.number < sizeof pdf.page.object / sizeof *pdf.page.object); pdf.page.object[pdf.page.number++] = pdf.object.number - 1; return pdf; } struct pdf endpage(const struct pdf pdf, const struct pdf stream) { return generate(startobject(generate(pdf, "endstream\n" "endobj\n" )), "%lu endobj\n", ((void)assert(pdf.offset >= stream.offset), pdf.offset - stream.offset)); } static struct pdf endpages(struct pdf pdf) { assert(pdf.page.parent); assert(pdf.page.parent <= sizeof pdf.object.offset / sizeof *pdf.object.offset); pdf.object.offset[pdf.page.parent - 1] = pdf.offset; pdf = generate(pdf, "%lu 0 obj <> endobj\n"); } static struct pdf xref(struct pdf pdf) { assert(pdf.offset < maximumoffset); pdf = generate(pdf, "xref\n" "0 %lu\n" "0000000000 65535 f \n", pdf.object.number + 1 ); assert(pdf.object.number <= sizeof pdf.object.offset / sizeof *pdf.object.offset); for (size_t entry = 0; entry < pdf.object.number; ++entry) pdf = generate(pdf, "%010lu 00000 n \n", pdf.object.offset[entry]); return pdf; } static char *timestamp(char * const string, const size_t size) { assert(string); const char blank[] = "D:00000000000000000'00'"; assert(size >= sizeof blank); const time_t now = time(0); if (now != -1 && strftime(string, size, "D:%Y%m%d%H%M%SZ00'00'", gmtime(&now))) return string; else return memcpy(string, blank, sizeof blank); } struct pdf endpdf(struct pdf pdf) { char string[32] = {0}; pdf = generate(startobject(endpages(pdf)), "<> endobj\n", pdf.title ? pdf.title : "untitled", pdf.author ? pdf.author : "unknown", timestamp(string, sizeof string) ); (void)generate(xref(pdf), "trailer\n" "<>\n" "startxref\n" "%lu\n" "%%%%EOF\n", pdf.object.number + 1, pdf.object.number, pdf.offset ); return (struct pdf){0}; }