Support for \cfg{input-charset}. Input files can now be in ASCII,
[sgt/halibut] / bk_ps.c
CommitLineData
43341922 1/*
2 * PostScript backend for Halibut
3 */
4
5#include <assert.h>
6#include "halibut.h"
7#include "paper.h"
8
09358aa7 9static void ps_versionid(FILE *fp, word *words);
10
43341922 11paragraph *ps_config_filename(char *filename)
12{
13 paragraph *p;
14 wchar_t *ufilename, *up;
15 int len;
16
17 p = mknew(paragraph);
18 memset(p, 0, sizeof(*p));
19 p->type = para_Config;
20 p->next = NULL;
21 p->fpos.filename = "<command line>";
22 p->fpos.line = p->fpos.col = -1;
23
24 ufilename = ufroma_dup(filename);
25 len = ustrlen(ufilename) + 2 + lenof(L"ps-filename");
26 p->keyword = mknewa(wchar_t, len);
27 up = p->keyword;
28 ustrcpy(up, L"ps-filename");
29 up = uadv(up);
30 ustrcpy(up, ufilename);
31 up = uadv(up);
32 *up = L'\0';
33 assert(up - p->keyword < len);
34 sfree(ufilename);
35
36 return p;
37}
38
39void ps_backend(paragraph *sourceform, keywordlist *keywords,
40 indexdata *idx, void *vdoc) {
41 document *doc = (document *)vdoc;
42 int font_index;
43 font_encoding *fe;
44 page_data *page;
45 int pageno;
46 FILE *fp;
47 char *filename;
48 paragraph *p;
49
50 IGNORE(keywords);
51 IGNORE(idx);
52
53 filename = dupstr("output.ps");
54 for (p = sourceform; p; p = p->next) {
43341922 55 if (p->type == para_Config && p->parent) {
56 if (!ustricmp(p->keyword, L"ps-filename")) {
57 sfree(filename);
58 filename = utoa_dup(uadv(p->keyword));
59 }
60 }
61 }
62
63 fp = fopen(filename, "w");
64 if (!fp) {
65 error(err_cantopenw, filename);
66 return;
67 }
68
69 fprintf(fp, "%%!PS-Adobe-1.0\n");
70 for (pageno = 0, page = doc->pages; page; page = page->next)
71 pageno++;
72 fprintf(fp, "%%%%Pages: %d\n", pageno);
73 fprintf(fp, "%%%%EndComments\n");
74
75 fprintf(fp, "%%%%BeginProlog\n");
7c8c4239 76 /*
77 * Supply a prologue function which allows a reasonably
78 * compressed representation of the text on the pages.
79 *
80 * Expects two arguments: a y-coordinate, and then an array.
81 * Elements of the array are processed sequentially as follows:
82 *
83 * - a number is treated as an x-coordinate
84 * - an array is treated as a (font, size) pair
85 * - a string is shown
86 */
87 fprintf(fp,
88 "/t {\n"
89 " exch /y exch def {\n"
90 " /x exch def\n"
91 " x type [] type eq {x aload pop scalefont setfont} if\n"
92 " x type dup 1 type eq exch 1.0 type eq or {x y moveto} if\n"
93 " x type () type eq {x show} if\n"
94 " } forall\n"
95 "} def\n");
96
43341922 97 fprintf(fp, "%%%%EndProlog\n");
98
99 fprintf(fp, "%%%%BeginSetup\n");
09358aa7 100
101 /*
102 * This is as good a place as any to put version IDs.
103 */
104 for (p = sourceform; p; p = p->next)
105 if (p->type == para_VersionID)
106 ps_versionid(fp, p->words);
107
43341922 108 /*
109 * Re-encode and re-metric the fonts.
110 */
111 font_index = 0;
112 for (fe = doc->fonts->head; fe; fe = fe->next) {
113 char fname[40];
114 int i;
115
116 sprintf(fname, "f%d", font_index++);
117 fe->name = dupstr(fname);
118
119 fprintf(fp, "/%s findfont dup length dict begin\n", fe->font->name);
120 fprintf(fp, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
121 fprintf(fp, "/Encoding [\n");
122 for (i = 0; i < 256; i++)
123 fprintf(fp, "/%s\n", fe->vector[i] ? fe->vector[i] : ".notdef");
124 fprintf(fp, "] def /Metrics 256 dict dup begin\n");
125 for (i = 0; i < 256; i++) {
126 if (fe->indices[i] >= 0) {
127 double width = fe->font->widths[fe->indices[i]];
128 fprintf(fp, "/%s %g def\n", fe->vector[i],
129 1000.0 * width / 4096.0);
130 }
131 }
132 fprintf(fp, "end def currentdict end\n");
133 fprintf(fp, "/fontname-%s exch definefont /%s exch def\n\n",
134 fe->name, fe->name);
135 }
136 fprintf(fp, "%%%%EndSetup\n");
137
138 /*
23765aeb 139 * Output the text and graphics.
43341922 140 */
141 pageno = 0;
142 for (page = doc->pages; page; page = page->next) {
7c8c4239 143 text_fragment *frag, *frag_end;
23765aeb 144 rect *r;
43341922 145
146 pageno++;
147 fprintf(fp, "%%%%Page: %d %d\n", pageno, pageno);
148 fprintf(fp, "%%%%BeginPageSetup\n");
149 fprintf(fp, "%%%%EndPageSetup\n");
150
138d7ffb 151#if 0
152 {
153 xref *xr;
154 /*
155 * I used this diagnostic briefly to ensure that
156 * cross-reference rectangles were being put where they
157 * should be.
158 */
159 for (xr = page->first_xref; xr; xr = xr->next) {
160 fprintf(fp, "gsave 0.7 setgray %g %g moveto",
161 xr->lx/4096.0, xr->ty/4096.0);
162 fprintf(fp, " %g %g lineto %g %g lineto",
163 xr->lx/4096.0, xr->by/4096.0,
164 xr->rx/4096.0, xr->by/4096.0);
165 fprintf(fp, " %g %g lineto closepath fill grestore\n",
166 xr->rx/4096.0, xr->ty/4096.0);
167 }
168 }
169#endif
170
23765aeb 171 for (r = page->first_rect; r; r = r->next) {
172 fprintf(fp, "%g %g moveto %g 0 rlineto 0 %g rlineto "
173 "-%g 0 rlineto closepath fill\n",
174 r->x / 4096.0, r->y / 4096.0, r->w / 4096.0,
175 r->h / 4096.0, r->w / 4096.0);
176 }
177
7c8c4239 178 frag = page->first_text;
179 while (frag) {
180 font_encoding *fe;
181 int fs;
43341922 182 char *c;
183
7c8c4239 184 /*
185 * Collect all the adjacent text fragments with the
186 * same y-coordinate.
187 */
188 for (frag_end = frag;
189 frag_end && frag_end->y == frag->y;
190 frag_end = frag_end->next);
191
192 fprintf(fp, "%g[", frag->y / 4096.0);
193
194 fe = NULL;
195 fs = -1;
196
197 while (frag && frag != frag_end) {
198
199 if (frag->fe != fe || frag->fontsize != fs)
200 fprintf(fp, "[%s %d]", frag->fe->name, frag->fontsize);
201 fe = frag->fe;
202 fs = frag->fontsize;
203
204 fprintf(fp, "%g(", frag->x/4096.0);
205 for (c = frag->text; *c; c++) {
206 if (*c == '(' || *c == ')' || *c == '\\')
207 fputc('\\', fp);
208 fputc(*c, fp);
209 }
210 fprintf(fp, ")");
43341922 211
7c8c4239 212 frag = frag->next;
43341922 213 }
214
7c8c4239 215 fprintf(fp, "]t\n");
43341922 216 }
217
218 fprintf(fp, "showpage\n");
219 }
220
221 fprintf(fp, "%%%%EOF\n");
222
223 fclose(fp);
224
225 sfree(filename);
226}
09358aa7 227
228static void ps_versionid(FILE *fp, word *words)
229{
230 fprintf(fp, "%% ");
231
232 for (; words; words = words->next) {
233 char *text;
234 int type;
235
236 switch (words->type) {
237 case word_HyperLink:
238 case word_HyperEnd:
239 case word_UpperXref:
240 case word_LowerXref:
241 case word_XrefEnd:
242 case word_IndexRef:
243 continue;
244 }
245
246 type = removeattr(words->type);
247
248 switch (type) {
249 case word_Normal:
250 text = utoa_dup(words->text);
251 break;
252 case word_WhiteSpace:
253 text = dupstr(" ");
254 break;
255 case word_Quote:
256 text = dupstr("'");
257 break;
258 }
259
260 fputs(text, fp);
261 sfree(text);
262 }
263
264 fprintf(fp, "\n");
265}