1
0
mirror of git://projects.qi-hardware.com/fped.git synced 2024-11-05 08:27:09 +02:00
fped/postscript.c

790 lines
19 KiB
C
Raw Normal View History

/*
* postscript.c - Dump objects in Postscript
*
* Written 2009 by Werner Almesberger
* Copyright 2009 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 "util.h"
#include "coord.h"
#include "inst.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_FONT_OUTLINE mm_to_units(0.025)
#define PS_MEAS_LINE mm_to_units(0.015)
#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.5) /* ~10 pt, real mm */
#define PS_MEAS_BASE_OFFSET mm_to_units(0.5) /* real mm */
#define PS_CROSS_WIDTH mm_to_units(0.01)
#define PS_CROSS_DASH mm_to_units(0.1)
struct postscript_params postscript_params = {
.show_pad_names = 1,
.show_stuff = 0,
.label_vecs = 0,
.show_meas = 1,
};
static const struct postscript_params minimal_params;
static struct postscript_params active_params;
/* ----- 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;
*best = b->next;
add_box(b->x0+x, b->y0, b->x0+b->x, b->y0+y);
add_box(b->x0, b->y0+y, b->x0+b->x, b->y0+b->y);
free(b);
return 1;
}
/* ----- Items ------------------------------------------------------------- */
static void ps_pad_name(FILE *file, const struct inst *inst)
{
struct coord a = inst->base;
struct coord b = inst->u.pad.other;
unit_type h, w;
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, " (%s) %d %d\n", inst->u.pad.name, w/2, h/2);
fprintf(file, " 4 copy 1000 maxfont\n");
fprintf(file, " maxfont scalefont setfont\n");
fprintf(file, " %d %d moveto\n", (a.x+b.x)/2, (a.y+b.y)/2);
fprintf(file, " (%s) center %d showoutlined newpath\n",
inst->u.pad.name, PS_FONT_OUTLINE);
}
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";
default:
abort();
}
}
static void ps_pad(FILE *file, const struct inst *inst, int show_name)
{
struct coord a = inst->base;
struct coord b = inst->u.pad.other;
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",
hatch(inst->obj->u.pad.type));
if (show_name)
ps_pad_name(file, inst);
}
static void ps_rpad(FILE *file, const struct inst *inst, int show_name)
{
struct coord a = inst->base;
struct coord b = inst->u.pad.other;
unit_type h, w, r;
sort_coord(&a, &b);
h = b.y-a.y;
w = b.x-a.x;
fprintf(file, "0 setgray %d setlinewidth\n", PS_HATCH_LINE);
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);
}
fprintf(file, " closepath gsave %s grestore stroke\n",
hatch(inst->obj->u.pad.type));
if (show_name)
ps_pad_name(file, inst);
}
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_vec(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_meas(FILE *file, const struct inst *inst,
enum curr_unit unit)
{
struct coord a0, b0, a1, b1;
struct coord c, d;
char *s;
a0 = inst->base;
b0 = inst->u.meas.end;
project_meas(inst, &a1, &b1);
fprintf(file, "1 setlinecap 0 setgray %d 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);
fprintf(file, "gsave %d %d moveto\n", c.x/2, c.y/2);
fprintf(file, " /Helvetica-Bold findfont dup\n");
fprintf(file, " (%s) %d %d realsize\n", s,
(int) (dist_point(a1, b1)-1.5*PS_MEAS_ARROW_LEN),
PS_MEAS_TEXT_HEIGHT);
fprintf(file, " 4 copy 1000 maxfont maxfont scalefont setfont\n");
fprintf(file, " %f rotate\n", atan2(d.y, d.x)/M_PI*180);
fprintf(file, " (%s) %d realsize hcenter\n",
s, PS_MEAS_BASE_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)
{
switch (prio) {
case ip_pad:
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_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);
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)
{
enum inst_prio prio;
const struct inst *inst;
fprintf(file, "gsave %f dup scale\n", zoom);
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);
FOR_PKG_INSTS(pkg, prio, inst)
ps_foreground(file, prio, inst);
}
fprintf(file, "grestore\n");
}
/* ----- Object frames ----------------------------------------------------- */
static int generate_frames(FILE *file, const struct pkg *pkg,
const struct frame *frame, double zoom)
{
const struct inst *inst;
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:
/* @@@ */
return 0;
}
/* ----- 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, " (%s) %d %d\n",
pkg->name, PAGE_HALF_WIDTH, PS_HEADER_HEIGHT);
fprintf(file, " 4 copy 1000 maxfont maxfont scalefont setfont\n");
fprintf(file, " (%s) show grestore\n", pkg->name);
ps_hline(file, PAGE_HALF_HEIGHT-PS_HEADER_HEIGHT-PS_DIVIDER_BORDER);
}
static void ps_page(FILE *file, int page)
{
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");
}
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, " (%s) %d %d\n", s, w, h);
fprintf(file, " 4 copy 1000 maxfont maxfont scalefont setfont\n");
fprintf(file, " (%s) show grestore\n", s);
}
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);
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();
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);
active_params = minimal_params;
/* divider */
d = PAGE_HALF_WIDTH-2*x/(f+2);
fprintf(file, "grestore %d %d moveto 0 %d rlineto stroke\n",
d-PS_DIVIDER_BORDER, PS_DIVIDER_BORDER, y);
/* x1 package */
fprintf(file, "gsave %d %d translate\n",
(d+PAGE_HALF_WIDTH)/2, y/6*5+PS_DIVIDER_BORDER);
ps_draw_package(file, pkg, 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);
} 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);
active_params = minimal_params;
/* divider */
d = PAGE_HALF_WIDTH-x/(f+1);
fprintf(file, "grestore %d %d moveto 0 %d rlineto stroke\n",
d-PS_DIVIDER_BORDER, PS_DIVIDER_BORDER, y);
/* x1 package */
fprintf(file, "gsave %d %d translate\n",
(d+PAGE_HALF_WIDTH)/2, c);
ps_draw_package(file, pkg, 1);
} else {
fprintf(file, "gsave 0 %d translate\n", c);
ps_draw_package(file, pkg, f);
}
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
*/
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");
/*
* Stack: font string width height factor -> factor
*/
fprintf(file,
"/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 div h ury lly sub div 2 copy gt { exch } if pop\n"
" f mul grestore } 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 dup false charpath flattenpath pathbbox\n"
" /ury exch def /urx exch def /lly exch def /llx exch def\n"
" grestore\n"
" llx urx sub 2 div\n"
" currentpoint exch pop lly sub off add rmoveto } def\n");
/*
* Stack: string outline_width -> -
*/
fprintf(file,
"/showoutlined {\n"
" gsave 2 mul setlinewidth 1 setgray\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,
"/realsize {\n"
" 254 div 72 mul 1000 div 0 matrix currentmatrix idtransform pop\n"
" } def\n");
fprintf(file, "%%%%EndProlog\n");
}
static void epilogue(FILE *file)
{
fprintf(file, "%%%%EOF\n");
}
int postscript(FILE *file)
{
struct pkg *pkg;
int pages;
for (pkg = pkgs; pkg; pkg = pkg->next)
if (pkg->name)
pages++;
prologue(file, pages);
pages = 0;
for (pkg = pkgs; pkg; pkg = pkg->next)
if (pkg->name)
ps_package(file, pkg, ++pages);
epilogue(file);
fflush(file);
return !ferror(file);
}