12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235 |
- /*
- * postscript.c - Dump objects in Postscript
- *
- * Written 2009-2012 by Werner Almesberger
- * Copyright 2009-2012 by Werner Almesberger
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- #include <errno.h>
- #include "util.h"
- #include "coord.h"
- #include "layer.h"
- #include "obj.h"
- #include "inst.h"
- #include "unparse.h"
- #include "gui_status.h"
- #include "gui_inst.h"
- #include "postscript.h"
- /*
- * A4 is 210 mm x 297 mm
- * US Letter is 216 mm x 279 mm
- *
- * We pick the smallest dimensions minus a bit of slack and center on the
- * printer page.
- */
- #define PAGE_HALF_WIDTH mm_to_units(210/2.0-10) /* A4 */
- #define PAGE_HALF_HEIGHT mm_to_units(279/2.0-15) /* US Letter */
- /*
- * Page layout:
- *
- * HEADER DATE
- * --------------------------- HEADER_HEIGHT+DIVIDER_BORDER below top
- * |
- * | 2x
- * 10 x |<------------- roughly at 10/12
- * | 1x
- * |
- * --------------------------- 50% height
- * Frames in boxes
- *
- */
- #define PS_HEADER_HEIGHT mm_to_units(8)
- #define PS_DIVIDER_BORDER mm_to_units(2)
- #define PS_DIVIDER_WIDTH mm_to_units(0.5)
- #define PS_MISC_TEXT_HEIGHT mm_to_units(3)
- #define PS_DOT_DIST mm_to_units(0.03)
- #define PS_DOT_DIAM mm_to_units(0.01)
- #define PS_HATCH mm_to_units(0.1)
- #define PS_HATCH_LINE mm_to_units(0.015)
- #define PS_STRIPE mm_to_units(0.08)
- #define PS_RIM_LINE mm_to_units(0.02)
- #define PS_FONT_OUTLINE mm_to_units(0.025)
- #define PS_VEC_LINE mm_to_units(0.02)
- #define PS_VEC_ARROW_LEN mm_to_units(0.3)
- #define PS_VEC_ARROW_ANGLE 20
- #define PS_VEC_TEXT_HEIGHT mm_to_units(3) /* ~8.5 pt, real mm */
- #define PS_VEC_BASE_OFFSET mm_to_units(0.5) /* real mm */
- #define PS_MEAS_LINE mm_to_units(0.1) /* real mm */
- #define PS_MEAS_ARROW_LEN mm_to_units(0.15)
- #define PS_MEAS_ARROW_ANGLE 30
- #define PS_MEAS_TEXT_HEIGHT mm_to_units(3) /* ~8.5 pt, real mm */
- #define PS_MEAS_BASE_OFFSET mm_to_units(0.5) /* real mm */
- #define PS_MEAS_MIN_HEIGHT (PS_MEAS_TEXT_HEIGHT/2)
- #define PS_CROSS_WIDTH mm_to_units(0.01)
- #define PS_CROSS_DASH mm_to_units(0.1)
- #define PS_KEY_X_GAP mm_to_units(8)
- #define PS_KEY_Y_GAP mm_to_units(4)
- #define PS_KEY_HEIGTH mm_to_units(8)
- #define TEXT_HEIGHT_FACTOR 1.5 /* height/width of typical text */
- struct postscript_params postscript_params = {
- .zoom = 0,
- .max_width = 0,
- .max_height = 0,
- .show_pad_names = 1,
- .show_stuff = 0,
- .label_vecs = 0,
- .show_meas = 1,
- .show_key = 0,
- };
- static const struct postscript_params minimal_params;
- static struct postscript_params active_params;
- static int pad_type_seen[pt_n];
- /* ----- Boxes ------------------------------------------------------------- */
- static struct box {
- unit_type x, y; /* width and height */
- unit_type x0, y0; /* lower left corner */
- struct box *next;
- } *boxes = NULL;
- static void add_box(unit_type xa, unit_type ya, unit_type xb, unit_type yb)
- {
- struct box *box;
- box = alloc_type(struct box);
- box->x = xb-xa;
- box->y = yb-ya;
- box->x0 = xa;
- box->y0 = ya;
- box->next = boxes;
- boxes = box;
- }
- static void free_boxes(void)
- {
- struct box *next;
- while (boxes) {
- next = boxes->next;
- free(boxes);
- boxes = next;
- }
- }
- static int get_box(unit_type x, unit_type y, unit_type *xa, unit_type *ya)
- {
- struct box **box, **best = NULL;
- struct box *b;
- double size, best_size;
- for (box = &boxes; *box; box = &(*box)->next) {
- if ((*box)->x < x || (*box)->y < y)
- continue;
- size = (double) (*box)->x*(*box)->y;
- if (!best || size < best_size) {
- best = box;
- best_size = size;
- }
- }
- if (!best)
- return 0;
- b = *best;
- if (xa)
- *xa = b->x0;
- if (ya)
- *ya = b->y0+b->y-y;
- *best = b->next;
- add_box(b->x0+x, b->y0, b->x0+b->x, b->y0+b->y);
- add_box(b->x0, b->y0, b->x0+x, b->y0+b->y-y);
- free(b);
- return 1;
- }
- /* ----- Helper functions -------------------------------------------------- */
- static void ps_string(FILE *file, const char *s)
- {
- fputc('(', file);
- while (*s) {
- if (*s == '(' || *s == ')' || *s == '\\')
- fputc('\\', file);
- fputc(*s, file);
- s++;
- }
- fputc(')', file);
- }
- static void ps_filled_box(FILE *file, struct coord a, struct coord b,
- const char *pattern)
- {
- fprintf(file, "0 setgray %d setlinewidth\n", PS_HATCH_LINE);
- fprintf(file, " %d %d moveto\n", a.x, a.y);
- fprintf(file, " %d %d lineto\n", b.x, a.y);
- fprintf(file, " %d %d lineto\n", b.x, b.y);
- fprintf(file, " %d %d lineto\n", a.x, b.y);
- fprintf(file, " closepath gsave %s grestore stroke\n", pattern);
- }
- static void ps_outlined_text_in_rect(FILE *file, const char *s,
- struct coord a, struct coord b)
- {
- const char *t;
- unit_type h, w;
- for (t = s; *t == ' '; t++);
- if (!*t)
- return;
- h = a.y-b.y;
- w = a.x-b.x;
- if (h < 0)
- h = -h;
- if (w < 0)
- w = -w;
- fprintf(file, "0 setgray /Helvetica-Bold findfont dup\n");
- fprintf(file, " ");
- ps_string(file, s);
- fprintf(file, " %d %d\n", w/2, h/2);
- fprintf(file, " boxfont\n");
- fprintf(file, " %d %d moveto\n", (a.x+b.x)/2, (a.y+b.y)/2);
- fprintf(file, " ");
- ps_string(file, s);
- fprintf(file, " center %d showoutlined newpath\n", PS_FONT_OUTLINE);
- }
- /* ----- Items ------------------------------------------------------------- */
- static void ps_pad_name(FILE *file, const struct inst *inst)
- {
- ps_outlined_text_in_rect(file, inst->u.pad.name,
- inst->base, inst->u.pad.other);
- }
- static const char *hatch(enum pad_type type)
- {
- switch (type) {
- case pt_normal:
- return "crosspath";
- case pt_bare:
- return "hatchpath";
- case pt_paste:
- return "backhatchpath";
- case pt_mask:
- return "dotpath";
- case pt_trace:
- return "horpath";
- default:
- abort();
- }
- }
- static void ps_pad(FILE *file, const struct inst *inst, int show_name)
- {
- enum pad_type type = layers_to_pad_type(inst->u.pad.layers);
- pad_type_seen[type] = 1;
- ps_filled_box(file, inst->base, inst->u.pad.other, hatch(type));
- if (show_name && !inst->u.pad.hole)
- ps_pad_name(file, inst);
- }
- static void ps_rounded_rect(FILE *file, struct coord a, struct coord b)
- {
- unit_type h, w, r;
- sort_coord(&a, &b);
- h = b.y-a.y;
- w = b.x-a.x;
- if (h > w) {
- r = w/2;
- fprintf(file, " %d %d moveto\n", b.x, b.y-r);
- fprintf(file, " %d %d %d 0 180 arc\n", a.x+r, b.y-r, r);
- fprintf(file, " %d %d lineto\n", a.x, a.y+r);
- fprintf(file, " %d %d %d 180 360 arc\n", a.x+r, a.y+r, r);
- } else {
- r = h/2;
- fprintf(file, " %d %d moveto\n", b.x-r, a.y);
- fprintf(file, " %d %d %d -90 90 arc\n", b.x-r, a.y+r, r);
- fprintf(file, " %d %d lineto\n", a.x+r, b.y);
- fprintf(file, " %d %d %d 90 270 arc\n", a.x+r, a.y+r, r);
- }
- }
- static void ps_rpad(FILE *file, const struct inst *inst, int show_name)
- {
- enum pad_type type = layers_to_pad_type(inst->u.pad.layers);
- pad_type_seen[type] = 1;
- fprintf(file, "0 setgray %d setlinewidth\n", PS_HATCH_LINE);
- ps_rounded_rect(file, inst->base, inst->u.pad.other);
- fprintf(file, " closepath gsave %s grestore stroke\n", hatch(type));
- if (show_name && !inst->u.pad.hole)
- ps_pad_name(file, inst);
- }
- static void ps_hole(FILE *file, const struct inst *inst, int show_name)
- {
- fprintf(file, "1 setgray %d setlinewidth\n", PS_RIM_LINE);
- ps_rounded_rect(file, inst->base, inst->u.hole.other);
- fprintf(file, " closepath gsave fill grestore\n");
- fprintf(file, " 0 setgray stroke\n");
- if (show_name && inst->u.hole.pad)
- ps_pad_name(file, inst->u.hole.pad);
- }
- static void ps_line(FILE *file, const struct inst *inst)
- {
- struct coord a = inst->base;
- struct coord b = inst->u.rect.end;
- fprintf(file, "1 setlinecap 0.5 setgray %d setlinewidth\n",
- inst->u.rect.width);
- fprintf(file, " %d %d moveto %d %d lineto stroke\n",
- a.x, a.y, b.x, b.y);
- }
- static void ps_rect(FILE *file, const struct inst *inst)
- {
- struct coord a = inst->base;
- struct coord b = inst->u.rect.end;
- fprintf(file, "1 setlinecap 0.5 setgray %d setlinewidth\n",
- inst->u.rect.width);
- fprintf(file, " %d %d moveto\n", a.x, a.y);
- fprintf(file, " %d %d lineto\n", b.x, a.y);
- fprintf(file, " %d %d lineto\n", b.x, b.y);
- fprintf(file, " %d %d lineto\n", a.x, b.y);
- fprintf(file, " closepath stroke\n");
- }
- static void ps_arc(FILE *file, const struct inst *inst)
- {
- double a1, a2;
- a1 = inst->u.arc.a1;
- a2 = inst->u.arc.a2;
- if (a2 <= a1)
- a2 += 360;
- fprintf(file, "1 setlinecap 0.5 setgray %d setlinewidth\n",
- inst->u.arc.width);
- fprintf(file, " newpath %d %d %d %f %f arc stroke\n",
- inst->base.x, inst->base.y, inst->u.arc.r, a1, a2);
- }
- static void ps_frame(FILE *file, const struct inst *inst)
- {
- }
- static void ps_arrow(FILE *file, struct coord from, struct coord to, int len,
- int angle)
- {
- struct coord side, p;
- if (from.x == to.x && from.y == to.y) {
- side.x = 0;
- side.y = -len;
- } else {
- side = normalize(sub_vec(to, from), len);
- }
- p = add_vec(to, rotate(side, 180-angle));
- fprintf(file, " %d %d moveto\n", p.x, p.y);
- fprintf(file, " %d %d lineto\n", to.x, to.y);
- p = add_vec(to, rotate(side, 180+angle));
- fprintf(file, " %d %d moveto\n", p.x, p.y);
- fprintf(file, " %d %d lineto\n", to.x, to.y);
- fprintf(file, " stroke\n");
- }
- static void ps_vec(FILE *file, const struct inst *inst)
- {
- struct coord a, b, c, d;
- char *s, *sx, *sy;
- a = inst->base;
- b = inst->u.vec.end;
- fprintf(file, "1 setlinecap 0 setgray %d setlinewidth\n", PS_VEC_LINE);
- fprintf(file, " %d %d moveto\n", a.x, a.y);
- fprintf(file, " %d %d lineto\n", b.x, b.y);
- fprintf(file, " stroke\n");
- ps_arrow(file, a, b, PS_VEC_ARROW_LEN, PS_VEC_ARROW_ANGLE);
- if (!active_params.label_vecs)
- return;
- sx = unparse(inst->vec->x);
- sy = unparse(inst->vec->y);
- s = stralloc_printf("(%s, %s)", sx, sy);
- free(sx);
- free(sy);
- c = add_vec(a, b);
- d = sub_vec(b, a);
- fprintf(file, "gsave %d %d moveto\n", c.x/2, c.y/2);
- fprintf(file, " /Helvetica-Bold findfont dup\n");
- fprintf(file, " ");
- ps_string(file, s);
- fprintf(file, " %d %d realsize\n",
- (int) (dist_point(a, b)-2*PS_VEC_ARROW_LEN),
- PS_VEC_TEXT_HEIGHT);
- fprintf(file, " boxfont\n");
- fprintf(file, " %f rotate\n", atan2(d.y, d.x)/M_PI*180);
- fprintf(file, " ");
- ps_string(file, s);
- fprintf(file, " %d realsize pop 0 hcenter\n", PS_VEC_BASE_OFFSET);
- fprintf(file, " show grestore\n");
- free(s);
- }
- /* ----- Measurements ------------------------------------------------------ */
- static unit_type guesstimate_text_height(const char *s, unit_type width,
- double zoom)
- {
- return width/strlen(s)*TEXT_HEIGHT_FACTOR*zoom;
- }
- static void ps_meas(FILE *file, const struct inst *inst,
- enum curr_unit unit, double zoom)
- {
- struct coord a0, b0, a1, b1;
- struct coord c, d;
- char *s;
- unit_type height, width, offset;
- a0 = inst->base;
- b0 = inst->u.meas.end;
- project_meas(inst, &a1, &b1);
- fprintf(file, "1 setlinecap 0 setgray %d realsize setlinewidth\n",
- PS_MEAS_LINE);
- fprintf(file, " %d %d moveto\n", a0.x, a0.y);
- fprintf(file, " %d %d lineto\n", a1.x, a1.y);
- fprintf(file, " %d %d lineto\n", b1.x, b1.y);
- fprintf(file, " %d %d lineto\n", b0.x, b0.y);
- fprintf(file, " stroke\n");
- ps_arrow(file, a1, b1, PS_MEAS_ARROW_LEN, PS_MEAS_ARROW_ANGLE);
- ps_arrow(file, b1, a1, PS_MEAS_ARROW_LEN, PS_MEAS_ARROW_ANGLE);
- s = format_len(inst->obj->u.meas.label ? inst->obj->u.meas.label : "",
- dist_point(a1, b1), unit);
- c = add_vec(a1, b1);
- d = sub_vec(b1, a1);
- /*
- * First try: put text between the arrows
- */
- width = dist_point(a1, b1)-1.5*PS_MEAS_ARROW_LEN;
- offset = PS_MEAS_BASE_OFFSET;
- height = 0;
- if (guesstimate_text_height(s, width, zoom) < PS_MEAS_MIN_HEIGHT) {
- #if 0
- fprintf(stderr, "%s -> width %d height %d vs. %d\n",
- s, width, guesstimate_text_height(s, width, zoom), PS_MEAS_MIN_HEIGHT);
- #endif
- /*
- * Second try: push it above the arrows
- */
- width = dist_point(a1, b1);
- offset +=
- PS_MEAS_ARROW_LEN*sin(PS_MEAS_ARROW_ANGLE*M_PI/180)*zoom;
- if (guesstimate_text_height(s, width, zoom) <
- PS_MEAS_MIN_HEIGHT) {
- height = PS_MEAS_MIN_HEIGHT;
- width = strlen(s)*height;
- }
- }
- if (height) {
- fprintf(file, "gsave %d %d moveto\n", c.x/2, c.y/2);
- fprintf(file, " /Helvetica-Bold findfont dup\n");
- fprintf(file, " ");
- ps_string(file, s);
- fprintf(file, " %d realsize %d realsize\n", width, height);
- fprintf(file, " boxfont\n");
- fprintf(file, " %f rotate\n", atan2(d.y, d.x)/M_PI*180);
- fprintf(file, " ");
- ps_string(file, s);
- fprintf(file, " %d realsize hcenter\n", offset);
- fprintf(file, " show grestore\n");
- } else {
- fprintf(file, "gsave %d %d moveto\n", c.x/2, c.y/2);
- fprintf(file, " /Helvetica-Bold findfont dup\n");
- fprintf(file, " ");
- ps_string(file, s);
- fprintf(file, " %d %d realsize\n", width, PS_MEAS_TEXT_HEIGHT);
- fprintf(file, " boxfont\n");
- fprintf(file, " %f rotate\n", atan2(d.y, d.x)/M_PI*180);
- fprintf(file, " ");
- ps_string(file, s);
- fprintf(file, " %d realsize hcenter\n", offset);
- fprintf(file, " show grestore\n");
- }
- free(s);
- }
- /* ----- Print layers ------------------------------------------------------ */
- static void ps_background(FILE *file, enum inst_prio prio,
- const struct inst *inst)
- {
- switch (prio) {
- case ip_line:
- ps_line(file, inst);
- break;
- case ip_rect:
- ps_rect(file, inst);
- break;
- case ip_circ:
- case ip_arc:
- ps_arc(file, inst);
- break;
- default:
- break;
- }
- }
- static void ps_foreground(FILE *file, enum inst_prio prio,
- const struct inst *inst, double zoom)
- {
- switch (prio) {
- case ip_pad_copper:
- case ip_pad_special:
- if (inst->obj->u.pad.rounded)
- ps_rpad(file, inst, active_params.show_pad_names);
- else
- ps_pad(file, inst, active_params.show_pad_names);
- break;
- case ip_hole:
- ps_hole(file, inst, active_params.show_pad_names);
- break;
- case ip_vec:
- if (active_params.show_stuff)
- ps_vec(file, inst);
- break;
- case ip_frame:
- if (active_params.show_stuff)
- ps_frame(file, inst);
- break;
- case ip_meas:
- if (active_params.show_meas)
- ps_meas(file, inst, curr_unit, zoom);
- break;
- default:
- break;
- }
- }
- /* ----- Package level ----------------------------------------------------- */
- static void ps_cross(FILE *file, const struct inst *inst)
- {
- fprintf(file, "gsave 0 setgray %d setlinewidth\n", PS_CROSS_WIDTH);
- fprintf(file, " [%d] 0 setdash\n", PS_CROSS_DASH);
- fprintf(file, " %d 0 moveto %d 0 lineto\n",
- inst->bbox.min.x, inst->bbox.max.x);
- fprintf(file, " 0 %d moveto 0 %d lineto\n",
- inst->bbox.min.y, inst->bbox.max.y);
- fprintf(file, " stroke grestore\n");
- }
- static void ps_draw_package(FILE *file, const struct pkg *pkg, double zoom,
- int cross)
- {
- enum inst_prio prio;
- const struct inst *inst;
- fprintf(file, "gsave %f dup scale\n", zoom);
- if (cross)
- ps_cross(file, pkgs->insts[ip_frame]);
- FOR_INST_PRIOS_UP(prio) {
- FOR_PKG_INSTS(pkgs, prio, inst)
- ps_background(file, prio, inst);
- FOR_PKG_INSTS(pkg, prio, inst)
- ps_background(file, prio, inst);
- }
- FOR_INST_PRIOS_UP(prio) {
- FOR_PKG_INSTS(pkgs, prio, inst)
- ps_foreground(file, prio, inst, zoom);
- FOR_PKG_INSTS(pkg, prio, inst)
- ps_foreground(file, prio, inst, zoom);
- }
- fprintf(file, "grestore\n");
- }
- /* ----- Object frames ----------------------------------------------------- */
- static void ps_draw_frame(FILE *file, const struct pkg *pkg,
- const struct inst *outer, double zoom)
- {
- enum inst_prio prio;
- const struct inst *inst;
- fprintf(file, "gsave %f dup scale\n", zoom);
- ps_cross(file, outer);
- FOR_INST_PRIOS_UP(prio) {
- FOR_PKG_INSTS(pkgs, prio, inst)
- if (inst->outer == outer)
- ps_background(file, prio, inst);
- FOR_PKG_INSTS(pkg, prio, inst)
- if (inst->outer == outer)
- ps_background(file, prio, inst);
- }
- FOR_INST_PRIOS_UP(prio) {
- FOR_PKG_INSTS(pkgs, prio, inst)
- if (inst->outer == outer)
- ps_foreground(file, prio, inst, zoom);
- FOR_PKG_INSTS(pkg, prio, inst)
- if (inst->outer == outer)
- ps_foreground(file, prio, inst, zoom);
- }
- fprintf(file, "grestore\n");
- }
- static int generate_frames(FILE *file, const struct pkg *pkg,
- const struct frame *frame, double zoom)
- {
- const struct inst *inst;
- unit_type x, y, xa, ya;
- unit_type cx, cy, border;
- int ok;
- /*
- * This doesn't work yet. The whole idea of just picking the current
- * instance of each object and drawing it is flawed, since we may have
- * very different sizes in a frame, so one big vector may dominate all
- * the finer details.
- *
- * Also, the amount of text can be large and force tiny fonts to make
- * things fit.
- *
- * A better approach would be to use a more qualitative display than a
- * quantitative one, emphasizing the logical structure of the drawing
- * and not the actual sizes.
- *
- * This could be done by ranking vectors by current, average, maximum,
- * etc. size, then let their size be determined by the amount of text
- * that's needed and the size of subordinate vectors. One difficulty
- * would be in making vectors with a fixed length ratio look correct,
- * particularly 1:1.
- *
- * Furthermore, don't write on the vector but put the text horizontally
- * on either the left or the right side.
- *
- * Frame references could be drawn by simply connecting a line to the
- * area of the respective frame. And let's not forget that we also need
- * to list the variables somewhere.
- */
- return 0;
- while (frame) {
- if (frame->name)
- for (inst = pkg->insts[ip_frame]; inst;
- inst = inst->next)
- if (inst->u.frame.ref == frame)
- goto found_frame;
- frame = frame->next;
- }
- if (!frame)
- return 1;
- found_frame:
- border = PS_MEAS_TEXT_HEIGHT+PS_DIVIDER_WIDTH+PS_DIVIDER_BORDER/2;
- x = (inst->bbox.max.x-inst->bbox.min.x)*zoom+2*border;
- y = (inst->bbox.max.y-inst->bbox.min.y)*zoom+2*border;
- if (!get_box(x, y, &xa, &ya))
- return 0;
- /*
- * Recurse down first, so that we only draw something if we can be sure
- * that all the rest can be drawn too.
- */
- ok = generate_frames(file, pkg, frame->next, zoom);
- if (!ok)
- return 0;
- #if 1
- fprintf(file, "0 setlinewidth 0.8 setgray\n");
- fprintf(file, "%d %d moveto\n", xa+border, ya+border);
- fprintf(file, "%d %d lineto\n", xa+x-border, ya+border);
- fprintf(file, "%d %d lineto\n", xa+x-border, ya+y-border);
- fprintf(file, "%d %d lineto\n", xa+border, ya+y-border);
- fprintf(file, "closepath fill\n");
- #endif
- cx = xa+x/2-(inst->bbox.min.x+inst->bbox.max.x)/2*zoom;
- cy = ya+y/2-(inst->bbox.min.y+inst->bbox.max.y)/2*zoom;
- fprintf(file, "%% Frame %s\n", frame->name ? frame->name : "(root)");
- fprintf(file, "gsave %d %d translate\n", cx, cy);
- ps_draw_frame(file, pkg, inst, zoom);
- fprintf(file, "grestore\n");
- return 1;
- }
- /* ----- Page level -------------------------------------------------------- */
- static void ps_hline(FILE *file, int y)
- {
- fprintf(file, "gsave %d setlinewidth\n", PS_DIVIDER_WIDTH);
- fprintf(file, " %d %d moveto\n", -PAGE_HALF_WIDTH, y);
- fprintf(file, " %d 0 rlineto stroke grestore\n", PAGE_HALF_WIDTH*2);
- }
- static void ps_header(FILE *file, const struct pkg *pkg)
- {
- fprintf(file, "gsave %d %d moveto\n",
- -PAGE_HALF_WIDTH, PAGE_HALF_HEIGHT-PS_HEADER_HEIGHT);
- fprintf(file, " /Helvetica-Bold findfont dup\n");
- fprintf(file, " ");
- ps_string(file, pkg->name);
- fprintf(file, " %d %d\n", PAGE_HALF_WIDTH, PS_HEADER_HEIGHT);
- fprintf(file, " boxfont\n");
- fprintf(file, " ");
- ps_string(file, pkg->name);
- fprintf(file, " show grestore\n");
- ps_hline(file, PAGE_HALF_HEIGHT-PS_HEADER_HEIGHT-PS_DIVIDER_BORDER);
- }
- static void ps_page(FILE *file, int page, const struct pkg *pkg)
- {
- fprintf(file, "%%%%Page: %d %d\n", page, page);
- fprintf(file, "%%%%BeginPageSetup\n");
- fprintf(file,
- "currentpagedevice /PageSize get\n"
- " aload pop\n"
- " 2 div exch 2 div exch\n"
- " translate\n"
- " 72 %d div 1000 div dup scale\n",
- (int) MIL_UNITS);
- fprintf(file, "%%%%EndPageSetup\n");
- fprintf(file, "[ /Title ");
- ps_string(file, pkg->name);
- fprintf(file, " /OUT pdfmark\n");
- }
- static void ps_unit(FILE *file,
- unit_type x, unit_type y, unit_type w, unit_type h)
- {
- const char *s;
- switch (curr_unit) {
- case curr_unit_mm:
- s = "Dimensions in mm";
- break;
- case curr_unit_mil:
- s = "Dimensions in mil";
- break;
- case curr_unit_auto:
- return;
- default:
- abort();
- }
- fprintf(file, "gsave %d %d moveto\n", x, y);
- fprintf(file, " /Helvetica findfont dup\n");
- fprintf(file, " ");
- ps_string(file, s);
- fprintf(file, " %d %d\n", w, h);
- fprintf(file, " boxfont\n");
- fprintf(file, " ");
- ps_string(file, s);
- fprintf(file, " show grestore\n");
- }
- static void ps_key(FILE *file, double w, double h, enum pad_type type)
- {
- char tmp[20]; /* @@@ plenty :) */
- double f = 32;
- struct coord a, b;
- unit_type key_w;
- key_w = (w-2*PS_KEY_X_GAP-PS_KEY_X_GAP*(pt_n-1))/pt_n;
- a.x = b.x = (key_w+PS_KEY_X_GAP)*type-w/2+PS_KEY_X_GAP;
- a.y = b.y = -h/2-PS_KEY_Y_GAP;
- b.x += key_w;
- b.y -= PS_KEY_HEIGTH;
- a.x /= f;
- a.y /= f;
- b.x /= f;
- b.y /= f;
- strcpy(tmp, pad_type_name(type));
- tmp[0] = toupper(tmp[0]);
- fprintf(file, "gsave %f %f scale\n", f, f);
- ps_filled_box(file, a, b, hatch(type));
- ps_outlined_text_in_rect(file, tmp, a, b);
- fprintf(file, "grestore\n");
- }
- static void ps_keys(FILE *file, double w, double h)
- {
- enum pad_type i;
- for (i = 0; i != pt_n; i++)
- if (pad_type_seen[i])
- ps_key(file, w, h, i);
- }
- static void ps_package(FILE *file, const struct pkg *pkg, int page)
- {
- struct bbox bbox;
- unit_type x, y;
- unit_type w, h;
- double f;
- unit_type c, d;
- int done;
- ps_page(file, page, pkg);
- ps_header(file, pkg);
- x = 2*PAGE_HALF_WIDTH-2*PS_DIVIDER_BORDER;
- y = PAGE_HALF_HEIGHT-PS_HEADER_HEIGHT-3*PS_DIVIDER_BORDER;
- bbox = inst_get_bbox(pkg);
- w = 2*(-bbox.min.x > bbox.max.x ? -bbox.min.x : bbox.max.x);
- h = 2*(-bbox.min.y > bbox.max.y ? -bbox.min.y : bbox.max.y);
- /*
- * Zoom such that we can fit at least one drawing
- */
- if (w > x/2 || h > y) {
- f = (double) x/w;
- if ((double) y/h < f)
- f = (double) y/h;
- if (f > 1)
- f = 1;
- } else {
- for (f = 20; f > 1; f--)
- if (x/(f+2) >= w && y/f >= h)
- break;
- }
- /*
- * Decide if we have room for two, one, or zero smaller views
- */
- c = y/2+PS_DIVIDER_BORDER;
- active_params = postscript_params;
- if (x/(f+2) >= w && y/3 > h) {
- /* main drawing */
- fprintf(file, "gsave %d %d translate\n",
- (int) (x/(f+2)*f/2)-PAGE_HALF_WIDTH, c);
- ps_draw_package(file, pkg, f, 1);
- active_params = minimal_params;
- /* divider */
- d = PAGE_HALF_WIDTH-2*x/(f+2);
- fprintf(file, "grestore gsave %d setlinewidth\n",
- PS_DIVIDER_WIDTH);
- fprintf(file, " %d %d moveto 0 %d rlineto stroke\n",
- d-PS_DIVIDER_BORDER, PS_DIVIDER_BORDER, y);
- /* x1 package */
- fprintf(file, "grestore gsave %d %d translate\n",
- (d+PAGE_HALF_WIDTH)/2, y/6*5+PS_DIVIDER_BORDER);
- ps_draw_package(file, pkg, 1, 1);
- /* x2 package */
- fprintf(file, "grestore gsave %d %d translate\n",
- (d+PAGE_HALF_WIDTH)/2, y/3+PS_DIVIDER_BORDER);
- ps_draw_package(file, pkg, 2, 1);
- } else if (x/(f+1) >= w && y/2 > h) {
- /* main drawing */
- fprintf(file, "gsave %d %d translate\n",
- (int) (x/(f+1)*f/2)-PAGE_HALF_WIDTH, c);
- ps_draw_package(file, pkg, f, 1);
- active_params = minimal_params;
- /* divider */
- d = PAGE_HALF_WIDTH-x/(f+1);
- fprintf(file, "grestore gsave %d setlinewidth\n",
- PS_DIVIDER_WIDTH);
- fprintf(file, " %d %d moveto 0 %d rlineto stroke\n",
- d-PS_DIVIDER_BORDER, PS_DIVIDER_BORDER, y);
- /* x1 package */
- fprintf(file, "grestore gsave %d %d translate\n",
- (d+PAGE_HALF_WIDTH)/2, c);
- ps_draw_package(file, pkg, 1, 1);
- } else {
- fprintf(file, "gsave 0 %d translate\n", c);
- ps_draw_package(file, pkg, f, 1);
- }
- fprintf(file, "grestore\n");
- ps_unit(file, -PAGE_HALF_WIDTH, PS_DIVIDER_BORDER, PAGE_HALF_WIDTH,
- PS_MISC_TEXT_HEIGHT);
- ps_hline(file, 0);
- /*
- * Put the frames
- *
- * @@@ is it really a good idea to use the same zoom for all of them ?
- */
- active_params.show_stuff = 1;
- active_params.label_vecs = 1;
- for (f = 20; f >= 0.1; f = f > 1 ? f-1 : f-0.1) {
- add_box(-PAGE_HALF_WIDTH, -PAGE_HALF_HEIGHT, PAGE_HALF_WIDTH,
- -PS_DIVIDER_BORDER);
- done = generate_frames(file, pkg, frames, f);
- free_boxes();
- if (done)
- break;
- }
- fprintf(file, "showpage\n");
- }
- /* ----- File level -------------------------------------------------------- */
- static void prologue(FILE *file, int pages)
- {
- fprintf(file, "%%!PS-Adobe-3.0\n");
- fprintf(file, "%%%%Pages: %d\n", pages);
- fprintf(file, "%%%%EndComments\n");
- fprintf(file, "%%%%BeginDefaults\n");
- fprintf(file, "%%%%PageResources: font Helvetica Helvetica-Bold\n");
- fprintf(file, "%%%%EndDefaults\n");
- fprintf(file, "%%%%BeginProlog\n");
- fprintf(file,
- "/dotpath {\n"
- " gsave flattenpath pathbbox clip newpath\n"
- " 1 setlinecap %d setlinewidth\n"
- " /ury exch def /urx exch def /lly exch def /llx exch def\n"
- " llx %d urx {\n"
- " lly %d ury {\n"
- " 1 index exch moveto 0 0 rlineto stroke\n"
- " } for\n"
- " } for\n"
- " grestore newpath } def\n", PS_DOT_DIAM, PS_DOT_DIST, PS_DOT_DIST);
- fprintf(file,
- "/hatchpath {\n"
- " gsave flattenpath pathbbox clip newpath\n"
- " /ury exch def /urx exch def /lly exch def /llx exch def\n"
- " lly ury sub %d urx llx sub {\n" /* for -(ury-lly) to urx-llx */
- " llx add dup lly moveto\n"
- " ury lly sub add ury lineto stroke\n"
- " } for\n"
- " grestore newpath } def\n", PS_HATCH);
- fprintf(file,
- "/backhatchpath {\n"
- " gsave flattenpath pathbbox clip newpath\n"
- " /ury exch def /urx exch def /lly exch def /llx exch def\n"
- " 0 %d ury lly sub urx llx sub add {\n" /* for 0 to urx-llx+ury-lly */
- " llx add dup lly moveto\n"
- " ury lly sub sub ury lineto stroke\n"
- " } for\n"
- " grestore newpath } def\n", PS_HATCH);
- fprintf(file,
- "/crosspath {\n"
- " gsave hatchpath grestore backhatchpath } def\n");
- fprintf(file,
- "/horpath {\n"
- " gsave flattenpath pathbbox clip newpath\n"
- " /ury exch def /urx exch def /lly exch def /llx exch def\n"
- " lly %d ury {\n" /* for lly to ury */
- " dup llx exch moveto\n"
- " urx exch lineto stroke\n"
- " } for\n"
- " grestore newpath } def\n", PS_STRIPE);
- /*
- * Stack: font string width height factor -> factor
- *
- * Hack: sometimes, scalefont can't produce a suitable font and just
- * gives us something zero-sized, which trips the division. We just
- * ignore this case for now. Since maxfont is used in pairs, the
- * second one may still succeed.
- */
- fprintf(file,
- "/sdiv { dup 0 eq { pop 1 } if div } def\n"
- "/maxfont {\n"
- " gsave 0 0 moveto\n"
- " /f exch def /h exch def /w exch def\n"
- " exch f scalefont setfont\n"
- " false charpath flattenpath pathbbox\n"
- " /ury exch def /urx exch def /lly exch def /llx exch def\n"
- " w urx llx sub sdiv h ury lly sub sdiv 2 copy gt { exch } if pop\n"
- " f mul grestore } def\n");
- /*
- * Unrotate: - -> -
- */
- fprintf(file,
- "/getscale { matrix currentmatrix dup 0 get dup mul exch 1 get dup mul\n"
- " add sqrt } def\n");
- /*
- * Stack: string -> string
- */
- fprintf(file,
- "/center {\n"
- " currentpoint /y exch def /x exch def\n"
- " gsave dup false charpath flattenpath pathbbox\n"
- " /ury exch def /urx exch def\n"
- " /lly exch def /llx exch def\n"
- " grestore\n"
- " x llx urx add 2 div sub y lly ury add 2 div sub rmoveto } def\n");
- /*
- * Stack: string dist -> string
- */
- fprintf(file,
- "/hcenter {\n"
- " /off exch def\n"
- " gsave matrix setmatrix dup false charpath flattenpath pathbbox\n"
- " /ury exch def /urx exch def /lly exch def /llx exch def\n"
- " grestore\n"
- //" /currscale getscale def\n"
- " llx urx sub 2 div\n"
- //" off lly sub rmoveto } def\n");
- " off rmoveto } def\n");
- /*
- * Stack: string outline_width -> -
- */
- fprintf(file,
- "/showoutlined {\n"
- " gsave 2 mul setlinewidth 1 setgray 1 setlinejoin\n"
- " dup false charpath flattenpath stroke grestore\n"
- " show } def\n");
- /*
- * Stack: string -> string
- */
- fprintf(file,
- "/debugbox { gsave dup false charpath flattenpath pathbbox\n"
- " /ury exch def /urx exch def /lly exch def /llx exch def\n"
- " 0 setgray 100 setlinewidth\n"
- " llx lly urx llx sub ury lly sub rectstroke grestore } def\n");
- /*
- * Stack: int -> int
- */
- fprintf(file,
- "/originalsize 1 0 matrix currentmatrix idtransform pop def\n"
- "/realsize {\n"
- " 254 div 72 mul 1000 div 0 matrix currentmatrix idtransform\n"
- " dup mul exch dup mul add sqrt\n"
- " originalsize div } def\n");
- /*
- * Stack: font string x-size y-size -> -
- */
- fprintf(file,
- "/boxfont { 4 copy 1000 maxfont maxfont scalefont setfont } def\n");
- /*
- * Ignore pdfmark. From
- * http://www.adobe.com/devnet/acrobat/pdfs/pdfmark_reference.pdf
- * Page 10, Example 1.1.
- */
- fprintf(file,
- "/pdfmark where { pop }\n"
- " { /globaldict where { pop globaldict } { userdict } ifelse"
- " /pdfmark /cleartomark load put } ifelse\n");
- fprintf(file, "%%%%EndProlog\n");
- }
- static void epilogue(FILE *file)
- {
- fprintf(file, "%%%%EOF\n");
- }
- static int ps_for_all_pkg(FILE *file,
- void (*fn)(FILE *file, const struct pkg *pkg, int page),
- const char *one)
- {
- struct pkg *pkg;
- int pages = 0;
- for (pkg = pkgs; pkg; pkg = pkg->next)
- if (pkg->name)
- if (!one || !strcmp(pkg->name, one))
- pages++;
- if (one && !pages) {
- fprintf(stderr, "no package \"%s\" to select\n", one);
- errno = ENOENT;
- return 0;
- }
- prologue(file, pages);
- pages = 0;
- for (pkg = pkgs; pkg; pkg = pkg->next)
- if (pkg->name)
- if (!one || !strcmp(pkg->name, one))
- fn(file, pkg, ++pages);
- epilogue(file);
- fflush(file);
- return !ferror(file);
- }
- int postscript(FILE *file, const char *one)
- {
- return ps_for_all_pkg(file, ps_package, one);
- }
- /*
- * Experimental. Doesn't work properly.
- */
- static void ps_package_fullpage(FILE *file, const struct pkg *pkg, int page)
- {
- unit_type cx, cy;
- struct bbox bbox;
- double fx, fy, f;
- double w = 2.0*PAGE_HALF_WIDTH;
- double h = 2.0*PAGE_HALF_HEIGHT;
- int yoff = 0;
- ps_page(file, page, pkg);
- active_params = postscript_params;
- bbox = inst_get_bbox(pkg);
- cx = (bbox.min.x+bbox.max.x)/2;
- cy = (bbox.min.y+bbox.max.y)/2;
- if (active_params.zoom) {
- f = active_params.zoom;
- } else {
- if (active_params.max_width)
- w = active_params.max_width;
- fx = w/(bbox.max.x-bbox.min.x);
- if (active_params.max_height)
- h = active_params.max_height;
- if (active_params.show_key) {
- yoff = PS_KEY_HEIGTH+PS_KEY_Y_GAP;
- h -= yoff;
- }
- fy = h/(bbox.max.y-bbox.min.y);
- f = fx < fy ? fx : fy;
- }
- fprintf(file, "gsave\n");
- fprintf(file, "%d %d translate\n", (int) (-cx*f), (int) (-cy*f)+yoff);
- memset(pad_type_seen, 0, sizeof(pad_type_seen));
- ps_draw_package(file, pkg, f, 0);
- fprintf(file, "grestore\n");
- if (active_params.show_key) {
- fprintf(file, "gsave 0 %d translate\n", yoff);
- ps_keys(file, w, h);
- fprintf(file, "grestore\n");
- }
- fprintf(file, "showpage\n");
- }
- int postscript_fullpage(FILE *file, const char *one)
- {
- return ps_for_all_pkg(file, ps_package_fullpage, one);
- }
|