/* * pdf.c - Generate PDF * * Copyright 2012, 2013 by Werner Almesberger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #define _GNU_SOURCE /* for strcasecmp */ #include #include #include #include "util.h" #include "run.h" #include "genkicat.h" #include "tree.h" #include "libs.h" #include "pdf.h" static struct format { const char *file_setup; const char *overlay_setup; int left; struct text { const char *font; int size; int y; } name, path, lib, comment, index; int comment_line_skip; int index_line_skip; int index_column_skip; int index_lines; int index_columns; } landscape = { .file_setup = "%%Orientation: Landscape", .overlay_setup = "90 rotate", .left = 20, .name = { "Helvetica-Bold", 24, 57 }, .path = { "Helvetica-Bold", 18, 30 }, .lib = { "Courier", 12, 75 }, .comment = { "Helvetica", 12, 600 }, .index = { "Helvetica", 10, 32 }, .comment_line_skip = 14, .index_line_skip = 12, .index_column_skip = 195, /* make a little wider than in portrait */ .index_lines = 45, .index_columns = 4, }, portrait = { .file_setup = "%%Orientation: Portrait", .overlay_setup = "0 790 translate", .left = 20, .name = { "Helvetica-Bold", 24, 57 }, .path = { "Helvetica-Bold", 18, 30 }, .lib = { "Courier", 12, 75 }, .comment = { "Helvetica", 12, 740 }, .index = { "Helvetica", 10, 0 }, .comment_line_skip = 14, .index_line_skip = 12, .index_column_skip = 185, .index_lines = 63, .index_columns = 3, }; static const struct format *format; static int total, done = 0; /* ----- Utility functions ------------------------------------------------- */ static void ps_string(FILE *file, const char *s) { fputc('(', file); while (*s) { if (*s == '(' || *s == ')' || *s == '\\') fputc('\\', file); fputc(*s, file); s++; } fputc(')', file); } /* ----- Alphabetic index -------------------------------------------------- */ struct index { const char *s; const struct node *node; }; static void collect_names(const struct node *node, struct index **idx, int *n) { const struct name *name; while (node) { if (node->child) collect_names(node->child, idx, n); else { for (name = node->e->names; name; name = name->next) { (*n)++; *idx = realloc(*idx, *n*sizeof(struct entry)); if (!*idx) abort(); (*idx)[*n-1].s = name->s; (*idx)[*n-1].node = node; } } node = node->next; } } static int comp(const void *a, const void *b) { const struct index *ai = a; const struct index *bi = b; return strcasecmp(ai->s, bi->s); } static void make_index(FILE *file, const struct node *node) { struct index *idx = NULL; const struct index *p; int n = 0; int line = 0, col = 0; collect_names(node, &idx, &n); qsort(idx, n, sizeof(struct index), comp); fprintf(file, "[ /Title (Index) /Count 0 /OUT pdfmark\n"); fprintf(file, "/%s findfont %d scalefont setfont\n", format->index.font, format->index.size); fprintf(file, "gsave %s 0 setgray\n", format->overlay_setup); for (p = idx; p != idx+n; p++) { if (line == format->index_lines) { line = 0; col++; } if (col == format->index_columns) { fprintf(file, "grestore showpage\n"); fprintf(file, "gsave %s 0 setgray\n", format->overlay_setup); col = 0; } fprintf(file, "newpath %d -%d moveto currentpoint\n", format->left+col*format->index_column_skip, format->index.y+line*format->index_line_skip); fprintf(file, "[ /Rect [ "); ps_string(file, p->s); fprintf(file, " false charpath flattenpath pathbbox ]\n"); fprintf(file, " /Subtype /Link\n"); fprintf(file, " /Border [ 0 0 0 ]\n"); fprintf(file, " /Action << /Subtype /GoTo /Dest /%p%p >>\n", p->node, p->s); fprintf(file, " /ANN pdfmark\n"); fprintf(file, "moveto "); ps_string(file, p->s); fprintf(file, " show\n"); line++; } fprintf(file, "grestore showpage\n"); free(idx); } /* ----- Overlay and table of contents ------------------------------------- */ static int children(const struct node *node) { int n = 0; while (node) { n++; node = node->next; } return n; } static void print_path(FILE *file, const struct node *node) { if (node->parent) { print_path(file, node->parent); fprintf(file, "( > ) show\n"); } ps_string(file, node->name); fprintf(file, " 0.5 setgray show 0 setgray\n"); } static void make_title(FILE *file, const struct node *node, int unit) { const struct name *name; fprintf(file, "gsave %s 0 setgray\n", format->overlay_setup); fprintf(file, "/%s findfont %d scalefont setfont\n", format->name.font, format->name.size); fprintf(file, "%d %d moveto\n", format->left, -format->name.y); for (name = node->e->names; name; name = name->next) { if (name != node->e->names) fprintf(file, "(, ) show 0.5 setgray\n"); ps_string(file, name->s); fprintf(file, " show\n"); if (!unit) fprintf(file, "[ /Dest /%p%p /DEST pdfmark\n", node, name->s); } fprintf(file, "0 setgray\n"); if (node->e->units > 1) fprintf(file, " ( \\(%c\\)) show\n", 'A'+unit); fprintf(file, "/%s findfont %d scalefont setfont\n", format->path.font, format->path.size); fprintf(file, "%d %d moveto\n", format->left, -format->path.y); print_path(file, node); fprintf(file, "/%s findfont %d scalefont setfont\n", format->lib.font, format->lib.size); fprintf(file, "%d %d moveto\n", format->left, -format->lib.y); ps_string(file, node->e->file->path); fprintf(file, " show\n"); fprintf(file, "grestore\n"); } static void print_comment(FILE *file, const struct line *comment) { const struct line *line; int lines = 0; int n; fprintf(file, "gsave %s 0 setgray\n", format->overlay_setup); fprintf(file, "/%s findfont %d scalefont setfont\n", format->comment.font, format->comment.size); for (line = comment; line; line = line->next) lines++; n = 0; for (line = comment; line; line = line->next) { n++; fprintf(file, "newpath %d -%d moveto\n", format->left, format->comment.y+(n-lines)*format->comment_line_skip); if (line->url) { fprintf(file, "currentpoint\n"); fprintf(file, "[ /Rect [ "); ps_string(file, line->s); fprintf(file, " false charpath flattenpath pathbbox ]\n"); fprintf(file, " /Subtype /Link\n"); fprintf(file, " /Border [ 0 0 0 ]\n"); fprintf(file, " /Action << /Subtype /URI /URI "); ps_string(file, line->s); fprintf(file, " >>\n"); fprintf(file, " /ANN pdfmark\n"); fprintf(file, " moveto\n"); } ps_string(file, line->s); fprintf(file, " show\n"); } fprintf(file, "grestore\n"); } /* ----- Component conversion ---------------------------------------------- */ static void convert_entry(const struct node *node, FILE *out) { const struct lib *lib = node->e->file->lib; int i; for (i = 0; i != node->e->units; i++) { if (!quiet) { fprintf(stderr, "\r%u/%u", ++done, total); fflush(stderr); } make_title(out, node, i); if (!i && node->comment) print_comment(out, node->comment); fprintf(out, "gsave\n"); node->e->file->lib->ps_entry(out, lib, node->e, i, format == &landscape); fprintf(out, "\ngrestore\n"); } } static void convert_tree(const struct node *node, FILE *out) { while (node) { fprintf(out, "[ /Title (%s) /Count %d /OUT pdfmark\n", node->name, -children(node->child)); if (node->child) convert_tree(node->child, out); else convert_entry(node, out); node = node->next; } } /* ----- Setup and PDF generation ------------------------------------------ */ static int count_tree(const struct node *node) { int sum = 0; while (node) { if (node->child) sum += count_tree(node->child); else sum += node->e->units; node = node->next; } return sum; } void make_pdf(int pdf, int use_portrait, const char *title_page) { FILE *out; int res; if (use_portrait) format = &portrait; else format = &landscape; if (pdf) out = popen( "gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sPAPERSIZE=a4 " "-sOutputFile=- -f -", "w"); else out = popen("cat", "w"); if (!out) { perror("gs"); exit(1); } fprintf(out, "%%!PS\n%s\n", format->file_setup); if (title_page) { fprintf(out, "gsave\n"); cat(out, title_page); fprintf(out, "grestore\n"); } make_index(out, tree); total = count_tree(tree); convert_tree(tree, out); if (!quiet) fprintf(stderr, "\rFinishing\n"); res = pclose(out); if (res < 0) { perror("pclose"); exit(1); } if (res) { fprintf(stderr, "exit status %d\n", res); exit(1); } }