2 * PostScript backend for Halibut
9 static void ps_versionid(FILE *fp
, word
*words
);
11 paragraph
*ps_config_filename(char *filename
)
13 return cmdline_cfg_simple("ps-filename", filename
, NULL
);
16 void ps_backend(paragraph
*sourceform
, keywordlist
*keywords
,
17 indexdata
*idx
, void *vdoc
) {
18 document
*doc
= (document
*)vdoc
;
30 filename
= dupstr("output.ps");
31 for (p
= sourceform
; p
; p
= p
->next
) {
32 if (p
->type
== para_Config
) {
33 if (!ustricmp(p
->keyword
, L
"ps-filename")) {
35 filename
= dupstr(adv(p
->origkeyword
));
40 fp
= fopen(filename
, "w");
42 error(err_cantopenw
, filename
);
46 fprintf(fp
, "%%!PS-Adobe-1.0\n");
47 for (pageno
= 0, page
= doc
->pages
; page
; page
= page
->next
)
49 fprintf(fp
, "%%%%Pages: %d\n", pageno
);
50 fprintf(fp
, "%%%%EndComments\n");
52 fprintf(fp
, "%%%%BeginProlog\n");
54 * Supply a prologue function which allows a reasonably
55 * compressed representation of the text on the pages.
57 * Expects two arguments: a y-coordinate, and then an array.
58 * Elements of the array are processed sequentially as follows:
60 * - a number is treated as an x-coordinate
61 * - an array is treated as a (font, size) pair
66 " exch /y exch def {\n"
68 " x type [] type eq {x aload pop scalefont setfont} if\n"
69 " x type dup 1 type eq exch 1.0 type eq or {x y moveto} if\n"
70 " x type () type eq {x show} if\n"
74 fprintf(fp
, "%%%%EndProlog\n");
76 fprintf(fp
, "%%%%BeginSetup\n");
79 * This is as good a place as any to put version IDs.
81 for (p
= sourceform
; p
; p
= p
->next
)
82 if (p
->type
== para_VersionID
)
83 ps_versionid(fp
, p
->words
);
86 * Re-encode and re-metric the fonts.
89 for (fe
= doc
->fonts
->head
; fe
; fe
= fe
->next
) {
93 sprintf(fname
, "f%d", font_index
++);
94 fe
->name
= dupstr(fname
);
96 fprintf(fp
, "/%s findfont dup length dict begin\n", fe
->font
->name
);
97 fprintf(fp
, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
98 fprintf(fp
, "/Encoding [\n");
99 for (i
= 0; i
< 256; i
++)
100 fprintf(fp
, "/%s\n", fe
->vector
[i
] ? fe
->vector
[i
] : ".notdef");
101 fprintf(fp
, "] def /Metrics 256 dict dup begin\n");
102 for (i
= 0; i
< 256; i
++) {
103 if (fe
->indices
[i
] >= 0) {
104 double width
= fe
->font
->widths
[fe
->indices
[i
]];
105 fprintf(fp
, "/%s %g def\n", fe
->vector
[i
],
106 1000.0 * width
/ 4096.0);
109 fprintf(fp
, "end def currentdict end\n");
110 fprintf(fp
, "/fontname-%s exch definefont /%s exch def\n\n",
113 fprintf(fp
, "%%%%EndSetup\n");
116 * Output the text and graphics.
119 for (page
= doc
->pages
; page
; page
= page
->next
) {
120 text_fragment
*frag
, *frag_end
;
124 fprintf(fp
, "%%%%Page: %d %d\n", pageno
, pageno
);
125 fprintf(fp
, "%%%%BeginPageSetup\n");
126 fprintf(fp
, "%%%%EndPageSetup\n");
132 * I used this diagnostic briefly to ensure that
133 * cross-reference rectangles were being put where they
136 for (xr
= page
->first_xref
; xr
; xr
= xr
->next
) {
137 fprintf(fp
, "gsave 0.7 setgray %g %g moveto",
138 xr
->lx
/4096.0, xr
->ty
/4096.0);
139 fprintf(fp
, " %g %g lineto %g %g lineto",
140 xr
->lx
/4096.0, xr
->by
/4096.0,
141 xr
->rx
/4096.0, xr
->by
/4096.0);
142 fprintf(fp
, " %g %g lineto closepath fill grestore\n",
143 xr
->rx
/4096.0, xr
->ty
/4096.0);
148 for (r
= page
->first_rect
; r
; r
= r
->next
) {
149 fprintf(fp
, "%g %g moveto %g 0 rlineto 0 %g rlineto "
150 "-%g 0 rlineto closepath fill\n",
151 r
->x
/ 4096.0, r
->y
/ 4096.0, r
->w
/ 4096.0,
152 r
->h
/ 4096.0, r
->w
/ 4096.0);
155 frag
= page
->first_text
;
162 * Collect all the adjacent text fragments with the
165 for (frag_end
= frag
;
166 frag_end
&& frag_end
->y
== frag
->y
;
167 frag_end
= frag_end
->next
);
169 fprintf(fp
, "%g[", frag
->y
/ 4096.0);
174 while (frag
&& frag
!= frag_end
) {
176 if (frag
->fe
!= fe
|| frag
->fontsize
!= fs
)
177 fprintf(fp
, "[%s %d]", frag
->fe
->name
, frag
->fontsize
);
181 fprintf(fp
, "%g(", frag
->x
/4096.0);
182 for (c
= frag
->text
; *c
; c
++) {
183 if (*c
== '(' || *c
== ')' || *c
== '\\')
195 fprintf(fp
, "showpage\n");
198 fprintf(fp
, "%%%%EOF\n");
205 static void ps_versionid(FILE *fp
, word
*words
)
209 for (; words
; words
= words
->next
) {
213 switch (words
->type
) {
223 type
= removeattr(words
->type
);
227 text
= utoa_dup(words
->text
, CS_ASCII
);
229 case word_WhiteSpace
: