plan9port/src/cmd/page/ps.c
2005-01-04 21:23:50 +00:00

450 lines
9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* ps.c
*
* provide postscript file reading support for page
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <bio.h>
#include <ctype.h>
#include "page.h"
typedef struct PSInfo PSInfo;
typedef struct Page Page;
struct Page {
char *name;
int offset; /* offset of page beginning within file */
};
struct PSInfo {
GSInfo gs;
Rectangle bbox; /* default bounding box */
Page *page;
int npage;
int clueless; /* don't know where page boundaries are */
long psoff; /* location of %! in file */
char ctm[256];
};
static int pswritepage(Document *d, int fd, int page);
static Image* psdrawpage(Document *d, int page);
static char* pspagename(Document*, int);
#define R(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y
Rectangle
rdbbox(char *p)
{
Rectangle r;
int a;
char *f[4];
while(*p == ':' || *p == ' ' || *p == '\t')
p++;
if(tokenize(p, f, 4) != 4)
return Rect(0,0,0,0);
r = Rect(atoi(f[0]), atoi(f[1]), atoi(f[2]), atoi(f[3]));
r = canonrect(r);
if(Dx(r) <= 0 || Dy(r) <= 0)
return Rect(0,0,0,0);
if(truetoboundingbox)
return r;
/* initdraw not called yet, can't use %R */
if(chatty) fprint(2, "[%d %d %d %d] -> ", R(r));
/*
* attempt to sniff out A4, 8½×11, others
* A4 is 596×842
* 8½×11 is 612×792
*/
a = Dx(r)*Dy(r);
if(a < 300*300){ /* really small, probably supposed to be */
/* empty */
} else if(Dx(r) <= 596 && r.max.x <= 596 && Dy(r) > 792 && Dy(r) <= 842 && r.max.y <= 842) /* A4 */
r = Rect(0, 0, 596, 842);
else { /* cast up to 8½×11 */
if(Dx(r) <= 612 && r.max.x <= 612){
r.min.x = 0;
r.max.x = 612;
}
if(Dy(r) <= 792 && r.max.y <= 792){
r.min.y = 0;
r.max.y = 792;
}
}
if(chatty) fprint(2, "[%d %d %d %d]\n", R(r));
return r;
}
#define RECT(X) X.min.x, X.min.y, X.max.x, X.max.y
int
prefix(char *x, char *y)
{
return strncmp(x, y, strlen(y)) == 0;
}
/*
* document ps is really being printed as n-up pages.
* we need to treat every n pages as 1.
*/
void
repaginate(PSInfo *ps, int n)
{
int i, np, onp;
Page *page;
page = ps->page;
onp = ps->npage;
np = (ps->npage+n-1)/n;
if(chatty) {
for(i=0; i<=onp+1; i++)
print("page %d: %d\n", i, page[i].offset);
}
for(i=0; i<np; i++)
page[i] = page[n*i];
/* trailer */
page[np] = page[onp];
/* EOF */
page[np+1] = page[onp+1];
ps->npage = np;
if(chatty) {
for(i=0; i<=np+1; i++)
print("page %d: %d\n", i, page[i].offset);
}
}
Document*
initps(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf)
{
Document *d;
PSInfo *ps;
char *p;
char *q, *r;
char eol;
char *nargv[1];
char fdbuf[20];
char tmp[32];
int fd;
int i;
int incomments;
int cantranslate;
int trailer=0;
int nesting=0;
int dumb=0;
int landscape=0;
long psoff;
long npage, mpage;
Page *page;
Rectangle bbox = Rect(0,0,0,0);
if(argc > 1) {
fprint(2, "can only view one ps file at a time\n");
return nil;
}
fprint(2, "reading through postscript...\n");
if(b == nil){ /* standard input; spool to disk (ouch) */
fd = spooltodisk(buf, nbuf, nil);
sprint(fdbuf, "/fd/%d", fd);
b = Bopen(fdbuf, OREAD);
if(b == nil){
fprint(2, "cannot open disk spool file\n");
wexits("Bopen temp");
}
nargv[0] = fdbuf;
argv = nargv;
}
/* find %!, perhaps after PCL nonsense */
Bseek(b, 0, 0);
psoff = 0;
eol = 0;
for(i=0; i<16; i++){
psoff = Boffset(b);
if(!(p = Brdline(b, eol='\n')) && !(p = Brdline(b, eol='\r'))) {
fprint(2, "cannot find end of first line\n");
wexits("initps");
}
if(p[0]=='\x1B')
p++, psoff++;
if(p[0] == '%' && p[1] == '!')
break;
}
if(i == 16){
werrstr("not ps");
return nil;
}
/* page counting */
npage = 0;
mpage = 16;
page = emalloc(mpage*sizeof(*page));
memset(page, 0, mpage*sizeof(*page));
cantranslate = goodps;
incomments = 1;
Keepreading:
while(p = Brdline(b, eol)) {
if(p[0] == '%')
if(chatty) fprint(2, "ps %.*s\n", utfnlen(p, Blinelen(b)-1), p);
if(npage == mpage) {
mpage *= 2;
page = erealloc(page, mpage*sizeof(*page));
memset(&page[npage], 0, npage*sizeof(*page));
}
if(p[0] != '%' || p[1] != '%')
continue;
if(prefix(p, "%%BeginDocument")) {
nesting++;
continue;
}
if(nesting > 0 && prefix(p, "%%EndDocument")) {
nesting--;
continue;
}
if(nesting)
continue;
if(prefix(p, "%%EndComment")) {
incomments = 0;
continue;
}
if(reverse == -1 && prefix(p, "%%PageOrder")) {
/* glean whether we should reverse the viewing order */
p[Blinelen(b)-1] = 0;
if(strstr(p, "Ascend"))
reverse = 0;
else if(strstr(p, "Descend"))
reverse = 1;
else if(strstr(p, "Special"))
dumb = 1;
p[Blinelen(b)-1] = '\n';
continue;
} else if(prefix(p, "%%Trailer")) {
incomments = 1;
page[npage].offset = Boffset(b)-Blinelen(b);
trailer = 1;
continue;
} else if(incomments && prefix(p, "%%Orientation")) {
if(strstr(p, "Landscape"))
landscape = 1;
} else if(incomments && Dx(bbox)==0 && prefix(p, q="%%BoundingBox")) {
bbox = rdbbox(p+strlen(q)+1);
if(chatty)
/* can't use %R because haven't initdraw() */
fprint(2, "document bbox [%d %d %d %d]\n",
RECT(bbox));
continue;
}
/*
* If they use the initgraphics command, we can't play our translation tricks.
*/
p[Blinelen(b)-1] = 0;
if((q=strstr(p, "initgraphics")) && ((r=strchr(p, '%'))==nil || r > q))
cantranslate = 0;
p[Blinelen(b)-1] = eol;
if(!prefix(p, "%%Page:"))
continue;
/*
* figure out of the %%Page: line contains a page number
* or some other page description to use in the menu bar.
*
* lines look like %%Page: x y or %%Page: x
* we prefer just x, and will generate our
* own if necessary.
*/
p[Blinelen(b)-1] = 0;
if(chatty) fprint(2, "page %s\n", p);
r = p+7;
while(*r == ' ' || *r == '\t')
r++;
q = r;
while(*q && *q != ' ' && *q != '\t')
q++;
free(page[npage].name);
if(*r) {
if(*r == '"' && *q == '"')
r++, q--;
if(*q)
*q = 0;
page[npage].name = estrdup(r);
*q = 'x';
} else {
snprint(tmp, sizeof tmp, "p %ld", npage+1);
page[npage].name = estrdup(tmp);
}
/*
* store the offset info for later viewing
*/
trailer = 0;
p[Blinelen(b)-1] = eol;
page[npage++].offset = Boffset(b)-Blinelen(b);
}
if(Blinelen(b) > 0){
fprint(2, "page: linelen %d\n", Blinelen(b));
Bseek(b, Blinelen(b), 1);
goto Keepreading;
}
if(Dx(bbox) == 0 || Dy(bbox) == 0)
bbox = Rect(0,0,612,792); /* 8½×11 */
/*
* if we didn't find any pages, assume the document
* is one big page
*/
if(npage == 0) {
dumb = 1;
if(chatty) fprint(2, "don't know where pages are\n");
reverse = 0;
goodps = 0;
trailer = 0;
page[npage].name = "p 1";
page[npage++].offset = 0;
}
if(npage+2 > mpage) {
mpage += 2;
page = erealloc(page, mpage*sizeof(*page));
memset(&page[mpage-2], 0, 2*sizeof(*page));
}
if(!trailer)
page[npage].offset = Boffset(b);
Bseek(b, 0, 2); /* EOF */
page[npage+1].offset = Boffset(b);
d = emalloc(sizeof(*d));
ps = emalloc(sizeof(*ps));
ps->page = page;
ps->npage = npage;
ps->bbox = bbox;
ps->psoff = psoff;
d->extra = ps;
d->npage = ps->npage;
d->b = b;
d->drawpage = psdrawpage;
d->pagename = pspagename;
d->fwdonly = ps->clueless = dumb;
d->docname = argv[0];
if(spawngs(&ps->gs) < 0)
return nil;
if(!cantranslate)
bbox.min = ZP;
setdim(&ps->gs, bbox, ppi, landscape);
if(goodps){
/*
* We want to only send the page (i.e. not header and trailer) information
* for each page, so initialize the device by sending the header now.
*/
pswritepage(d, ps->gs.gsfd, -1);
waitgs(&ps->gs);
}
if(dumb) {
fprint(ps->gs.gsfd, "(%s) run\n", argv[0]);
fprint(ps->gs.gsfd, "(/fd/3) (w) file dup (THIS IS NOT A PLAN9 BITMAP 01234567890123456789012345678901234567890123456789\\n) writestring flushfile\n");
}
ps->bbox = bbox;
return d;
}
static int
pswritepage(Document *d, int fd, int page)
{
Biobuf *b = d->b;
PSInfo *ps = d->extra;
int t, n, i;
long begin, end;
char buf[8192];
if(page == -1)
begin = ps->psoff;
else
begin = ps->page[page].offset;
end = ps->page[page+1].offset;
if(chatty) {
fprint(2, "writepage(%d)... from #%ld to #%ld...\n",
page, begin, end);
}
Bseek(b, begin, 0);
t = end-begin;
n = sizeof(buf);
if(n > t) n = t;
while(t > 0 && (i=Bread(b, buf, n)) > 0) {
if(write(fd, buf, i) != i)
return -1;
t -= i;
if(n > t)
n = t;
}
return end-begin;
}
static Image*
psdrawpage(Document *d, int page)
{
PSInfo *ps = d->extra;
Image *im;
if(ps->clueless)
return readimage(display, ps->gs.gsdfd, 0);
waitgs(&ps->gs);
if(goodps)
pswritepage(d, ps->gs.gsfd, page);
else {
pswritepage(d, ps->gs.gsfd, -1);
pswritepage(d, ps->gs.gsfd, page);
pswritepage(d, ps->gs.gsfd, d->npage);
}
/*
* If last line terminator is \r, gs will read ahead to check for \n
* so send one to avoid deadlock.
*/
write(ps->gs.gsfd, "\n", 1);
im = readimage(display, ps->gs.gsdfd, 0);
if(im == nil) {
fprint(2, "fatal: readimage error %r\n");
wexits("readimage");
}
waitgs(&ps->gs);
return im;
}
static char*
pspagename(Document *d, int page)
{
PSInfo *ps = (PSInfo *) d->extra;
return ps->page[page].name;
}