Root/postscript.c

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

Archive Download this file

Branches:
master



interactive