placeholder; does not yet build
This commit is contained in:
parent
d1e9002f81
commit
24c02865d8
13 changed files with 3661 additions and 0 deletions
450
src/cmd/page/ps.c
Normal file
450
src/cmd/page/ps.c
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue