postscript.c 30 KB

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