From e1d613a20baeb81e9ac98feb91df494ec71c1e59 Mon Sep 17 00:00:00 2001 From: Werner Almesberger Date: Wed, 11 Jul 2012 12:22:23 -0300 Subject: [PATCH] gencat/pdf.c: add generation of alphabetic index with hyperlinks --- gencat/pdf.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 126 insertions(+), 7 deletions(-) diff --git a/gencat/pdf.c b/gencat/pdf.c index 0d0e941..5e5cc0d 100644 --- a/gencat/pdf.c +++ b/gencat/pdf.c @@ -12,8 +12,10 @@ #define _GNU_SOURCE #include #include +#include #include +#include "util.h" #include "gencat.h" #include "tree.h" #include "pdf.h" @@ -28,8 +30,12 @@ static struct format { const char *font; int size; int y; - } name, path, lib, comment; + } 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", @@ -39,7 +45,12 @@ static struct format { .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", @@ -49,21 +60,32 @@ static struct format { .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; -static int children(const struct node *node) +static char *sanitize_name(const char *s) { - int n = 0; + char *t, *tmp; - while (node) { - n++; - node = node->next; + 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; } - return n; + *t = 0; + return tmp; } @@ -80,6 +102,97 @@ static void ps_string(FILE *file, const char *s) } +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->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); +} + + +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) { @@ -94,6 +207,7 @@ static void print_path(FILE *file, const struct node *node) 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); @@ -105,6 +219,10 @@ static void make_title(FILE *file, const struct node *node, int unit) 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->units > 1) @@ -260,6 +378,7 @@ void make_pdf(int pdf, int use_portrait) 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)