/* * pdf.c - Generate PDF * * Copyright 2012 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. */ #include #include #include #include "util.h" #include "gencat.h" #include "tree.h" #include "libs.h" #include "pdf.h" static struct format { const char *file_setup; const char *overlay_setup; const char *comp_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", .comp_setup = "", .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 = 140, .index_lines = 45, .index_columns = 6, }, portrait = { .file_setup = "%%Orientation: Portrait", .overlay_setup = "0 790 translate", .comp_setup = "-120 700 translate -90 rotate", .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 = 140, .index_lines = 63, .index_columns = 4, }, format; static int total, done = 0; /* ----- Utility functions ------------------------------------------------- */ static char *sanitize_name(const char *s) { char *t, *tmp; tmp = alloc_size(strlen(s)+1); for (t = tmp; *s; s++) { if (*s <= ' ' || *s > '~') continue; /* PDF reference 3rd ed., 3.2.2 ASCII Encoding */ if (strchr("()<>[]{}/%", *s)) continue; *t++ = *s; } *t = 0; return tmp; } 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 -------------------------------------------------- */ static void collect_names(const struct node *node, const char ***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(char *)); if (!*idx) abort(); (*idx)[*n-1] = name->s; } } node = node->next; } } static int comp(const void *a, const void *b) { return strcmp(*(const char **) a, *(const char **) b); } static void make_index(FILE *file, const struct node *node) { const char **idx = NULL, **p; int n = 0; int line = 0, col = 0; char *s; collect_names(node, &idx, &n); qsort(idx, n, sizeof(char *), 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); s = sanitize_name(*p); fprintf(file, "[ /Rect [ "); ps_string(file, *p); 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 /%s >>\n", s); fprintf(file, " /ANN pdfmark\n"); free(s); fprintf(file, "moveto "); ps_string(file, *p); 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; char *s; 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"); s = sanitize_name(name->s); if (!unit) fprintf(file, "[ /Dest /%s /DEST pdfmark\n", s); free(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, "%d -%d moveto\n", format.left, format.comment.y+(n-lines)*format.comment_line_skip); ps_string(file, line->s); fprintf(file, " show\n"); } fprintf(file, "grestore\n"); } /* ----- Component conversion ---------------------------------------------- */ static void convert_comp(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 %s\n", format.comp_setup); node->e->file->lib->ps_entry(out, lib, node->e, i); 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_comp(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) { FILE *out; int res; if (use_portrait) format = portrait; else format = landscape; if (pdf) out = popen( "gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=- " "-f -", "w"); else out = popen("cat", "w"); if (!out) { perror("gs"); exit(1); } fprintf(out, "%%!PS\n%s\n", format.file_setup); 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); } }