Root/postscript.c

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

Archive Download this file

Branches:
master



interactive