Root/
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 | |
25 | static 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 | |
69 | static const struct format *format; |
70 | |
71 | static int total, done = 0; |
72 | |
73 | |
74 | /* ----- Utility functions ------------------------------------------------- */ |
75 | |
76 | |
77 | static 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 | |
93 | struct index { |
94 | const char *s; |
95 | const struct node *node; |
96 | }; |
97 | |
98 | |
99 | static 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 | |
121 | static 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 | |
130 | static 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 | |
184 | static 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 | |
196 | static 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 | |
207 | static 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 | |
244 | static 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 | |
285 | static 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 | |
306 | static 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 | |
323 | static 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 | |
338 | void 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 |
Branches:
master