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