Root/
Source at commit c28f701086339ed9b937e7f6779910ace2106cbe created 11 years 8 months ago. By Werner Almesberger, postscript.c (prologue): set line join style to "round" in showoutlined | |
---|---|
1 | /* |
2 | * postscript.c - Dump objects in Postscript |
3 | * |
4 | * Written 2009-2012 by Werner Almesberger |
5 | * Copyright 2009-2012 by Werner Almesberger |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | */ |
12 | |
13 | |
14 | #include <stdlib.h> |
15 | #include <stdio.h> |
16 | #include <string.h> |
17 | #include <ctype.h> |
18 | #include <errno.h> |
19 | |
20 | #include "util.h" |
21 | #include "coord.h" |
22 | #include "layer.h" |
23 | #include "obj.h" |
24 | #include "inst.h" |
25 | #include "unparse.h" |
26 | #include "gui_status.h" |
27 | #include "gui_inst.h" |
28 | #include "postscript.h" |
29 | |
30 | |
31 | /* |
32 | * A4 is 210 mm x 297 mm |
33 | * US Letter is 216 mm x 279 mm |
34 | * |
35 | * We pick the smallest dimensions minus a bit of slack and center on the |
36 | * printer page. |
37 | */ |
38 | |
39 | #define PAGE_HALF_WIDTH mm_to_units(210/2.0-10) /* A4 */ |
40 | #define PAGE_HALF_HEIGHT mm_to_units(279/2.0-15) /* US Letter */ |
41 | |
42 | /* |
43 | * Page layout: |
44 | * |
45 | * HEADER DATE |
46 | * --------------------------- HEADER_HEIGHT+DIVIDER_BORDER below top |
47 | * | |
48 | * | 2x |
49 | * 10 x |<------------- roughly at 10/12 |
50 | * | 1x |
51 | * | |
52 | * --------------------------- 50% height |
53 | * Frames in boxes |
54 | * |
55 | */ |
56 | |
57 | |
58 | #define PS_HEADER_HEIGHT mm_to_units(8) |
59 | #define PS_DIVIDER_BORDER mm_to_units(2) |
60 | #define PS_DIVIDER_WIDTH mm_to_units(0.5) |
61 | |
62 | #define PS_MISC_TEXT_HEIGHT mm_to_units(3) |
63 | |
64 | #define PS_DOT_DIST mm_to_units(0.03) |
65 | #define PS_DOT_DIAM mm_to_units(0.01) |
66 | |
67 | #define PS_HATCH mm_to_units(0.1) |
68 | #define PS_HATCH_LINE mm_to_units(0.015) |
69 | |
70 | #define PS_STRIPE mm_to_units(0.08) |
71 | |
72 | #define PS_RIM_LINE mm_to_units(0.02) |
73 | |
74 | #define PS_FONT_OUTLINE mm_to_units(0.025) |
75 | |
76 | #define PS_VEC_LINE mm_to_units(0.02) |
77 | #define PS_VEC_ARROW_LEN mm_to_units(0.3) |
78 | #define PS_VEC_ARROW_ANGLE 20 |
79 | #define PS_VEC_TEXT_HEIGHT mm_to_units(3) /* ~8.5 pt, real mm */ |
80 | #define PS_VEC_BASE_OFFSET mm_to_units(0.5) /* real mm */ |
81 | |
82 | #define PS_MEAS_LINE mm_to_units(0.1) /* real mm */ |
83 | #define PS_MEAS_ARROW_LEN mm_to_units(0.15) |
84 | #define PS_MEAS_ARROW_ANGLE 30 |
85 | #define PS_MEAS_TEXT_HEIGHT mm_to_units(3) /* ~8.5 pt, real mm */ |
86 | #define PS_MEAS_BASE_OFFSET mm_to_units(0.5) /* real mm */ |
87 | #define PS_MEAS_MIN_HEIGHT (PS_MEAS_TEXT_HEIGHT/2) |
88 | |
89 | #define PS_CROSS_WIDTH mm_to_units(0.01) |
90 | #define PS_CROSS_DASH mm_to_units(0.1) |
91 | |
92 | #define PS_KEY_GAP mm_to_units(8) |
93 | #define PS_KEY_HEIGTH mm_to_units(8) |
94 | |
95 | #define TEXT_HEIGHT_FACTOR 1.5 /* height/width of typical text */ |
96 | |
97 | |
98 | struct postscript_params postscript_params = { |
99 | .zoom = 0, |
100 | .max_width = 0, |
101 | .max_height = 0, |
102 | .show_pad_names = 1, |
103 | .show_stuff = 0, |
104 | .label_vecs = 0, |
105 | .show_meas = 1, |
106 | .show_key = 0, |
107 | }; |
108 | |
109 | static const struct postscript_params minimal_params; |
110 | static struct postscript_params active_params; |
111 | |
112 | |
113 | /* ----- Boxes ------------------------------------------------------------- */ |
114 | |
115 | |
116 | static struct box { |
117 | unit_type x, y; /* width and height */ |
118 | unit_type x0, y0; /* lower left corner */ |
119 | struct box *next; |
120 | } *boxes = NULL; |
121 | |
122 | |
123 | static void add_box(unit_type xa, unit_type ya, unit_type xb, unit_type yb) |
124 | { |
125 | struct box *box; |
126 | |
127 | box = alloc_type(struct box); |
128 | box->x = xb-xa; |
129 | box->y = yb-ya; |
130 | box->x0 = xa; |
131 | box->y0 = ya; |
132 | box->next = boxes; |
133 | boxes = box; |
134 | } |
135 | |
136 | |
137 | static void free_boxes(void) |
138 | { |
139 | struct box *next; |
140 | |
141 | while (boxes) { |
142 | next = boxes->next; |
143 | free(boxes); |
144 | boxes = next; |
145 | } |
146 | } |
147 | |
148 | |
149 | static int get_box(unit_type x, unit_type y, unit_type *xa, unit_type *ya) |
150 | { |
151 | struct box **box, **best = NULL; |
152 | struct box *b; |
153 | double size, best_size; |
154 | |
155 | for (box = &boxes; *box; box = &(*box)->next) { |
156 | if ((*box)->x < x || (*box)->y < y) |
157 | continue; |
158 | size = (double) (*box)->x*(*box)->y; |
159 | if (!best || size < best_size) { |
160 | best = box; |
161 | best_size = size; |
162 | } |
163 | } |
164 | if (!best) |
165 | return 0; |
166 | b = *best; |
167 | if (xa) |
168 | *xa = b->x0; |
169 | if (ya) |
170 | *ya = b->y0+b->y-y; |
171 | |
172 | *best = b->next; |
173 | add_box(b->x0+x, b->y0, b->x0+b->x, b->y0+b->y); |
174 | add_box(b->x0, b->y0, b->x0+x, b->y0+b->y-y); |
175 | free(b); |
176 | |
177 | return 1; |
178 | } |
179 | |
180 | |
181 | /* ----- Helper functions -------------------------------------------------- */ |
182 | |
183 | |
184 | static void ps_string(FILE *file, const char *s) |
185 | { |
186 | fputc('(', file); |
187 | while (*s) { |
188 | if (*s == '(' || *s == ')' || *s == '\\') |
189 | fputc('\\', file); |
190 | fputc(*s, file); |
191 | s++; |
192 | } |
193 | fputc(')', file); |
194 | } |
195 | |
196 | |
197 | static void ps_filled_box(FILE *file, struct coord a, struct coord b, |
198 | const char *pattern) |
199 | { |
200 | fprintf(file, "0 setgray %d setlinewidth\n", PS_HATCH_LINE); |
201 | fprintf(file, " %d %d moveto\n", a.x, a.y); |
202 | fprintf(file, " %d %d lineto\n", b.x, a.y); |
203 | fprintf(file, " %d %d lineto\n", b.x, b.y); |
204 | fprintf(file, " %d %d lineto\n", a.x, b.y); |
205 | fprintf(file, " closepath gsave %s grestore stroke\n", pattern); |
206 | } |
207 | |
208 | |
209 | static void ps_outlined_text_in_rect(FILE *file, const char *s, |
210 | struct coord a, struct coord b) |
211 | { |
212 | const char *t; |
213 | unit_type h, w; |
214 | |
215 | for (t = s; *t == ' '; t++); |
216 | if (!*t) |
217 | return; |
218 | h = a.y-b.y; |
219 | w = a.x-b.x; |
220 | if (h < 0) |
221 | h = -h; |
222 | if (w < 0) |
223 | w = -w; |
224 | fprintf(file, "0 setgray /Helvetica-Bold findfont dup\n"); |
225 | fprintf(file, " "); |
226 | ps_string(file, s); |
227 | fprintf(file, " %d %d\n", w/2, h/2); |
228 | fprintf(file, " boxfont\n"); |
229 | fprintf(file, " %d %d moveto\n", (a.x+b.x)/2, (a.y+b.y)/2); |
230 | fprintf(file, " "); |
231 | ps_string(file, s); |
232 | fprintf(file, " center %d showoutlined newpath\n", PS_FONT_OUTLINE); |
233 | } |
234 | |
235 | |
236 | /* ----- Items ------------------------------------------------------------- */ |
237 | |
238 | |
239 | static void ps_pad_name(FILE *file, const struct inst *inst) |
240 | { |
241 | ps_outlined_text_in_rect(file, inst->u.pad.name, |
242 | inst->base, inst->u.pad.other); |
243 | } |
244 | |
245 | |
246 | static const char *hatch(enum pad_type type) |
247 | { |
248 | switch (type) { |
249 | case pt_normal: |
250 | return "crosspath"; |
251 | case pt_bare: |
252 | return "hatchpath"; |
253 | case pt_paste: |
254 | return "backhatchpath"; |
255 | case pt_mask: |
256 | return "dotpath"; |
257 | case pt_trace: |
258 | return "horpath"; |
259 | default: |
260 | abort(); |
261 | } |
262 | } |
263 | |
264 | |
265 | static void ps_pad(FILE *file, const struct inst *inst, int show_name) |
266 | { |
267 | ps_filled_box(file, inst->base, inst->u.pad.other, |
268 | hatch(layers_to_pad_type(inst->u.pad.layers))); |
269 | |
270 | if (show_name && !inst->u.pad.hole) |
271 | ps_pad_name(file, inst); |
272 | } |
273 | |
274 | |
275 | static void ps_rounded_rect(FILE *file, struct coord a, struct coord b) |
276 | { |
277 | unit_type h, w, r; |
278 | |
279 | sort_coord(&a, &b); |
280 | h = b.y-a.y; |
281 | w = b.x-a.x; |
282 | |
283 | if (h > w) { |
284 | r = w/2; |
285 | fprintf(file, " %d %d moveto\n", b.x, b.y-r); |
286 | fprintf(file, " %d %d %d 0 180 arc\n", a.x+r, b.y-r, r); |
287 | fprintf(file, " %d %d lineto\n", a.x, a.y+r); |
288 | fprintf(file, " %d %d %d 180 360 arc\n", a.x+r, a.y+r, r); |
289 | } else { |
290 | r = h/2; |
291 | fprintf(file, " %d %d moveto\n", b.x-r, a.y); |
292 | fprintf(file, " %d %d %d -90 90 arc\n", b.x-r, a.y+r, r); |
293 | fprintf(file, " %d %d lineto\n", a.x+r, b.y); |
294 | fprintf(file, " %d %d %d 90 270 arc\n", a.x+r, a.y+r, r); |
295 | } |
296 | } |
297 | |
298 | |
299 | static void ps_rpad(FILE *file, const struct inst *inst, int show_name) |
300 | { |
301 | fprintf(file, "0 setgray %d setlinewidth\n", PS_HATCH_LINE); |
302 | ps_rounded_rect(file, inst->base, inst->u.pad.other); |
303 | fprintf(file, " closepath gsave %s grestore stroke\n", |
304 | hatch(layers_to_pad_type(inst->u.pad.layers))); |
305 | |
306 | if (show_name && !inst->u.pad.hole) |
307 | ps_pad_name(file, inst); |
308 | } |
309 | |
310 | |
311 | static void ps_hole(FILE *file, const struct inst *inst, int show_name) |
312 | { |
313 | fprintf(file, "1 setgray %d setlinewidth\n", PS_RIM_LINE); |
314 | ps_rounded_rect(file, inst->base, inst->u.hole.other); |
315 | fprintf(file, " closepath gsave fill grestore\n"); |
316 | fprintf(file, " 0 setgray stroke\n"); |
317 | |
318 | if (show_name && inst->u.hole.pad) |
319 | ps_pad_name(file, inst->u.hole.pad); |
320 | } |
321 | |
322 | |
323 | static void ps_line(FILE *file, const struct inst *inst) |
324 | { |
325 | struct coord a = inst->base; |
326 | struct coord b = inst->u.rect.end; |
327 | |
328 | fprintf(file, "1 setlinecap 0.5 setgray %d setlinewidth\n", |
329 | inst->u.rect.width); |
330 | fprintf(file, " %d %d moveto %d %d lineto stroke\n", |
331 | a.x, a.y, b.x, b.y); |
332 | } |
333 | |
334 | |
335 | static void ps_rect(FILE *file, const struct inst *inst) |
336 | { |
337 | struct coord a = inst->base; |
338 | struct coord b = inst->u.rect.end; |
339 | |
340 | fprintf(file, "1 setlinecap 0.5 setgray %d setlinewidth\n", |
341 | inst->u.rect.width); |
342 | fprintf(file, " %d %d moveto\n", a.x, a.y); |
343 | fprintf(file, " %d %d lineto\n", b.x, a.y); |
344 | fprintf(file, " %d %d lineto\n", b.x, b.y); |
345 | fprintf(file, " %d %d lineto\n", a.x, b.y); |
346 | fprintf(file, " closepath stroke\n"); |
347 | } |
348 | |
349 | |
350 | static void ps_arc(FILE *file, const struct inst *inst) |
351 | { |
352 | double a1, a2; |
353 | |
354 | a1 = inst->u.arc.a1; |
355 | a2 = inst->u.arc.a2; |
356 | if (a2 <= a1) |
357 | a2 += 360; |
358 | |
359 | fprintf(file, "1 setlinecap 0.5 setgray %d setlinewidth\n", |
360 | inst->u.arc.width); |
361 | fprintf(file, " newpath %d %d %d %f %f arc stroke\n", |
362 | inst->base.x, inst->base.y, inst->u.arc.r, a1, a2); |
363 | } |
364 | |
365 | |
366 | static void ps_frame(FILE *file, const struct inst *inst) |
367 | { |
368 | } |
369 | |
370 | |
371 | static void ps_arrow(FILE *file, struct coord from, struct coord to, int len, |
372 | int angle) |
373 | { |
374 | struct coord side, p; |
375 | |
376 | if (from.x == to.x && from.y == to.y) { |
377 | side.x = 0; |
378 | side.y = -len; |
379 | } else { |
380 | side = normalize(sub_vec(to, from), len); |
381 | } |
382 | |
383 | p = add_vec(to, rotate(side, 180-angle)); |
384 | fprintf(file, " %d %d moveto\n", p.x, p.y); |
385 | fprintf(file, " %d %d lineto\n", to.x, to.y); |
386 | |
387 | p = add_vec(to, rotate(side, 180+angle)); |
388 | fprintf(file, " %d %d moveto\n", p.x, p.y); |
389 | fprintf(file, " %d %d lineto\n", to.x, to.y); |
390 | fprintf(file, " stroke\n"); |
391 | } |
392 | |
393 | |
394 | static void ps_vec(FILE *file, const struct inst *inst) |
395 | { |
396 | struct coord a, b, c, d; |
397 | char *s, *sx, *sy; |
398 | |
399 | a = inst->base; |
400 | b = inst->u.vec.end; |
401 | fprintf(file, "1 setlinecap 0 setgray %d setlinewidth\n", PS_VEC_LINE); |
402 | fprintf(file, " %d %d moveto\n", a.x, a.y); |
403 | fprintf(file, " %d %d lineto\n", b.x, b.y); |
404 | fprintf(file, " stroke\n"); |
405 | |
406 | ps_arrow(file, a, b, PS_VEC_ARROW_LEN, PS_VEC_ARROW_ANGLE); |
407 | |
408 | if (!active_params.label_vecs) |
409 | return; |
410 | |
411 | sx = unparse(inst->vec->x); |
412 | sy = unparse(inst->vec->y); |
413 | s = stralloc_printf("(%s, %s)", sx, sy); |
414 | free(sx); |
415 | free(sy); |
416 | c = add_vec(a, b); |
417 | d = sub_vec(b, a); |
418 | fprintf(file, "gsave %d %d moveto\n", c.x/2, c.y/2); |
419 | fprintf(file, " /Helvetica-Bold findfont dup\n"); |
420 | fprintf(file, " "); |
421 | ps_string(file, s); |
422 | fprintf(file, " %d %d realsize\n", |
423 | (int) (dist_point(a, b)-2*PS_VEC_ARROW_LEN), |
424 | PS_VEC_TEXT_HEIGHT); |
425 | fprintf(file, " boxfont\n"); |
426 | fprintf(file, " %f rotate\n", atan2(d.y, d.x)/M_PI*180); |
427 | fprintf(file, " "); |
428 | ps_string(file, s); |
429 | fprintf(file, " %d realsize pop 0 hcenter\n", PS_VEC_BASE_OFFSET); |
430 | fprintf(file, " show grestore\n"); |
431 | free(s); |
432 | } |
433 | |
434 | |
435 | /* ----- Measurements ------------------------------------------------------ */ |
436 | |
437 | |
438 | static unit_type guesstimate_text_height(const char *s, unit_type width, |
439 | double zoom) |
440 | { |
441 | return width/strlen(s)*TEXT_HEIGHT_FACTOR*zoom; |
442 | } |
443 | |
444 | |
445 | static void ps_meas(FILE *file, const struct inst *inst, |
446 | enum curr_unit unit, double zoom) |
447 | { |
448 | struct coord a0, b0, a1, b1; |
449 | struct coord c, d; |
450 | char *s; |
451 | unit_type height, width, offset; |
452 | |
453 | a0 = inst->base; |
454 | b0 = inst->u.meas.end; |
455 | project_meas(inst, &a1, &b1); |
456 | fprintf(file, "1 setlinecap 0 setgray %d realsize setlinewidth\n", |
457 | PS_MEAS_LINE); |
458 | fprintf(file, " %d %d moveto\n", a0.x, a0.y); |
459 | fprintf(file, " %d %d lineto\n", a1.x, a1.y); |
460 | fprintf(file, " %d %d lineto\n", b1.x, b1.y); |
461 | fprintf(file, " %d %d lineto\n", b0.x, b0.y); |
462 | fprintf(file, " stroke\n"); |
463 | |
464 | ps_arrow(file, a1, b1, PS_MEAS_ARROW_LEN, PS_MEAS_ARROW_ANGLE); |
465 | ps_arrow(file, b1, a1, PS_MEAS_ARROW_LEN, PS_MEAS_ARROW_ANGLE); |
466 | |
467 | s = format_len(inst->obj->u.meas.label ? inst->obj->u.meas.label : "", |
468 | dist_point(a1, b1), unit); |
469 | |
470 | c = add_vec(a1, b1); |
471 | d = sub_vec(b1, a1); |
472 | |
473 | /* |
474 | * First try: put text between the arrows |
475 | */ |
476 | width = dist_point(a1, b1)-1.5*PS_MEAS_ARROW_LEN; |
477 | offset = PS_MEAS_BASE_OFFSET; |
478 | height = 0; |
479 | if (guesstimate_text_height(s, width, zoom) < PS_MEAS_MIN_HEIGHT) { |
480 | #if 0 |
481 | fprintf(stderr, "%s -> width %d height %d vs. %d\n", |
482 | s, width, guesstimate_text_height(s, width, zoom), PS_MEAS_MIN_HEIGHT); |
483 | #endif |
484 | /* |
485 | * Second try: push it above the arrows |
486 | */ |
487 | width = dist_point(a1, b1); |
488 | offset += |
489 | PS_MEAS_ARROW_LEN*sin(PS_MEAS_ARROW_ANGLE*M_PI/180)*zoom; |
490 | |
491 | if (guesstimate_text_height(s, width, zoom) < |
492 | PS_MEAS_MIN_HEIGHT) { |
493 | height = PS_MEAS_MIN_HEIGHT; |
494 | width = strlen(s)*height; |
495 | } |
496 | } |
497 | |
498 | if (height) { |
499 | fprintf(file, "gsave %d %d moveto\n", c.x/2, c.y/2); |
500 | fprintf(file, " /Helvetica-Bold findfont dup\n"); |
501 | fprintf(file, " "); |
502 | ps_string(file, s); |
503 | fprintf(file, " %d realsize %d realsize\n", width, height); |
504 | fprintf(file, " boxfont\n"); |
505 | fprintf(file, " %f rotate\n", atan2(d.y, d.x)/M_PI*180); |
506 | fprintf(file, " "); |
507 | ps_string(file, s); |
508 | fprintf(file, " %d realsize hcenter\n", offset); |
509 | fprintf(file, " show grestore\n"); |
510 | } else { |
511 | fprintf(file, "gsave %d %d moveto\n", c.x/2, c.y/2); |
512 | fprintf(file, " /Helvetica-Bold findfont dup\n"); |
513 | fprintf(file, " "); |
514 | ps_string(file, s); |
515 | fprintf(file, " %d %d realsize\n", width, PS_MEAS_TEXT_HEIGHT); |
516 | fprintf(file, " boxfont\n"); |
517 | fprintf(file, " %f rotate\n", atan2(d.y, d.x)/M_PI*180); |
518 | fprintf(file, " "); |
519 | ps_string(file, s); |
520 | fprintf(file, " %d realsize hcenter\n", offset); |
521 | fprintf(file, " show grestore\n"); |
522 | } |
523 | free(s); |
524 | } |
525 | |
526 | |
527 | /* ----- Print layers ------------------------------------------------------ */ |
528 | |
529 | |
530 | static void ps_background(FILE *file, enum inst_prio prio, |
531 | const struct inst *inst) |
532 | { |
533 | switch (prio) { |
534 | case ip_line: |
535 | ps_line(file, inst); |
536 | break; |
537 | case ip_rect: |
538 | ps_rect(file, inst); |
539 | break; |
540 | case ip_circ: |
541 | case ip_arc: |
542 | ps_arc(file, inst); |
543 | break; |
544 | default: |
545 | break; |
546 | } |
547 | } |
548 | |
549 | |
550 | static void ps_foreground(FILE *file, enum inst_prio prio, |
551 | const struct inst *inst, double zoom) |
552 | { |
553 | switch (prio) { |
554 | case ip_pad_copper: |
555 | case ip_pad_special: |
556 | if (inst->obj->u.pad.rounded) |
557 | ps_rpad(file, inst, active_params.show_pad_names); |
558 | else |
559 | ps_pad(file, inst, active_params.show_pad_names); |
560 | break; |
561 | case ip_hole: |
562 | ps_hole(file, inst, active_params.show_pad_names); |
563 | break; |
564 | case ip_vec: |
565 | if (active_params.show_stuff) |
566 | ps_vec(file, inst); |
567 | break; |
568 | case ip_frame: |
569 | if (active_params.show_stuff) |
570 | ps_frame(file, inst); |
571 | break; |
572 | case ip_meas: |
573 | if (active_params.show_meas) |
574 | ps_meas(file, inst, curr_unit, zoom); |
575 | break; |
576 | default: |
577 | break; |
578 | } |
579 | } |
580 | |
581 | |
582 | /* ----- Package level ----------------------------------------------------- */ |
583 | |
584 | |
585 | static void ps_cross(FILE *file, const struct inst *inst) |
586 | { |
587 | fprintf(file, "gsave 0 setgray %d setlinewidth\n", PS_CROSS_WIDTH); |
588 | fprintf(file, " [%d] 0 setdash\n", PS_CROSS_DASH); |
589 | fprintf(file, " %d 0 moveto %d 0 lineto\n", |
590 | inst->bbox.min.x, inst->bbox.max.x); |
591 | fprintf(file, " 0 %d moveto 0 %d lineto\n", |
592 | inst->bbox.min.y, inst->bbox.max.y); |
593 | fprintf(file, " stroke grestore\n"); |
594 | } |
595 | |
596 | |
597 | static void ps_draw_package(FILE *file, const struct pkg *pkg, double zoom, |
598 | int cross) |
599 | { |
600 | enum inst_prio prio; |
601 | const struct inst *inst; |
602 | |
603 | fprintf(file, "gsave %f dup scale\n", zoom); |
604 | if (cross) |
605 | ps_cross(file, pkgs->insts[ip_frame]); |
606 | FOR_INST_PRIOS_UP(prio) { |
607 | FOR_PKG_INSTS(pkgs, prio, inst) |
608 | ps_background(file, prio, inst); |
609 | FOR_PKG_INSTS(pkg, prio, inst) |
610 | ps_background(file, prio, inst); |
611 | } |
612 | FOR_INST_PRIOS_UP(prio) { |
613 | FOR_PKG_INSTS(pkgs, prio, inst) |
614 | ps_foreground(file, prio, inst, zoom); |
615 | FOR_PKG_INSTS(pkg, prio, inst) |
616 | ps_foreground(file, prio, inst, zoom); |
617 | } |
618 | fprintf(file, "grestore\n"); |
619 | } |
620 | |
621 | |
622 | /* ----- Object frames ----------------------------------------------------- */ |
623 | |
624 | |
625 | static void ps_draw_frame(FILE *file, const struct pkg *pkg, |
626 | const struct inst *outer, double zoom) |
627 | { |
628 | enum inst_prio prio; |
629 | const struct inst *inst; |
630 | |
631 | fprintf(file, "gsave %f dup scale\n", zoom); |
632 | ps_cross(file, outer); |
633 | FOR_INST_PRIOS_UP(prio) { |
634 | FOR_PKG_INSTS(pkgs, prio, inst) |
635 | if (inst->outer == outer) |
636 | ps_background(file, prio, inst); |
637 | FOR_PKG_INSTS(pkg, prio, inst) |
638 | if (inst->outer == outer) |
639 | ps_background(file, prio, inst); |
640 | } |
641 | FOR_INST_PRIOS_UP(prio) { |
642 | FOR_PKG_INSTS(pkgs, prio, inst) |
643 | if (inst->outer == outer) |
644 | ps_foreground(file, prio, inst, zoom); |
645 | FOR_PKG_INSTS(pkg, prio, inst) |
646 | if (inst->outer == outer) |
647 | ps_foreground(file, prio, inst, zoom); |
648 | } |
649 | fprintf(file, "grestore\n"); |
650 | } |
651 | |
652 | |
653 | static int generate_frames(FILE *file, const struct pkg *pkg, |
654 | const struct frame *frame, double zoom) |
655 | { |
656 | const struct inst *inst; |
657 | unit_type x, y, xa, ya; |
658 | unit_type cx, cy, border; |
659 | int ok; |
660 | |
661 | /* |
662 | * This doesn't work yet. The whole idea of just picking the current |
663 | * instance of each object and drawing it is flawed, since we may have |
664 | * very different sizes in a frame, so one big vector may dominate all |
665 | * the finer details. |
666 | * |
667 | * Also, the amount of text can be large and force tiny fonts to make |
668 | * things fit. |
669 | * |
670 | * A better approach would be to use a more qualitative display than a |
671 | * quantitative one, emphasizing the logical structure of the drawing |
672 | * and not the actual sizes. |
673 | * |
674 | * This could be done by ranking vectors by current, average, maximum, |
675 | * etc. size, then let their size be determined by the amount of text |
676 | * that's needed and the size of subordinate vectors. One difficulty |
677 | * would be in making vectors with a fixed length ratio look correct, |
678 | * particularly 1:1. |
679 | * |
680 | * Furthermore, don't write on the vector but put the text horizontally |
681 | * on either the left or the right side. |
682 | * |
683 | * Frame references could be drawn by simply connecting a line to the |
684 | * area of the respective frame. And let's not forget that we also need |
685 | * to list the variables somewhere. |
686 | */ |
687 | return 0; |
688 | |
689 | while (frame) { |
690 | if (frame->name) |
691 | for (inst = pkg->insts[ip_frame]; inst; |
692 | inst = inst->next) |
693 | if (inst->u.frame.ref == frame) |
694 | goto found_frame; |
695 | frame = frame->next; |
696 | } |
697 | if (!frame) |
698 | return 1; |
699 | |
700 | found_frame: |
701 | border = PS_MEAS_TEXT_HEIGHT+PS_DIVIDER_WIDTH+PS_DIVIDER_BORDER/2; |
702 | x = (inst->bbox.max.x-inst->bbox.min.x)*zoom+2*border; |
703 | y = (inst->bbox.max.y-inst->bbox.min.y)*zoom+2*border; |
704 | if (!get_box(x, y, &xa, &ya)) |
705 | return 0; |
706 | |
707 | /* |
708 | * Recurse down first, so that we only draw something if we can be sure |
709 | * that all the rest can be drawn too. |
710 | */ |
711 | |
712 | ok = generate_frames(file, pkg, frame->next, zoom); |
713 | if (!ok) |
714 | return 0; |
715 | |
716 | |
717 | #if 1 |
718 | fprintf(file, "0 setlinewidth 0.8 setgray\n"); |
719 | fprintf(file, "%d %d moveto\n", xa+border, ya+border); |
720 | fprintf(file, "%d %d lineto\n", xa+x-border, ya+border); |
721 | fprintf(file, "%d %d lineto\n", xa+x-border, ya+y-border); |
722 | fprintf(file, "%d %d lineto\n", xa+border, ya+y-border); |
723 | fprintf(file, "closepath fill\n"); |
724 | #endif |
725 | cx = xa+x/2-(inst->bbox.min.x+inst->bbox.max.x)/2*zoom; |
726 | cy = ya+y/2-(inst->bbox.min.y+inst->bbox.max.y)/2*zoom; |
727 | |
728 | fprintf(file, "%% Frame %s\n", frame->name ? frame->name : "(root)"); |
729 | fprintf(file, "gsave %d %d translate\n", cx, cy); |
730 | ps_draw_frame(file, pkg, inst, zoom); |
731 | fprintf(file, "grestore\n"); |
732 | |
733 | return 1; |
734 | } |
735 | |
736 | |
737 | /* ----- Page level -------------------------------------------------------- */ |
738 | |
739 | |
740 | static void ps_hline(FILE *file, int y) |
741 | { |
742 | fprintf(file, "gsave %d setlinewidth\n", PS_DIVIDER_WIDTH); |
743 | fprintf(file, " %d %d moveto\n", -PAGE_HALF_WIDTH, y); |
744 | fprintf(file, " %d 0 rlineto stroke grestore\n", PAGE_HALF_WIDTH*2); |
745 | } |
746 | |
747 | |
748 | static void ps_header(FILE *file, const struct pkg *pkg) |
749 | { |
750 | fprintf(file, "gsave %d %d moveto\n", |
751 | -PAGE_HALF_WIDTH, PAGE_HALF_HEIGHT-PS_HEADER_HEIGHT); |
752 | fprintf(file, " /Helvetica-Bold findfont dup\n"); |
753 | fprintf(file, " "); |
754 | ps_string(file, pkg->name); |
755 | fprintf(file, " %d %d\n", PAGE_HALF_WIDTH, PS_HEADER_HEIGHT); |
756 | fprintf(file, " boxfont\n"); |
757 | fprintf(file, " "); |
758 | ps_string(file, pkg->name); |
759 | fprintf(file, " show grestore\n"); |
760 | |
761 | ps_hline(file, PAGE_HALF_HEIGHT-PS_HEADER_HEIGHT-PS_DIVIDER_BORDER); |
762 | } |
763 | |
764 | |
765 | static void ps_page(FILE *file, int page, const struct pkg *pkg) |
766 | { |
767 | fprintf(file, "%%%%Page: %d %d\n", page, page); |
768 | |
769 | fprintf(file, "%%%%BeginPageSetup\n"); |
770 | fprintf(file, |
771 | "currentpagedevice /PageSize get\n" |
772 | " aload pop\n" |
773 | " 2 div exch 2 div exch\n" |
774 | " translate\n" |
775 | " 72 %d div 1000 div dup scale\n", |
776 | (int) MIL_UNITS); |
777 | fprintf(file, "%%%%EndPageSetup\n"); |
778 | fprintf(file, "[ /Title "); |
779 | ps_string(file, pkg->name); |
780 | fprintf(file, " /OUT pdfmark\n"); |
781 | } |
782 | |
783 | |
784 | static void ps_unit(FILE *file, |
785 | unit_type x, unit_type y, unit_type w, unit_type h) |
786 | { |
787 | const char *s; |
788 | |
789 | switch (curr_unit) { |
790 | case curr_unit_mm: |
791 | s = "Dimensions in mm"; |
792 | break; |
793 | case curr_unit_mil: |
794 | s = "Dimensions in mil"; |
795 | break; |
796 | case curr_unit_auto: |
797 | return; |
798 | default: |
799 | abort(); |
800 | } |
801 | |
802 | fprintf(file, "gsave %d %d moveto\n", x, y); |
803 | fprintf(file, " /Helvetica findfont dup\n"); |
804 | fprintf(file, " "); |
805 | ps_string(file, s); |
806 | fprintf(file, " %d %d\n", w, h); |
807 | fprintf(file, " boxfont\n"); |
808 | fprintf(file, " "); |
809 | ps_string(file, s); |
810 | fprintf(file, " show grestore\n"); |
811 | } |
812 | |
813 | |
814 | static void ps_key(FILE *file, double w, double h, enum pad_type type) |
815 | { |
816 | char tmp[20]; /* @@@ plenty :) */ |
817 | double f = 32; |
818 | struct coord a, b; |
819 | unit_type key_w; |
820 | |
821 | key_w = (w-2*PS_KEY_GAP-PS_KEY_GAP*(pt_n-1))/pt_n; |
822 | a.x = b.x = (key_w+PS_KEY_GAP)*type-w/2+PS_KEY_GAP; |
823 | a.y = b.y = -h/2-PS_KEY_GAP; |
824 | b.x += key_w; |
825 | b.y -= PS_KEY_HEIGTH; |
826 | |
827 | a.x /= f; |
828 | a.y /= f; |
829 | b.x /= f; |
830 | b.y /= f; |
831 | |
832 | strcpy(tmp, pad_type_name(type)); |
833 | tmp[0] = toupper(tmp[0]); |
834 | fprintf(file, "gsave %f %f scale\n", f, f); |
835 | ps_filled_box(file, a, b, hatch(type)); |
836 | ps_outlined_text_in_rect(file, tmp, a, b); |
837 | fprintf(file, "grestore\n"); |
838 | } |
839 | |
840 | |
841 | static void ps_keys(FILE *file, double w, double h) |
842 | { |
843 | enum pad_type i; |
844 | |
845 | for (i = 0; i != pt_n; i++) |
846 | ps_key(file, w, h, i); |
847 | } |
848 | |
849 | |
850 | static void ps_package(FILE *file, const struct pkg *pkg, int page) |
851 | { |
852 | struct bbox bbox; |
853 | unit_type x, y; |
854 | unit_type w, h; |
855 | double f; |
856 | unit_type c, d; |
857 | int done; |
858 | |
859 | ps_page(file, page, pkg); |
860 | ps_header(file, pkg); |
861 | |
862 | x = 2*PAGE_HALF_WIDTH-2*PS_DIVIDER_BORDER; |
863 | y = PAGE_HALF_HEIGHT-PS_HEADER_HEIGHT-3*PS_DIVIDER_BORDER; |
864 | |
865 | bbox = inst_get_bbox(pkg); |
866 | w = 2*(-bbox.min.x > bbox.max.x ? -bbox.min.x : bbox.max.x); |
867 | h = 2*(-bbox.min.y > bbox.max.y ? -bbox.min.y : bbox.max.y); |
868 | |
869 | /* |
870 | * Zoom such that we can fit at least one drawing |
871 | */ |
872 | |
873 | if (w > x/2 || h > y) { |
874 | f = (double) x/w; |
875 | if ((double) y/h < f) |
876 | f = (double) y/h; |
877 | if (f > 1) |
878 | f = 1; |
879 | } else { |
880 | for (f = 20; f > 1; f--) |
881 | if (x/(f+2) >= w && y/f >= h) |
882 | break; |
883 | } |
884 | |
885 | /* |
886 | * Decide if we have room for two, one, or zero smaller views |
887 | */ |
888 | |
889 | c = y/2+PS_DIVIDER_BORDER; |
890 | active_params = postscript_params; |
891 | if (x/(f+2) >= w && y/3 > h) { |
892 | /* main drawing */ |
893 | fprintf(file, "gsave %d %d translate\n", |
894 | (int) (x/(f+2)*f/2)-PAGE_HALF_WIDTH, c); |
895 | ps_draw_package(file, pkg, f, 1); |
896 | |
897 | active_params = minimal_params; |
898 | |
899 | /* divider */ |
900 | d = PAGE_HALF_WIDTH-2*x/(f+2); |
901 | fprintf(file, "grestore gsave %d setlinewidth\n", |
902 | PS_DIVIDER_WIDTH); |
903 | fprintf(file, " %d %d moveto 0 %d rlineto stroke\n", |
904 | d-PS_DIVIDER_BORDER, PS_DIVIDER_BORDER, y); |
905 | |
906 | /* x1 package */ |
907 | fprintf(file, "grestore gsave %d %d translate\n", |
908 | (d+PAGE_HALF_WIDTH)/2, y/6*5+PS_DIVIDER_BORDER); |
909 | ps_draw_package(file, pkg, 1, 1); |
910 | |
911 | /* x2 package */ |
912 | fprintf(file, "grestore gsave %d %d translate\n", |
913 | (d+PAGE_HALF_WIDTH)/2, y/3+PS_DIVIDER_BORDER); |
914 | ps_draw_package(file, pkg, 2, 1); |
915 | } else if (x/(f+1) >= w && y/2 > h) { |
916 | /* main drawing */ |
917 | fprintf(file, "gsave %d %d translate\n", |
918 | (int) (x/(f+1)*f/2)-PAGE_HALF_WIDTH, c); |
919 | ps_draw_package(file, pkg, f, 1); |
920 | |
921 | active_params = minimal_params; |
922 | |
923 | /* divider */ |
924 | d = PAGE_HALF_WIDTH-x/(f+1); |
925 | fprintf(file, "grestore gsave %d setlinewidth\n", |
926 | PS_DIVIDER_WIDTH); |
927 | fprintf(file, " %d %d moveto 0 %d rlineto stroke\n", |
928 | d-PS_DIVIDER_BORDER, PS_DIVIDER_BORDER, y); |
929 | |
930 | /* x1 package */ |
931 | fprintf(file, "grestore gsave %d %d translate\n", |
932 | (d+PAGE_HALF_WIDTH)/2, c); |
933 | ps_draw_package(file, pkg, 1, 1); |
934 | } else { |
935 | fprintf(file, "gsave 0 %d translate\n", c); |
936 | ps_draw_package(file, pkg, f, 1); |
937 | } |
938 | fprintf(file, "grestore\n"); |
939 | |
940 | ps_unit(file, -PAGE_HALF_WIDTH, PS_DIVIDER_BORDER, PAGE_HALF_WIDTH, |
941 | PS_MISC_TEXT_HEIGHT); |
942 | ps_hline(file, 0); |
943 | |
944 | /* |
945 | * Put the frames |
946 | * |
947 | * @@@ is it really a good idea to use the same zoom for all of them ? |
948 | */ |
949 | |
950 | active_params.show_stuff = 1; |
951 | active_params.label_vecs = 1; |
952 | for (f = 20; f >= 0.1; f = f > 1 ? f-1 : f-0.1) { |
953 | add_box(-PAGE_HALF_WIDTH, -PAGE_HALF_HEIGHT, PAGE_HALF_WIDTH, |
954 | -PS_DIVIDER_BORDER); |
955 | done = generate_frames(file, pkg, frames, f); |
956 | free_boxes(); |
957 | if (done) |
958 | break; |
959 | } |
960 | |
961 | fprintf(file, "showpage\n"); |
962 | } |
963 | |
964 | |
965 | /* ----- File level -------------------------------------------------------- */ |
966 | |
967 | |
968 | static void prologue(FILE *file, int pages) |
969 | { |
970 | fprintf(file, "%%!PS-Adobe-3.0\n"); |
971 | fprintf(file, "%%%%Pages: %d\n", pages); |
972 | fprintf(file, "%%%%EndComments\n"); |
973 | |
974 | fprintf(file, "%%%%BeginDefaults\n"); |
975 | fprintf(file, "%%%%PageResources: font Helvetica Helvetica-Bold\n"); |
976 | fprintf(file, "%%%%EndDefaults\n"); |
977 | |
978 | fprintf(file, "%%%%BeginProlog\n"); |
979 | |
980 | fprintf(file, |
981 | "/dotpath {\n" |
982 | " gsave flattenpath pathbbox clip newpath\n" |
983 | " 1 setlinecap %d setlinewidth\n" |
984 | " /ury exch def /urx exch def /lly exch def /llx exch def\n" |
985 | " llx %d urx {\n" |
986 | " lly %d ury {\n" |
987 | " 1 index exch moveto 0 0 rlineto stroke\n" |
988 | " } for\n" |
989 | " } for\n" |
990 | " grestore newpath } def\n", PS_DOT_DIAM, PS_DOT_DIST, PS_DOT_DIST); |
991 | |
992 | fprintf(file, |
993 | "/hatchpath {\n" |
994 | " gsave flattenpath pathbbox clip newpath\n" |
995 | " /ury exch def /urx exch def /lly exch def /llx exch def\n" |
996 | " lly ury sub %d urx llx sub {\n" /* for -(ury-lly) to urx-llx */ |
997 | " llx add dup lly moveto\n" |
998 | " ury lly sub add ury lineto stroke\n" |
999 | " } for\n" |
1000 | " grestore newpath } def\n", PS_HATCH); |
1001 | |
1002 | fprintf(file, |
1003 | "/backhatchpath {\n" |
1004 | " gsave flattenpath pathbbox clip newpath\n" |
1005 | " /ury exch def /urx exch def /lly exch def /llx exch def\n" |
1006 | " 0 %d ury lly sub urx llx sub add {\n" /* for 0 to urx-llx+ury-lly */ |
1007 | " llx add dup lly moveto\n" |
1008 | " ury lly sub sub ury lineto stroke\n" |
1009 | " } for\n" |
1010 | " grestore newpath } def\n", PS_HATCH); |
1011 | |
1012 | fprintf(file, |
1013 | "/crosspath {\n" |
1014 | " gsave hatchpath grestore backhatchpath } def\n"); |
1015 | |
1016 | fprintf(file, |
1017 | "/horpath {\n" |
1018 | " gsave flattenpath pathbbox clip newpath\n" |
1019 | " /ury exch def /urx exch def /lly exch def /llx exch def\n" |
1020 | " lly %d ury {\n" /* for lly to ury */ |
1021 | " dup llx exch moveto\n" |
1022 | " urx exch lineto stroke\n" |
1023 | " } for\n" |
1024 | " grestore newpath } def\n", PS_STRIPE); |
1025 | |
1026 | /* |
1027 | * Stack: font string width height factor -> factor |
1028 | * |
1029 | * Hack: sometimes, scalefont can't produce a suitable font and just |
1030 | * gives us something zero-sized, which trips the division. We just |
1031 | * ignore this case for now. Since maxfont is used in pairs, the |
1032 | * second one may still succeed. |
1033 | */ |
1034 | |
1035 | fprintf(file, |
1036 | "/sdiv { dup 0 eq { pop 1 } if div } def\n" |
1037 | "/maxfont {\n" |
1038 | " gsave 0 0 moveto\n" |
1039 | " /f exch def /h exch def /w exch def\n" |
1040 | " exch f scalefont setfont\n" |
1041 | " false charpath flattenpath pathbbox\n" |
1042 | " /ury exch def /urx exch def /lly exch def /llx exch def\n" |
1043 | " w urx llx sub sdiv h ury lly sub sdiv 2 copy gt { exch } if pop\n" |
1044 | " f mul grestore } def\n"); |
1045 | |
1046 | /* |
1047 | * Unrotate: - -> - |
1048 | */ |
1049 | |
1050 | fprintf(file, |
1051 | "/getscale { matrix currentmatrix dup 0 get dup mul exch 1 get dup mul\n" |
1052 | " add sqrt } def\n"); |
1053 | |
1054 | /* |
1055 | * Stack: string -> string |
1056 | */ |
1057 | |
1058 | fprintf(file, |
1059 | "/center {\n" |
1060 | " currentpoint /y exch def /x exch def\n" |
1061 | " gsave dup false charpath flattenpath pathbbox\n" |
1062 | " /ury exch def /urx exch def\n" |
1063 | " /lly exch def /llx exch def\n" |
1064 | " grestore\n" |
1065 | " x llx urx add 2 div sub y lly ury add 2 div sub rmoveto } def\n"); |
1066 | |
1067 | /* |
1068 | * Stack: string dist -> string |
1069 | */ |
1070 | |
1071 | fprintf(file, |
1072 | "/hcenter {\n" |
1073 | " /off exch def\n" |
1074 | " gsave matrix setmatrix dup false charpath flattenpath pathbbox\n" |
1075 | " /ury exch def /urx exch def /lly exch def /llx exch def\n" |
1076 | " grestore\n" |
1077 | //" /currscale getscale def\n" |
1078 | " llx urx sub 2 div\n" |
1079 | //" off lly sub rmoveto } def\n"); |
1080 | " off rmoveto } def\n"); |
1081 | |
1082 | /* |
1083 | * Stack: string outline_width -> - |
1084 | */ |
1085 | |
1086 | fprintf(file, |
1087 | "/showoutlined {\n" |
1088 | " gsave 2 mul setlinewidth 1 setgray 1 setlinejoin\n" |
1089 | " dup false charpath flattenpath stroke grestore\n" |
1090 | " show } def\n"); |
1091 | |
1092 | /* |
1093 | * Stack: string -> string |
1094 | */ |
1095 | |
1096 | fprintf(file, |
1097 | "/debugbox { gsave dup false charpath flattenpath pathbbox\n" |
1098 | " /ury exch def /urx exch def /lly exch def /llx exch def\n" |
1099 | " 0 setgray 100 setlinewidth\n" |
1100 | " llx lly urx llx sub ury lly sub rectstroke grestore } def\n"); |
1101 | |
1102 | /* |
1103 | * Stack: int -> int |
1104 | */ |
1105 | |
1106 | fprintf(file, |
1107 | "/originalsize 1 0 matrix currentmatrix idtransform pop def\n" |
1108 | "/realsize {\n" |
1109 | " 254 div 72 mul 1000 div 0 matrix currentmatrix idtransform\n" |
1110 | " dup mul exch dup mul add sqrt\n" |
1111 | " originalsize div } def\n"); |
1112 | |
1113 | /* |
1114 | * Stack: font string x-size y-size -> - |
1115 | */ |
1116 | |
1117 | fprintf(file, |
1118 | "/boxfont { 4 copy 1000 maxfont maxfont scalefont setfont } def\n"); |
1119 | |
1120 | /* |
1121 | * Ignore pdfmark. From |
1122 | * http://www.adobe.com/devnet/acrobat/pdfs/pdfmark_reference.pdf |
1123 | * Page 10, Example 1.1. |
1124 | */ |
1125 | |
1126 | fprintf(file, |
1127 | "/pdfmark where { pop }\n" |
1128 | " { /globaldict where { pop globaldict } { userdict } ifelse" |
1129 | " /pdfmark /cleartomark load put } ifelse\n"); |
1130 | |
1131 | fprintf(file, "%%%%EndProlog\n"); |
1132 | } |
1133 | |
1134 | |
1135 | static void epilogue(FILE *file) |
1136 | { |
1137 | fprintf(file, "%%%%EOF\n"); |
1138 | } |
1139 | |
1140 | |
1141 | static int ps_for_all_pkg(FILE *file, |
1142 | void (*fn)(FILE *file, const struct pkg *pkg, int page), |
1143 | const char *one) |
1144 | { |
1145 | struct pkg *pkg; |
1146 | int pages = 0; |
1147 | |
1148 | for (pkg = pkgs; pkg; pkg = pkg->next) |
1149 | if (pkg->name) |
1150 | if (!one || !strcmp(pkg->name, one)) |
1151 | pages++; |
1152 | if (one && !pages) { |
1153 | fprintf(stderr, "no package \"%s\" to select\n", one); |
1154 | errno = ENOENT; |
1155 | return 0; |
1156 | } |
1157 | prologue(file, pages); |
1158 | pages = 0; |
1159 | for (pkg = pkgs; pkg; pkg = pkg->next) |
1160 | if (pkg->name) |
1161 | if (!one || !strcmp(pkg->name, one)) |
1162 | fn(file, pkg, ++pages); |
1163 | epilogue(file); |
1164 | |
1165 | fflush(file); |
1166 | return !ferror(file); |
1167 | } |
1168 | |
1169 | |
1170 | int postscript(FILE *file, const char *one) |
1171 | { |
1172 | return ps_for_all_pkg(file, ps_package, one); |
1173 | } |
1174 | |
1175 | |
1176 | /* |
1177 | * Experimental. Doesn't work properly. |
1178 | */ |
1179 | |
1180 | static void ps_package_fullpage(FILE *file, const struct pkg *pkg, int page) |
1181 | { |
1182 | unit_type cx, cy; |
1183 | struct bbox bbox; |
1184 | double fx, fy, f; |
1185 | double w = 2.0*PAGE_HALF_WIDTH; |
1186 | double h = 2.0*PAGE_HALF_HEIGHT; |
1187 | |
1188 | ps_page(file, page, pkg); |
1189 | active_params = postscript_params; |
1190 | bbox = inst_get_bbox(pkg); |
1191 | cx = (bbox.min.x+bbox.max.x)/2; |
1192 | cy = (bbox.min.y+bbox.max.y)/2; |
1193 | if (active_params.zoom) { |
1194 | f = active_params.zoom; |
1195 | } else { |
1196 | if (active_params.max_width) |
1197 | w = active_params.max_width; |
1198 | fx = w/(bbox.max.x-bbox.min.x); |
1199 | if (active_params.max_height) |
1200 | h = active_params.max_height; |
1201 | if (active_params.show_key) |
1202 | h -= 2*PS_KEY_HEIGTH; |
1203 | fy = h/(bbox.max.y-bbox.min.y); |
1204 | f = fx < fy ? fx : fy; |
1205 | } |
1206 | fprintf(file, "gsave\n"); |
1207 | fprintf(file, "%d %d translate\n", (int) (-cx*f), (int) (-cy*f)); |
1208 | ps_draw_package(file, pkg, f, 0); |
1209 | fprintf(file, "grestore\n"); |
1210 | if (active_params.show_key) |
1211 | ps_keys(file, w, h); |
1212 | fprintf(file, "showpage\n"); |
1213 | } |
1214 | |
1215 | |
1216 | int postscript_fullpage(FILE *file, const char *one) |
1217 | { |
1218 | return ps_for_all_pkg(file, ps_package_fullpage, one); |
1219 | } |
1220 |
Branches:
master