Root/genkicat/pdf.c

1/*
2 * pdf.c - Generate PDF
3 *
4 * Copyright 2012, 2013 by Werner Almesberger
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12#define _GNU_SOURCE /* for strcasecmp */
13#include <stdlib.h>
14#include <stdio.h>
15#include <string.h>
16
17#include "util.h"
18#include "run.h"
19#include "genkicat.h"
20#include "tree.h"
21#include "libs.h"
22#include "pdf.h"
23
24
25static struct format {
26    const char *file_setup;
27    const char *overlay_setup;
28    int left;
29    struct text {
30        const char *font;
31        int size;
32        int y;
33    } name, path, lib, comment, index;
34    int comment_line_skip;
35    int index_line_skip;
36    int index_column_skip;
37    int index_lines;
38    int index_columns;
39} landscape = {
40    .file_setup = "%%Orientation: Landscape",
41    .overlay_setup = "90 rotate",
42    .left = 20,
43    .name = { "Helvetica-Bold", 24, 57 },
44    .path = { "Helvetica-Bold", 18, 30 },
45    .lib = { "Courier", 12, 75 },
46    .comment = { "Helvetica", 12, 600 },
47    .index = { "Helvetica", 10, 32 },
48    .comment_line_skip = 14,
49    .index_line_skip = 12,
50    .index_column_skip = 195, /* make a little wider than in portrait */
51    .index_lines = 45,
52    .index_columns = 4,
53}, portrait = {
54    .file_setup = "%%Orientation: Portrait",
55    .overlay_setup = "0 790 translate",
56    .left = 20,
57    .name = { "Helvetica-Bold", 24, 57 },
58    .path = { "Helvetica-Bold", 18, 30 },
59    .lib = { "Courier", 12, 75 },
60    .comment = { "Helvetica", 12, 740 },
61    .index = { "Helvetica", 10, 0 },
62    .comment_line_skip = 14,
63    .index_line_skip = 12,
64    .index_column_skip = 185,
65    .index_lines = 63,
66    .index_columns = 3,
67};
68
69static const struct format *format;
70
71static int total, done = 0;
72
73
74/* ----- Utility functions ------------------------------------------------- */
75
76
77static void ps_string(FILE *file, const char *s)
78{
79    fputc('(', file);
80    while (*s) {
81        if (*s == '(' || *s == ')' || *s == '\\')
82            fputc('\\', file);
83        fputc(*s, file);
84        s++;
85    }
86    fputc(')', file);
87}
88
89
90/* ----- Alphabetic index -------------------------------------------------- */
91
92
93struct index {
94    const char *s;
95    const struct node *node;
96};
97
98
99static void collect_names(const struct node *node, struct index **idx, int *n)
100{
101    const struct name *name;
102
103    while (node) {
104        if (node->child)
105            collect_names(node->child, idx, n);
106        else {
107            for (name = node->e->names; name; name = name->next) {
108                (*n)++;
109                *idx = realloc(*idx, *n*sizeof(struct entry));
110                if (!*idx)
111                    abort();
112                (*idx)[*n-1].s = name->s;
113                (*idx)[*n-1].node = node;
114            }
115        }
116        node = node->next;
117    }
118}
119
120
121static int comp(const void *a, const void *b)
122{
123    const struct index *ai = a;
124    const struct index *bi = b;
125
126    return strcasecmp(ai->s, bi->s);
127}
128
129
130static void make_index(FILE *file, const struct node *node)
131{
132    struct index *idx = NULL;
133    const struct index *p;
134    int n = 0;
135    int line = 0, col = 0;
136
137    collect_names(node, &idx, &n);
138    qsort(idx, n, sizeof(struct index), comp);
139
140    fprintf(file, "[ /Title (Index) /Count 0 /OUT pdfmark\n");
141
142    fprintf(file, "/%s findfont %d scalefont setfont\n",
143        format->index.font, format->index.size);
144    fprintf(file, "gsave %s 0 setgray\n", format->overlay_setup);
145
146    for (p = idx; p != idx+n; p++) {
147        if (line == format->index_lines) {
148            line = 0;
149            col++;
150        }
151        if (col == format->index_columns) {
152            fprintf(file, "grestore showpage\n");
153            fprintf(file, "gsave %s 0 setgray\n",
154                format->overlay_setup);
155            col = 0;
156        }
157        fprintf(file, "newpath %d -%d moveto currentpoint\n",
158            format->left+col*format->index_column_skip,
159            format->index.y+line*format->index_line_skip);
160
161        fprintf(file, "[ /Rect [ ");
162        ps_string(file, p->s);
163        fprintf(file, " false charpath flattenpath pathbbox ]\n");
164        fprintf(file, " /Subtype /Link\n");
165        fprintf(file, " /Border [ 0 0 0 ]\n");
166        fprintf(file, " /Action << /Subtype /GoTo /Dest /%p%p >>\n",
167            p->node, p->s);
168        fprintf(file, " /ANN pdfmark\n");
169
170        fprintf(file, "moveto ");
171        ps_string(file, p->s);
172        fprintf(file, " show\n");
173        line++;
174    }
175    fprintf(file, "grestore showpage\n");
176
177    free(idx);
178}
179
180
181/* ----- Overlay and table of contents ------------------------------------- */
182
183
184static int children(const struct node *node)
185{
186    int n = 0;
187
188    while (node) {
189        n++;
190        node = node->next;
191    }
192    return n;
193}
194
195
196static void print_path(FILE *file, const struct node *node)
197{
198    if (node->parent) {
199        print_path(file, node->parent);
200        fprintf(file, "( > ) show\n");
201    }
202    ps_string(file, node->name);
203    fprintf(file, " 0.5 setgray show 0 setgray\n");
204}
205
206
207static void make_title(FILE *file, const struct node *node, int unit)
208{
209    const struct name *name;
210
211    fprintf(file, "gsave %s 0 setgray\n", format->overlay_setup);
212
213    fprintf(file, "/%s findfont %d scalefont setfont\n",
214        format->name.font, format->name.size);
215    fprintf(file, "%d %d moveto\n", format->left, -format->name.y);
216    for (name = node->e->names; name; name = name->next) {
217        if (name != node->e->names)
218            fprintf(file, "(, ) show 0.5 setgray\n");
219        ps_string(file, name->s);
220        fprintf(file, " show\n");
221        if (!unit)
222            fprintf(file, "[ /Dest /%p%p /DEST pdfmark\n",
223                node, name->s);
224    }
225    fprintf(file, "0 setgray\n");
226    if (node->e->units > 1)
227        fprintf(file, " ( \\(%c\\)) show\n", 'A'+unit);
228
229    fprintf(file, "/%s findfont %d scalefont setfont\n",
230        format->path.font, format->path.size);
231    fprintf(file, "%d %d moveto\n", format->left, -format->path.y);
232    print_path(file, node);
233
234    fprintf(file, "/%s findfont %d scalefont setfont\n",
235        format->lib.font, format->lib.size);
236    fprintf(file, "%d %d moveto\n", format->left, -format->lib.y);
237    ps_string(file, node->e->file->path);
238    fprintf(file, " show\n");
239
240    fprintf(file, "grestore\n");
241}
242
243
244static void print_comment(FILE *file, const struct line *comment)
245{
246    const struct line *line;
247    int lines = 0;
248    int n;
249
250    fprintf(file, "gsave %s 0 setgray\n", format->overlay_setup);
251    fprintf(file, "/%s findfont %d scalefont setfont\n",
252        format->comment.font, format->comment.size);
253    for (line = comment; line; line = line->next)
254        lines++;
255    n = 0;
256    for (line = comment; line; line = line->next) {
257        n++;
258        fprintf(file, "newpath %d -%d moveto\n", format->left,
259            format->comment.y+(n-lines)*format->comment_line_skip);
260        if (line->url) {
261            fprintf(file, "currentpoint\n");
262            fprintf(file, "[ /Rect [ ");
263            ps_string(file, line->s);
264            fprintf(file,
265                " false charpath flattenpath pathbbox ]\n");
266            fprintf(file, " /Subtype /Link\n");
267            fprintf(file, " /Border [ 0 0 0 ]\n");
268            fprintf(file, " /Action << /Subtype /URI /URI ");
269            ps_string(file, line->s);
270            fprintf(file, " >>\n");
271            fprintf(file, " /ANN pdfmark\n");
272            fprintf(file, " moveto\n");
273        }
274        ps_string(file, line->s);
275        fprintf(file, " show\n");
276
277    }
278    fprintf(file, "grestore\n");
279}
280
281
282/* ----- Component conversion ---------------------------------------------- */
283
284
285static void convert_entry(const struct node *node, FILE *out)
286{
287    const struct lib *lib = node->e->file->lib;
288    int i;
289
290    for (i = 0; i != node->e->units; i++) {
291        if (!quiet) {
292            fprintf(stderr, "\r%u/%u", ++done, total);
293            fflush(stderr);
294        }
295        make_title(out, node, i);
296        if (!i && node->comment)
297            print_comment(out, node->comment);
298        fprintf(out, "gsave\n");
299        node->e->file->lib->ps_entry(out, lib, node->e, i,
300            format == &landscape);
301        fprintf(out, "\ngrestore\n");
302    }
303}
304
305
306static void convert_tree(const struct node *node, FILE *out)
307{
308    while (node) {
309        fprintf(out, "[ /Title (%s) /Count %d /OUT pdfmark\n",
310            node->name, -children(node->child));
311        if (node->child)
312            convert_tree(node->child, out);
313        else
314            convert_entry(node, out);
315        node = node->next;
316    }
317}
318
319
320/* ----- Setup and PDF generation ------------------------------------------ */
321
322
323static int count_tree(const struct node *node)
324{
325    int sum = 0;
326
327    while (node) {
328        if (node->child)
329            sum += count_tree(node->child);
330        else
331            sum += node->e->units;
332        node = node->next;
333    }
334    return sum;
335}
336
337
338void make_pdf(int pdf, int use_portrait, const char *title_page)
339{
340    FILE *out;
341    int res;
342
343    if (use_portrait)
344        format = &portrait;
345    else
346        format = &landscape;
347    if (pdf)
348        out = popen(
349            "gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sPAPERSIZE=a4 "
350              "-sOutputFile=- -f -", "w");
351    else
352        out = popen("cat", "w");
353    if (!out) {
354        perror("gs");
355        exit(1);
356    }
357    fprintf(out, "%%!PS\n%s\n", format->file_setup);
358    if (title_page) {
359        fprintf(out, "gsave\n");
360        cat(out, title_page);
361        fprintf(out, "grestore\n");
362    }
363    make_index(out, tree);
364    total = count_tree(tree);
365    convert_tree(tree, out);
366    if (!quiet)
367        fprintf(stderr, "\rFinishing\n");
368    res = pclose(out);
369    if (res < 0) {
370        perror("pclose");
371        exit(1);
372    }
373    if (res) {
374        fprintf(stderr, "exit status %d\n", res);
375        exit(1);
376    }
377}
378

Archive Download this file

Branches:
master



interactive