tps.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tps.c (9072B) | |
--- | |
1 /* | |
2 * ps.c | |
3 * | |
4 * provide postscript file reading support for page | |
5 */ | |
6 | |
7 #include <u.h> | |
8 #include <libc.h> | |
9 #include <draw.h> | |
10 #include <cursor.h> | |
11 #include <thread.h> | |
12 #include <bio.h> | |
13 #include <ctype.h> | |
14 #include "page.h" | |
15 | |
16 static int pswritepage(Document *d, int fd, int page); | |
17 static Image* psdrawpage(Document *d, int page); | |
18 static char* pspagename(Document*, int); | |
19 | |
20 #define R(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y | |
21 Rectangle | |
22 rdbbox(char *p) | |
23 { | |
24 Rectangle r; | |
25 int a; | |
26 char *f[4]; | |
27 while(*p == ':' || *p == ' ' || *p == '\t') | |
28 p++; | |
29 if(tokenize(p, f, 4) != 4) | |
30 return Rect(0,0,0,0); | |
31 r = Rect(atoi(f[0]), atoi(f[1]), atoi(f[2]), atoi(f[3])); | |
32 r = canonrect(r); | |
33 if(Dx(r) <= 0 || Dy(r) <= 0) | |
34 return Rect(0,0,0,0); | |
35 | |
36 if(truetoboundingbox) | |
37 return r; | |
38 | |
39 /* initdraw not called yet, can't use %R */ | |
40 if(chatty) fprint(2, "[%d %d %d %d] -> ", R(r)); | |
41 /* | |
42 * attempt to sniff out A4, 8½×11, others | |
43 * A4 is 596×842 | |
44 * 8½×11 is 612×792 | |
45 */ | |
46 | |
47 a = Dx(r)*Dy(r); | |
48 if(a < 300*300){ /* really small, probably supposed to be… | |
49 /* empty */ | |
50 } else if(Dx(r) <= 596 && r.max.x <= 596 && Dy(r) > 792 && Dy(r)… | |
51 r = Rect(0, 0, 596, 842); | |
52 else { /* cast up to 8½×11 */ | |
53 if(Dx(r) <= 612 && r.max.x <= 612){ | |
54 r.min.x = 0; | |
55 r.max.x = 612; | |
56 } | |
57 if(Dy(r) <= 792 && r.max.y <= 792){ | |
58 r.min.y = 0; | |
59 r.max.y = 792; | |
60 } | |
61 } | |
62 if(chatty) fprint(2, "[%d %d %d %d]\n", R(r)); | |
63 return r; | |
64 } | |
65 | |
66 #define RECT(X) X.min.x, X.min.y, X.max.x, X.max.y | |
67 | |
68 int | |
69 prefix(char *x, char *y) | |
70 { | |
71 return strncmp(x, y, strlen(y)) == 0; | |
72 } | |
73 | |
74 /* | |
75 * document ps is really being printed as n-up pages. | |
76 * we need to treat every n pages as 1. | |
77 */ | |
78 void | |
79 repaginate(PSInfo *ps, int n) | |
80 { | |
81 int i, np, onp; | |
82 Page *page; | |
83 | |
84 page = ps->page; | |
85 onp = ps->npage; | |
86 np = (ps->npage+n-1)/n; | |
87 | |
88 if(chatty) { | |
89 for(i=0; i<=onp+1; i++) | |
90 print("page %d: %d\n", i, page[i].offset); | |
91 } | |
92 | |
93 for(i=0; i<np; i++) | |
94 page[i] = page[n*i]; | |
95 | |
96 /* trailer */ | |
97 page[np] = page[onp]; | |
98 | |
99 /* EOF */ | |
100 page[np+1] = page[onp+1]; | |
101 | |
102 ps->npage = np; | |
103 | |
104 if(chatty) { | |
105 for(i=0; i<=np+1; i++) | |
106 print("page %d: %d\n", i, page[i].offset); | |
107 } | |
108 | |
109 } | |
110 | |
111 Document* | |
112 initps(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf) | |
113 { | |
114 Document *d; | |
115 PSInfo *ps; | |
116 char *p; | |
117 char *q, *r; | |
118 char eol; | |
119 char *nargv[1]; | |
120 char fdbuf[20]; | |
121 char tmp[32]; | |
122 int fd; | |
123 int i; | |
124 int incomments; | |
125 int cantranslate; | |
126 int trailer=0; | |
127 int nesting=0; | |
128 int dumb=0; | |
129 int landscape=0; | |
130 long psoff; | |
131 long npage, mpage; | |
132 Page *page; | |
133 Rectangle bbox = Rect(0,0,0,0); | |
134 | |
135 if(argc > 1) { | |
136 fprint(2, "can only view one ps file at a time\n"); | |
137 return nil; | |
138 } | |
139 | |
140 fprint(2, "reading through postscript...\n"); | |
141 if(b == nil){ /* standard input; spool to disk (ouch) */ | |
142 fd = spooltodisk(buf, nbuf, nil); | |
143 sprint(fdbuf, "/dev/fd/%d", fd); | |
144 b = Bopen(fdbuf, OREAD); | |
145 if(b == nil){ | |
146 fprint(2, "cannot open disk spool file\n"); | |
147 wexits("Bopen temp"); | |
148 } | |
149 nargv[0] = fdbuf; | |
150 argv = nargv; | |
151 } | |
152 | |
153 /* find %!, perhaps after PCL nonsense */ | |
154 Bseek(b, 0, 0); | |
155 psoff = 0; | |
156 eol = 0; | |
157 for(i=0; i<16; i++){ | |
158 psoff = Boffset(b); | |
159 if(!(p = Brdline(b, eol='\n')) && !(p = Brdline(b, eol='… | |
160 fprint(2, "cannot find end of first line\n"); | |
161 wexits("initps"); | |
162 } | |
163 if(p[0]=='\x1B') | |
164 p++, psoff++; | |
165 if(p[0] == '%' && p[1] == '!') | |
166 break; | |
167 } | |
168 if(i == 16){ | |
169 werrstr("not ps"); | |
170 return nil; | |
171 } | |
172 | |
173 /* page counting */ | |
174 npage = 0; | |
175 mpage = 16; | |
176 page = emalloc(mpage*sizeof(*page)); | |
177 memset(page, 0, mpage*sizeof(*page)); | |
178 | |
179 cantranslate = goodps; | |
180 incomments = 1; | |
181 Keepreading: | |
182 while(p = Brdline(b, eol)) { | |
183 if(p[0] == '%') | |
184 if(chatty) fprint(2, "ps %.*s\n", utfnlen(p, Bli… | |
185 if(npage == mpage) { | |
186 mpage *= 2; | |
187 page = erealloc(page, mpage*sizeof(*page)); | |
188 memset(&page[npage], 0, npage*sizeof(*page)); | |
189 } | |
190 | |
191 if(p[0] != '%' || p[1] != '%') | |
192 continue; | |
193 | |
194 if(prefix(p, "%%BeginDocument")) { | |
195 nesting++; | |
196 continue; | |
197 } | |
198 if(nesting > 0 && prefix(p, "%%EndDocument")) { | |
199 nesting--; | |
200 continue; | |
201 } | |
202 if(nesting) | |
203 continue; | |
204 | |
205 if(prefix(p, "%%EndComment")) { | |
206 incomments = 0; | |
207 continue; | |
208 } | |
209 if(reverse == -1 && prefix(p, "%%PageOrder")) { | |
210 /* glean whether we should reverse the viewing o… | |
211 p[Blinelen(b)-1] = 0; | |
212 if(strstr(p, "Ascend")) | |
213 reverse = 0; | |
214 else if(strstr(p, "Descend")) | |
215 reverse = 1; | |
216 else if(strstr(p, "Special")) | |
217 dumb = 1; | |
218 p[Blinelen(b)-1] = '\n'; | |
219 continue; | |
220 } else if(prefix(p, "%%Trailer")) { | |
221 incomments = 1; | |
222 page[npage].offset = Boffset(b)-Blinelen(b); | |
223 trailer = 1; | |
224 continue; | |
225 } else if(incomments && prefix(p, "%%Orientation")) { | |
226 if(strstr(p, "Landscape")) | |
227 landscape = 1; | |
228 } else if(incomments && Dx(bbox)==0 && prefix(p, q="%%Bo… | |
229 bbox = rdbbox(p+strlen(q)+1); | |
230 if(chatty) | |
231 /* can't use %R because haven't initdraw… | |
232 fprint(2, "document bbox [%d %d %d %d]\n… | |
233 RECT(bbox)); | |
234 continue; | |
235 } | |
236 | |
237 /* | |
238 * If they use the initgraphics command, we can't play o… | |
239 */ | |
240 p[Blinelen(b)-1] = 0; | |
241 if((q=strstr(p, "initgraphics")) && ((r=strchr(p, '%'))=… | |
242 cantranslate = 0; | |
243 p[Blinelen(b)-1] = eol; | |
244 | |
245 if(!prefix(p, "%%Page:")) | |
246 continue; | |
247 | |
248 /* | |
249 * figure out of the %%Page: line contains a page number | |
250 * or some other page description to use in the menu bar. | |
251 * | |
252 * lines look like %%Page: x y or %%Page: x | |
253 * we prefer just x, and will generate our | |
254 * own if necessary. | |
255 */ | |
256 p[Blinelen(b)-1] = 0; | |
257 if(chatty) fprint(2, "page %s\n", p); | |
258 r = p+7; | |
259 while(*r == ' ' || *r == '\t') | |
260 r++; | |
261 q = r; | |
262 while(*q && *q != ' ' && *q != '\t') | |
263 q++; | |
264 free(page[npage].name); | |
265 if(*r) { | |
266 if(*r == '"' && *q == '"') | |
267 r++, q--; | |
268 if(*q) | |
269 *q = 0; | |
270 page[npage].name = estrdup(r); | |
271 *q = 'x'; | |
272 } else { | |
273 snprint(tmp, sizeof tmp, "p %ld", npage+1); | |
274 page[npage].name = estrdup(tmp); | |
275 } | |
276 | |
277 /* | |
278 * store the offset info for later viewing | |
279 */ | |
280 trailer = 0; | |
281 p[Blinelen(b)-1] = eol; | |
282 page[npage++].offset = Boffset(b)-Blinelen(b); | |
283 } | |
284 if(Blinelen(b) > 0){ | |
285 fprint(2, "page: linelen %d\n", Blinelen(b)); | |
286 Bseek(b, Blinelen(b), 1); | |
287 goto Keepreading; | |
288 } | |
289 | |
290 if(Dx(bbox) == 0 || Dy(bbox) == 0) | |
291 bbox = Rect(0,0,612,792); /* 8½×11 */ | |
292 /* | |
293 * if we didn't find any pages, assume the document | |
294 * is one big page | |
295 */ | |
296 if(npage == 0) { | |
297 dumb = 1; | |
298 if(chatty) fprint(2, "don't know where pages are\n"); | |
299 reverse = 0; | |
300 goodps = 0; | |
301 trailer = 0; | |
302 page[npage].name = "p 1"; | |
303 page[npage++].offset = 0; | |
304 } | |
305 | |
306 if(npage+2 > mpage) { | |
307 mpage += 2; | |
308 page = erealloc(page, mpage*sizeof(*page)); | |
309 memset(&page[mpage-2], 0, 2*sizeof(*page)); | |
310 } | |
311 | |
312 if(!trailer) | |
313 page[npage].offset = Boffset(b); | |
314 | |
315 Bseek(b, 0, 2); /* EOF */ | |
316 page[npage+1].offset = Boffset(b); | |
317 | |
318 d = emalloc(sizeof(*d)); | |
319 ps = emalloc(sizeof(*ps)); | |
320 ps->page = page; | |
321 ps->npage = npage; | |
322 ps->bbox = bbox; | |
323 ps->psoff = psoff; | |
324 | |
325 d->extra = ps; | |
326 d->npage = ps->npage; | |
327 d->b = b; | |
328 d->drawpage = psdrawpage; | |
329 d->pagename = pspagename; | |
330 | |
331 d->fwdonly = ps->clueless = dumb; | |
332 d->docname = argv[0]; | |
333 /* | |
334 * "tag" the doc as an image for now since there still is the "b… | |
335 * problem for ps files. | |
336 */ | |
337 d->type = Tgfx; | |
338 | |
339 if(spawngs(&ps->gs, "-dSAFER") < 0) | |
340 return nil; | |
341 | |
342 if(!cantranslate) | |
343 bbox.min = ZP; | |
344 setdim(&ps->gs, bbox, ppi, landscape); | |
345 | |
346 if(goodps){ | |
347 /* | |
348 * We want to only send the page (i.e. not header and tr… | |
349 * for each page, so initialize the device by sending t… | |
350 */ | |
351 pswritepage(d, ps->gs.gsfd, -1); | |
352 waitgs(&ps->gs); | |
353 } | |
354 | |
355 if(dumb) { | |
356 fprint(ps->gs.gsfd, "(%s) run PAGEFLUSH\n", argv[0]); | |
357 fprint(ps->gs.gsfd, "(/dev/fd/3) (w) file dup (THIS IS N… | |
358 close(ps->gs.gsfd); | |
359 } | |
360 | |
361 ps->bbox = bbox; | |
362 | |
363 return d; | |
364 } | |
365 | |
366 static int | |
367 pswritepage(Document *d, int fd, int page) | |
368 { | |
369 Biobuf *b = d->b; | |
370 PSInfo *ps = d->extra; | |
371 int t, n, i; | |
372 long begin, end; | |
373 char buf[8192]; | |
374 | |
375 if(page == -1) | |
376 begin = ps->psoff; | |
377 else | |
378 begin = ps->page[page].offset; | |
379 | |
380 end = ps->page[page+1].offset; | |
381 | |
382 if(chatty) { | |
383 fprint(2, "writepage(%d)... from #%ld to #%ld...\n", | |
384 page, begin, end); | |
385 } | |
386 Bseek(b, begin, 0); | |
387 | |
388 t = end-begin; | |
389 n = sizeof(buf); | |
390 if(n > t) n = t; | |
391 while(t > 0 && (i=Bread(b, buf, n)) > 0) { | |
392 if(write(fd, buf, i) != i) | |
393 return -1; | |
394 t -= i; | |
395 if(n > t) | |
396 n = t; | |
397 } | |
398 return end-begin; | |
399 } | |
400 | |
401 static Image* | |
402 psdrawpage(Document *d, int page) | |
403 { | |
404 PSInfo *ps = d->extra; | |
405 Image *im; | |
406 | |
407 if(ps->clueless) | |
408 return convert(&ps->gs.g); | |
409 | |
410 waitgs(&ps->gs); | |
411 | |
412 if(goodps) | |
413 pswritepage(d, ps->gs.gsfd, page); | |
414 else { | |
415 pswritepage(d, ps->gs.gsfd, -1); | |
416 pswritepage(d, ps->gs.gsfd, page); | |
417 pswritepage(d, ps->gs.gsfd, d->npage); | |
418 } | |
419 /* | |
420 * If last line terminator is \r, gs will read ahead to check fo… | |
421 * so send one to avoid deadlock. | |
422 */ | |
423 write(ps->gs.gsfd, "\n", 1); | |
424 fprint(ps->gs.gsfd, "\nPAGEFLUSH\n"); | |
425 im = convert(&ps->gs.g); | |
426 if(im == nil) { | |
427 fprint(2, "fatal: readimage error %r\n"); | |
428 wexits("readimage"); | |
429 } | |
430 waitgs(&ps->gs); | |
431 | |
432 return im; | |
433 } | |
434 | |
435 static char* | |
436 pspagename(Document *d, int page) | |
437 { | |
438 PSInfo *ps = (PSInfo *) d->extra; | |
439 return ps->page[page].name; | |
440 } |