Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

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