placeholder; does not yet build

This commit is contained in:
rsc 2005-01-04 21:23:50 +00:00
parent d1e9002f81
commit 24c02865d8
13 changed files with 3661 additions and 0 deletions

107
src/cmd/page/filter.c Normal file
View file

@ -0,0 +1,107 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <bio.h>
#include "page.h"
Document*
initfilt(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf, char *type, char *cmd, int docopy)
{
int ofd;
int p[2];
char xbuf[8192];
int n;
if(argc > 1) {
fprint(2, "can only view one %s file at a time\n", type);
return nil;
}
fprint(2, "converting from %s to postscript...\n", type);
if(docopy){
if(pipe(p) < 0){
fprint(2, "pipe fails: %r\n");
exits("Epipe");
}
}else{
p[0] = open("/dev/null", ORDWR);
p[1] = open("/dev/null", ORDWR);
}
ofd = opentemp("/tmp/pagecvtXXXXXXXXX");
switch(fork()){
case -1:
fprint(2, "fork fails: %r\n");
exits("Efork");
default:
close(p[1]);
if(docopy){
write(p[0], buf, nbuf);
if(b)
while((n = Bread(b, xbuf, sizeof xbuf)) > 0)
write(p[0], xbuf, n);
else
while((n = read(stdinfd, xbuf, sizeof xbuf)) > 0)
write(p[0], xbuf, n);
}
close(p[0]);
waitpid();
break;
case 0:
close(p[0]);
dup(p[1], 0);
dup(ofd, 1);
/* stderr shines through */
execl("/bin/rc", "rc", "-c", cmd, nil);
break;
}
if(b)
Bterm(b);
seek(ofd, 0, 0);
b = emalloc(sizeof(Biobuf));
Binit(b, ofd, OREAD);
return initps(b, argc, argv, nil, 0);
}
Document*
initdvi(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf)
{
int fd;
char *name;
char cmd[256];
char fdbuf[20];
/*
* Stupid DVIPS won't take standard input.
*/
if(b == nil){ /* standard input; spool to disk (ouch) */
fd = spooltodisk(buf, nbuf, &name);
sprint(fdbuf, "/fd/%d", fd);
b = Bopen(fdbuf, OREAD);
if(b == nil){
fprint(2, "cannot open disk spool file\n");
wexits("Bopen temp");
}
argv = &name;
argc = 1;
}
snprint(cmd, sizeof cmd, "dvips -Pps -r0 -q1 -f1 '%s'", argv[0]);
return initfilt(b, argc, argv, buf, nbuf, "dvi", cmd, 0);
}
Document*
inittroff(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf)
{
return initfilt(b, argc, argv, buf, nbuf, "troff", "lp -dstdout", 1);
}
Document*
initmsdoc(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf)
{
return initfilt(b, argc, argv, buf, nbuf, "microsoft office", "doc2ps", 1);
}

331
src/cmd/page/gfx.c Normal file
View file

@ -0,0 +1,331 @@
/*
* graphics file reading for page
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <bio.h>
#include "page.h"
typedef struct Convert Convert;
typedef struct GfxInfo GfxInfo;
typedef struct Graphic Graphic;
struct Convert {
char *name;
char *cmd;
char *truecmd; /* cmd for true color */
};
struct GfxInfo {
Graphic *g;
};
struct Graphic {
int type;
char *name;
uchar *buf; /* if stdin */
int nbuf;
};
enum {
Ipic,
Itiff,
Ijpeg,
Igif,
Iinferno,
Ifax,
Icvt2pic,
Iplan9bm,
Iccittg4,
Ippm,
Ipng,
Iyuv,
Ibmp,
};
/*
* N.B. These commands need to read stdin if %a is replaced
* with an empty string.
*/
Convert cvt[] = {
[Ipic] { "plan9", "fb/3to1 rgbv %a |fb/pcp -tplan9" },
[Itiff] { "tiff", "fb/tiff2pic %a | fb/3to1 rgbv | fb/pcp -tplan9" },
[Iplan9bm] { "plan9bm", nil },
[Ijpeg] { "jpeg", "jpg -9 %a", "jpg -t9 %a" },
[Igif] { "gif", "gif -9 %a", "gif -t9 %a" },
[Iinferno] { "inferno", nil },
[Ifax] { "fax", "aux/g3p9bit -g %a" },
[Icvt2pic] { "unknown", "fb/cvt2pic %a |fb/3to1 rgbv" },
[Ippm] { "ppm", "ppm -9 %a", "ppm -t9 %a" },
/* ``temporary'' hack for hobby */
[Iccittg4] { "ccitt-g4", "cat %a|rx nslocum /usr/lib/ocr/bin/bcp -M|fb/pcp -tcompressed -l0" },
[Ipng] { "png", "png -9 %a", "png -t9 %a" },
[Iyuv] { "yuv", "yuv -9 %a", "yuv -t9 %a" },
[Ibmp] { "bmp", "bmp -9 %a", "bmp -t9 %a" },
};
static Image* convert(Graphic*);
static Image* gfxdrawpage(Document *d, int page);
static char* gfxpagename(Document*, int);
static int spawnrc(char*, uchar*, int);
static int addpage(Document*, char*);
static int rmpage(Document*, int);
static int genaddpage(Document*, char*, uchar*, int);
static char*
gfxpagename(Document *doc, int page)
{
GfxInfo *gfx = doc->extra;
return gfx->g[page].name;
}
static Image*
gfxdrawpage(Document *doc, int page)
{
GfxInfo *gfx = doc->extra;
return convert(gfx->g+page);
}
Document*
initgfx(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf)
{
GfxInfo *gfx;
Document *doc;
int i;
USED(b);
doc = emalloc(sizeof(*doc));
gfx = emalloc(sizeof(*gfx));
gfx->g = nil;
doc->npage = 0;
doc->drawpage = gfxdrawpage;
doc->pagename = gfxpagename;
doc->addpage = addpage;
doc->rmpage = rmpage;
doc->extra = gfx;
doc->fwdonly = 0;
fprint(2, "reading through graphics...\n");
if(argc==0 && buf)
genaddpage(doc, nil, buf, nbuf);
else{
for(i=0; i<argc; i++)
if(addpage(doc, argv[i]) < 0)
fprint(2, "warning: not including %s: %r\n", argv[i]);
}
return doc;
}
static int
genaddpage(Document *doc, char *name, uchar *buf, int nbuf)
{
Graphic *g;
GfxInfo *gfx;
Biobuf *b;
uchar xbuf[32];
int i, l;
l = 0;
gfx = doc->extra;
assert((name == nil) ^ (buf == nil));
assert(name != nil || doc->npage == 0);
for(i=0; i<doc->npage; i++)
if(strcmp(gfx->g[i].name, name) == 0)
return i;
if(name){
l = strlen(name);
if((b = Bopen(name, OREAD)) == nil) {
werrstr("Bopen: %r");
return -1;
}
if(Bread(b, xbuf, sizeof xbuf) != sizeof xbuf) {
werrstr("short read: %r");
return -1;
}
Bterm(b);
buf = xbuf;
nbuf = sizeof xbuf;
}
gfx->g = erealloc(gfx->g, (doc->npage+1)*(sizeof(*gfx->g)));
g = &gfx->g[doc->npage];
memset(g, 0, sizeof *g);
if(memcmp(buf, "GIF", 3) == 0)
g->type = Igif;
else if(memcmp(buf, "\111\111\052\000", 4) == 0)
g->type = Itiff;
else if(memcmp(buf, "\115\115\000\052", 4) == 0)
g->type = Itiff;
else if(memcmp(buf, "\377\330\377", 3) == 0)
g->type = Ijpeg;
else if(memcmp(buf, "\211PNG\r\n\032\n", 3) == 0)
g->type = Ipng;
else if(memcmp(buf, "compressed\n", 11) == 0)
g->type = Iinferno;
else if(memcmp(buf, "\0PC Research, Inc", 17) == 0)
g->type = Ifax;
else if(memcmp(buf, "TYPE=ccitt-g31", 14) == 0)
g->type = Ifax;
else if(memcmp(buf, "II*", 3) == 0)
g->type = Ifax;
else if(memcmp(buf, "TYPE=ccitt-g4", 13) == 0)
g->type = Iccittg4;
else if(memcmp(buf, "TYPE=", 5) == 0)
g->type = Ipic;
else if(buf[0] == 'P' && '0' <= buf[1] && buf[1] <= '9')
g->type = Ippm;
else if(memcmp(buf, "BM", 2) == 0)
g->type = Ibmp;
else if(memcmp(buf, " ", 10) == 0 &&
'0' <= buf[10] && buf[10] <= '9' &&
buf[11] == ' ')
g->type = Iplan9bm;
else if(strtochan((char*)buf) != 0)
g->type = Iplan9bm;
else if (l > 4 && strcmp(name + l -4, ".yuv") == 0)
g->type = Iyuv;
else
g->type = Icvt2pic;
if(name)
g->name = estrdup(name);
else{
g->name = estrdup("stdin"); /* so it can be freed */
g->buf = buf;
g->nbuf = nbuf;
}
if(chatty) fprint(2, "classified \"%s\" as \"%s\"\n", g->name, cvt[g->type].name);
return doc->npage++;
}
static int
addpage(Document *doc, char *name)
{
return genaddpage(doc, name, nil, 0);
}
static int
rmpage(Document *doc, int n)
{
int i;
GfxInfo *gfx;
if(n < 0 || n >= doc->npage)
return -1;
gfx = doc->extra;
doc->npage--;
free(gfx->g[n].name);
for(i=n; i<doc->npage; i++)
gfx->g[i] = gfx->g[i+1];
if(n < doc->npage)
return n;
if(n == 0)
return 0;
return n-1;
}
static Image*
convert(Graphic *g)
{
int fd;
Convert c;
char *cmd;
char *name, buf[1000];
Image *im;
int rcspawned = 0;
Waitmsg *w;
c = cvt[g->type];
if(c.cmd == nil) {
if(chatty) fprint(2, "no conversion for bitmap \"%s\"...\n", g->name);
if(g->buf == nil){ /* not stdin */
fd = open(g->name, OREAD);
if(fd < 0) {
fprint(2, "cannot open file: %r\n");
wexits("open");
}
}else
fd = stdinpipe(g->buf, g->nbuf);
} else {
cmd = c.cmd;
if(truecolor && c.truecmd)
cmd = c.truecmd;
if(g->buf != nil) /* is stdin */
name = "";
else
name = g->name;
if(strlen(cmd)+strlen(name) > sizeof buf) {
fprint(2, "command too long\n");
wexits("convert");
}
snprint(buf, sizeof buf, cmd, name);
if(chatty) fprint(2, "using \"%s\" to convert \"%s\"...\n", buf, g->name);
fd = spawnrc(buf, g->buf, g->nbuf);
rcspawned++;
if(fd < 0) {
fprint(2, "cannot spawn converter: %r\n");
wexits("convert");
}
}
im = readimage(display, fd, 0);
if(im == nil) {
fprint(2, "warning: couldn't read image: %r\n");
}
close(fd);
/* for some reason rx doesn't work well with wait */
/* for some reason 3to1 exits on success with a non-null status of |3to1 */
if(rcspawned && g->type != Iccittg4) {
if((w=wait())!=nil && w->msg[0] && !strstr(w->msg, "3to1"))
fprint(2, "slave wait error: %s\n", w->msg);
free(w);
}
return im;
}
static int
spawnrc(char *cmd, uchar *stdinbuf, int nstdinbuf)
{
int pfd[2];
int pid;
if(chatty) fprint(2, "spawning(%s)...", cmd);
if(pipe(pfd) < 0)
return -1;
if((pid = fork()) < 0)
return -1;
if(pid == 0) {
close(pfd[1]);
if(stdinbuf)
dup(stdinpipe(stdinbuf, nstdinbuf), 0);
else
dup(open("/dev/null", OREAD), 0);
dup(pfd[0], 1);
//dup(pfd[0], 2);
execl("/bin/rc", "rc", "-c", cmd, nil);
wexits("exec");
}
close(pfd[0]);
return pfd[1];
}

342
src/cmd/page/gs.c Normal file
View file

@ -0,0 +1,342 @@
/*
* gs interface for page.
* ps.c and pdf.c both use these routines.
* a caveat: if you run more than one gs, only the last
* one gets killed by killgs
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <bio.h>
#include "page.h"
static int gspid; /* globals for atexit */
static int gsfd;
static void killgs(void);
static void
killgs(void)
{
char tmpfile[100];
close(gsfd);
postnote(PNGROUP, getpid(), "die");
/*
* from ghostscript's use.txt:
* ``Ghostscript currently doesn't do a very good job of deleting temporary
* files when it exits; you may have to delete them manually from time to
* time.''
*/
sprint(tmpfile, "/tmp/gs_%.5da", (gspid+300000)%100000);
if(chatty) fprint(2, "remove %s...\n", tmpfile);
remove(tmpfile);
sleep(100);
postnote(PNPROC, gspid, "die yankee pig dog");
}
int
spawnwriter(GSInfo *g, Biobuf *b)
{
char buf[4096];
int n;
int fd;
switch(fork()){
case -1: return -1;
case 0: break;
default: return 0;
}
Bseek(b, 0, 0);
fd = g->gsfd;
while((n = Bread(b, buf, sizeof buf)) > 0)
write(fd, buf, n);
fprint(fd, "(/fd/3) (w) file dup (THIS IS NOT AN INFERNO BITMAP\\n) writestring flushfile\n");
_exits(0);
return -1;
}
int
spawnreader(int fd)
{
int n, pfd[2];
char buf[1024];
if(pipe(pfd)<0)
return -1;
switch(fork()){
case -1:
return -1;
case 0:
break;
default:
close(pfd[0]);
return pfd[1];
}
close(pfd[1]);
switch(fork()){
case -1:
wexits("fork failed");
case 0:
while((n=read(fd, buf, sizeof buf)) > 0) {
write(1, buf, n);
write(pfd[0], buf, n);
}
break;
default:
while((n=read(pfd[0], buf, sizeof buf)) > 0) {
write(1, buf, n);
write(fd, buf, n);
}
break;
}
postnote(PNGROUP, getpid(), "i'm die-ing");
_exits(0);
return -1;
}
void
spawnmonitor(int fd)
{
char buf[4096];
char *xbuf;
int n;
int out;
int first;
switch(rfork(RFFDG|RFNOTEG|RFPROC)){
case -1:
default:
return;
case 0:
break;
}
out = open("/dev/cons", OWRITE);
if(out < 0)
out = 2;
xbuf = buf; /* for ease of acid */
first = 1;
while((n = read(fd, xbuf, sizeof buf)) > 0){
if(first){
first = 0;
fprint(2, "Ghostscript Error:\n");
}
write(out, xbuf, n);
alarm(500);
}
_exits(0);
}
int
spawngs(GSInfo *g)
{
char *args[16];
char tb[32], gb[32];
int i, nargs;
int devnull;
int stdinout[2];
int dataout[2];
int errout[2];
/*
* spawn gs
*
* gs's standard input is fed from stdinout.
* gs output written to fd-2 (i.e. output we generate intentionally) is fed to stdinout.
* gs output written to fd 1 (i.e. ouptut gs generates on error) is fed to errout.
* gs data output is written to fd 3, which is dataout.
*/
if(pipe(stdinout) < 0 || pipe(dataout)<0 || pipe(errout)<0)
return -1;
nargs = 0;
args[nargs++] = "gs";
args[nargs++] = "-dNOPAUSE";
args[nargs++] = "-dSAFER";
args[nargs++] = "-sDEVICE=plan9";
args[nargs++] = "-sOutputFile=/fd/3";
args[nargs++] = "-dQUIET";
args[nargs++] = "-r100";
sprint(tb, "-dTextAlphaBits=%d", textbits);
sprint(gb, "-dGraphicsAlphaBits=%d", gfxbits);
if(textbits)
args[nargs++] = tb;
if(gfxbits)
args[nargs++] = gb;
args[nargs++] = "-";
args[nargs] = nil;
gspid = fork();
if(gspid == 0) {
close(stdinout[1]);
close(dataout[1]);
close(errout[1]);
/*
* Horrible problem: we want to dup fd's 0-4 below,
* but some of the source fd's might have those small numbers.
* So we need to reallocate those. In order to not step on
* anything else, we'll dup the fd's to higher ones using
* dup(x, -1), but we need to use up the lower ones first.
*/
while((devnull = open("/dev/null", ORDWR)) < 5)
;
stdinout[0] = dup(stdinout[0], -1);
errout[0] = dup(errout[0], -1);
dataout[0] = dup(dataout[0], -1);
dup(stdinout[0], 0);
dup(errout[0], 1);
dup(devnull, 2); /* never anything useful */
dup(dataout[0], 3);
dup(stdinout[0], 4);
for(i=5; i<20; i++)
close(i);
exec("/bin/gs", args);
wexits("exec");
}
close(stdinout[0]);
close(errout[0]);
close(dataout[0]);
atexit(killgs);
if(teegs)
stdinout[1] = spawnreader(stdinout[1]);
gsfd = g->gsfd = stdinout[1];
g->gsdfd = dataout[1];
g->gspid = gspid;
spawnmonitor(errout[1]);
Binit(&g->gsrd, g->gsfd, OREAD);
gscmd(g, "/PAGEOUT (/fd/4) (w) file def\n");
gscmd(g, "/PAGE== { PAGEOUT exch write==only PAGEOUT (\\n) writestring PAGEOUT flushfile } def\n");
waitgs(g);
return 0;
}
int
gscmd(GSInfo *gs, char *fmt, ...)
{
char buf[1024];
int n;
va_list v;
va_start(v, fmt);
n = vseprint(buf, buf+sizeof buf, fmt, v) - buf;
if(n <= 0)
return n;
if(chatty) {
fprint(2, "cmd: ");
write(2, buf, n);
}
if(write(gs->gsfd, buf, n) != 0)
return -1;
return n;
}
/*
* set the dimensions of the bitmap we expect to get back from GS.
*/
void
setdim(GSInfo *gs, Rectangle bbox, int ppi, int landscape)
{
Rectangle pbox;
if(chatty)
fprint(2, "setdim: bbox=%R\n", bbox);
if(ppi)
gs->ppi = ppi;
gscmd(gs, "mark\n");
if(ppi)
gscmd(gs, "/HWResolution [%d %d]\n", ppi, ppi);
if(!Dx(bbox))
bbox = Rect(0, 0, 612, 792); /* 8½×11 */
switch(landscape){
case 0:
pbox = bbox;
break;
case 1:
pbox = Rect(bbox.min.y, bbox.min.x, bbox.max.y, bbox.max.x);
break;
}
gscmd(gs, "/PageSize [%d %d]\n", Dx(pbox), Dy(pbox));
gscmd(gs, "/Margins [%d %d]\n", -pbox.min.x, -pbox.min.y);
gscmd(gs, "currentdevice putdeviceprops pop\n");
gscmd(gs, "/#copies 1 store\n");
if(!eqpt(bbox.min, ZP))
gscmd(gs, "%d %d translate\n", -bbox.min.x, -bbox.min.y);
switch(landscape){
case 0:
break;
case 1:
gscmd(gs, "%d 0 translate\n", Dy(bbox));
gscmd(gs, "90 rotate\n");
break;
}
waitgs(gs);
}
void
waitgs(GSInfo *gs)
{
/* we figure out that gs is done by telling it to
* print something and waiting until it does.
*/
char *p;
Biobuf *b = &gs->gsrd;
uchar buf[1024];
int n;
// gscmd(gs, "(\\n**bstack\\n) print flush\n");
// gscmd(gs, "stack flush\n");
// gscmd(gs, "(**estack\\n) print flush\n");
gscmd(gs, "(\\n//GO.SYSIN DD\\n) PAGE==\n");
alarm(300*1000);
for(;;) {
p = Brdline(b, '\n');
if(p == nil) {
n = Bbuffered(b);
if(n <= 0)
break;
if(n > sizeof buf)
n = sizeof buf;
Bread(b, buf, n);
continue;
}
p[Blinelen(b)-1] = 0;
if(chatty) fprint(2, "p: ");
if(chatty) write(2, p, Blinelen(b)-1);
if(chatty) fprint(2, "\n");
if(strstr(p, "Error:")) {
alarm(0);
fprint(2, "ghostscript error: %s\n", p);
wexits("gs error");
}
if(strstr(p, "//GO.SYSIN DD")) {
break;
}
}
alarm(0);
}

23
src/cmd/page/mkfile Normal file
View file

@ -0,0 +1,23 @@
<$PLAN9/src/mkhdr
TARG=page
HFILES=page.h
OFILES=\
filter.$O\
gfx.$O\
gs.$O\
page.$O\
pdf.$O\
ps.$O\
rotate.$O\
util.$O\
view.$O\
<$PLAN9/src//mkone
pdfprolog.c: pdfprolog.ps
cat pdfprolog.ps | sed 's/.*/"&\\n"/g' >pdfprolog.c
pdf.$O: pdfprolog.c

277
src/cmd/page/nrotate.c Normal file
View file

@ -0,0 +1,277 @@
/*
* Rotate an image 180° in O(log Dx + log Dy)
* draw calls, using an extra buffer the same size
* as the image.
*
* The basic concept is that you can invert an array by
* inverting the top half, inverting the bottom half, and
* then swapping them.
*
* This is usually overkill, but it speeds up slow remote
* connections quite a bit.
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include "page.h"
int ndraw = 0;
enum {
Xaxis,
Yaxis,
};
static void reverse(Image*, Image*, int);
static void shuffle(Image*, Image*, int, int, Image*, int, int);
static void writefile(char *name, Image *im, int gran);
static void halvemaskdim(Image*);
static void swapranges(Image*, Image*, int, int, int, int);
/*
* Rotate the image 180° by reflecting first
* along the X axis, and then along the Y axis.
*/
void
rot180(Image *img)
{
Image *tmp;
tmp = xallocimage(display, img->r, img->chan, 0, DNofill);
if(tmp == nil)
return;
reverse(img, tmp, Xaxis);
reverse(img, tmp, Yaxis);
freeimage(tmp);
}
Image *mtmp;
static void
reverse(Image *img, Image *tmp, int axis)
{
Image *mask;
Rectangle r;
int i, d;
/*
* We start by swapping large chunks at a time.
* The chunk size should be the largest power of
* two that fits in the dimension.
*/
d = axis==Xaxis ? Dx(img) : Dy(img);
for(i = 1; i*2 <= d; i *= 2)
;
r = axis==Xaxis ? Rect(0,0, i,100) : Rect(0,0, 100,i);
mask = xallocimage(display, r, GREY1, 1, DTransparent);
mtmp = xallocimage(display, r, GREY1, 1, DTransparent);
/*
* Now color the bottom (or left) half of the mask opaque.
*/
if(axis==Xaxis)
r.max.x /= 2;
else
r.max.y /= 2;
draw(mask, r, display->opaque, nil, ZP);
writefile("mask", mask, i);
/*
* Shuffle will recur, shuffling the pieces as necessary
* and making the mask a finer and finer grating.
*/
shuffle(img, tmp, axis, d, mask, i, 0);
freeimage(mask);
}
/*
* Shuffle the image by swapping pieces of size maskdim.
*/
static void
shuffle(Image *img, Image *tmp, int axis, int imgdim, Image *mask, int maskdim)
{
int slop;
if(maskdim == 0)
return;
/*
* Figure out how much will be left over that needs to be
* shifted specially to the bottom.
*/
slop = imgdim % maskdim;
/*
* Swap adjacent grating lines as per mask.
*/
swapadjacent(img, tmp, axis, imgdim - slop, mask, maskdim);
/*
* Calculate the mask with gratings half as wide and recur.
*/
halvemaskdim(mask, maskdim, axis);
writefile("mask", mask, maskdim/2);
shuffle(img, tmp, axis, imgdim, mask, maskdim/2);
/*
* Move the slop down to the bottom of the image.
*/
swapranges(img, tmp, 0, imgdim-slop, imgdim, axis);
moveup(im, tmp, lastnn, nn, n, axis);
}
/*
* Halve the grating period in the mask.
* The grating currently looks like
* ####____####____####____####____
* where #### is opacity.
*
* We want
* ##__##__##__##__##__##__##__##__
* which is achieved by shifting the mask
* and drawing on itself through itself.
* Draw doesn't actually allow this, so
* we have to copy it first.
*
* ####____####____####____####____ (dst)
* + ____####____####____####____#### (src)
* in __####____####____####____####__ (mask)
* ===========================================
* ##__##__##__##__##__##__##__##__
*/
static void
halvemaskdim(Image *m, int maskdim, int axis)
{
Point δ;
δ = axis==Xaxis ? Pt(maskdim,0) : Pt(0,maskdim);
draw(mtmp, mtmp->r, mask, nil, mask->r.min);
gendraw(mask, mask->r, mtmp, δ, mtmp, divpt(δ,2));
writefile("mask", mask, maskdim/2);
}
/*
* Swap the regions [a,b] and [b,c]
*/
static void
swapranges(Image *img, Image *tmp, int a, int b, int c, int axis)
{
Rectangle r;
Point δ;
if(a == b || b == c)
return;
writefile("swap", img, 0);
draw(tmp, tmp->r, im, nil, im->r.min);
/* [a,a+(c-b)] gets [b,c] */
r = img->r;
if(axis==Xaxis){
δ = Pt(1,0);
r.min.x = img->r.min.x + a;
r.max.x = img->r.min.x + a + (c-b);
}else{
δ = Pt(0,1);
r.min.y = img->r.min.y + a;
r.max.y = img->r.min.y + a + (c-b);
}
draw(img, r, tmp, nil, addpt(tmp->r.min, mulpt(δ, b)));
/* [a+(c-b), c] gets [a,b] */
r = img->r;
if(axis==Xaxis){
r.min.x = img->r.min.x + a + (c-b);
r.max.x = img->r.min.x + c;
}else{
r.min.y = img->r.min.y + a + (c-b);
r.max.y = img->r.min.y + c;
}
draw(img, r, tmp, nil, addpt(tmp->r.min, mulpt(δ, a)));
writefile("swap", img, 1);
}
/*
* Swap adjacent regions as specified by the grating.
* We do this by copying the image through the mask twice,
* once aligned with the grading and once 180° out of phase.
*/
static void
swapadjacent(Image *img, Image *tmp, int axis, int imgdim, Image *mask, int maskdim)
{
Point δ;
Rectangle r0, r1;
δ = axis==Xaxis ? Pt(1,0) : Pt(0,1);
r0 = img->r;
r1 = img->r;
switch(axis){
case Xaxis:
r0.max.x = imgdim;
r1.min.x = imgdim;
break;
case Yaxis:
r0.max.y = imgdim;
r1.min.y = imgdim;
}
/*
* r0 is the lower rectangle, while r1 is the upper one.
*/
draw(tmp, tmp->r, img, nil,
}
void
interlace(Image *im, Image *tmp, int axis, int n, Image *mask, int gran)
{
Point p0, p1;
Rectangle r0, r1;
r0 = im->r;
r1 = im->r;
switch(axis) {
case Xaxis:
r0.max.x = n;
r1.min.x = n;
p0 = (Point){gran, 0};
p1 = (Point){-gran, 0};
break;
case Yaxis:
r0.max.y = n;
r1.min.y = n;
p0 = (Point){0, gran};
p1 = (Point){0, -gran};
break;
}
draw(tmp, im->r, im, display->black, im->r.min);
gendraw(im, r0, tmp, p0, mask, mask->r.min);
gendraw(im, r0, tmp, p1, mask, p1);
}
static void
writefile(char *name, Image *im, int gran)
{
static int c = 100;
int fd;
char buf[200];
snprint(buf, sizeof buf, "%d%s%d", c++, name, gran);
fd = create(buf, OWRITE, 0666);
if(fd < 0)
return;
writeimage(fd, im, 0);
close(fd);
}

236
src/cmd/page/page.c Normal file
View file

@ -0,0 +1,236 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <bio.h>
#include "page.h"
int resizing;
int mknewwindow;
int doabort;
int chatty;
int reverse = -1;
int goodps = 1;
int ppi = 100;
int teegs = 0;
int truetoboundingbox;
int textbits=4, gfxbits=4;
int wctlfd = -1;
int stdinfd;
int truecolor;
int imagemode;
int notewatcher;
int notegp;
int
watcher(void *v, char *x)
{
USED(v);
if(strcmp(x, "die") != 0)
postnote(PNGROUP, notegp, x);
_exits(0);
return 0;
}
int
bell(void *u, char *x)
{
if(x && strcmp(x, "hangup") == 0)
_exits(0);
if(x && strstr(x, "die") == nil)
fprint(2, "postnote %d: %s\n", getpid(), x);
/* alarms come from the gs monitor */
if(x && strstr(x, "alarm")){
postnote(PNGROUP, getpid(), "die (gs error)");
postnote(PNPROC, notewatcher, "die (gs error)");
}
/* function mentions u so that it's in the stack trace */
if((u == nil || u != x) && doabort)
abort();
/* fprint(2, "exiting %d\n", getpid()); */
wexits("note");
return 0;
}
static int
afmt(Fmt *fmt)
{
char *s;
s = va_arg(fmt->args, char*);
if(s == nil || s[0] == '\0')
return fmtstrcpy(fmt, "");
else
return fmtprint(fmt, "%#q", s);
}
void
usage(void)
{
fprint(2, "usage: page [-biRrw] [-p ppi] file...\n");
exits("usage");
}
void
main(int argc, char **argv)
{
Document *doc;
Biobuf *b;
enum { Ninput = 16 };
uchar buf[Ninput+1];
int readstdin;
ARGBEGIN{
/* "temporary" debugging options */
case 'P':
goodps = 0;
break;
case 'v':
chatty++;
break;
case 'V':
teegs++;
break;
case 'a':
doabort++;
break;
case 'T':
textbits = atoi(EARGF(usage()));
gfxbits = atoi(EARGF(usage()));
break;
/* real options */
case 'R':
resizing = 1;
break;
case 'r':
reverse = 1;
break;
case 'p':
ppi = atoi(EARGF(usage()));
break;
case 'b':
truetoboundingbox = 1;
break;
case 'w':
mknewwindow = 1;
resizing = 1;
break;
case 'i':
imagemode = 1;
break;
default:
usage();
}ARGEND;
notegp = getpid();
switch(notewatcher = fork()){
case -1:
sysfatal("fork\n");
exits(0);
default:
break;
case 0:
atnotify(watcher, 1);
for(;;)
sleep(1000);
_exits(0);
}
rfork(RFNOTEG);
atnotify(bell, 1);
readstdin = 0;
if(imagemode == 0 && argc == 0){
readstdin = 1;
stdinfd = dup(0, -1);
close(0);
open("/dev/cons", OREAD);
}
quotefmtinstall();
fmtinstall('a', afmt);
fmtinstall('R', Rfmt);
fmtinstall('P', Pfmt);
if(readstdin){
b = nil;
if(readn(stdinfd, buf, Ninput) != Ninput){
fprint(2, "page: short read reading %s\n", argv[0]);
wexits("read");
}
}else if(argc != 0){
if(!(b = Bopen(argv[0], OREAD))) {
fprint(2, "page: cannot open \"%s\"\n", argv[0]);
wexits("open");
}
if(Bread(b, buf, Ninput) != Ninput) {
fprint(2, "page: short read reading %s\n", argv[0]);
wexits("read");
}
}else
b = nil;
buf[Ninput] = '\0';
if(imagemode)
doc = initgfx(nil, 0, nil, nil, 0);
else if(strncmp((char*)buf, "%PDF-", 5) == 0)
doc = initpdf(b, argc, argv, buf, Ninput);
else if(strncmp((char*)buf, "\x04%!", 2) == 0)
doc = initps(b, argc, argv, buf, Ninput);
else if(buf[0] == '\x1B' && strstr((char*)buf, "@PJL"))
doc = initps(b, argc, argv, buf, Ninput);
else if(strncmp((char*)buf, "%!", 2) == 0)
doc = initps(b, argc, argv, buf, Ninput);
else if(strcmp((char*)buf, "\xF7\x02\x01\x83\x92\xC0\x1C;") == 0)
doc = initdvi(b, argc, argv, buf, Ninput);
else if(strncmp((char*)buf, "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", 8) == 0)
doc = initmsdoc(b, argc, argv, buf, Ninput);
else if(strncmp((char*)buf, "x T ", 4) == 0)
doc = inittroff(b, argc, argv, buf, Ninput);
else {
if(ppi != 100) {
fprint(2, "page: you can't specify -p with graphic files\n");
wexits("-p and graphics");
}
doc = initgfx(b, argc, argv, buf, Ninput);
}
if(doc == nil) {
fprint(2, "page: error reading file: %r\n");
wexits("document init");
}
if(doc->npage < 1 && !imagemode) {
fprint(2, "page: no pages found?\n");
wexits("pagecount");
}
if(reverse == -1) /* neither cmdline nor ps reader set it */
reverse = 0;
if(initdraw(0, 0, "page") < 0){
fprint(2, "page: initdraw failed: %r\n");
wexits("initdraw");
}
truecolor = screen->depth > 8;
viewer(doc);
wexits(0);
}
void
wexits(char *s)
{
if(s && *s && strcmp(s, "note") != 0 && mknewwindow)
sleep(10*1000);
postnote(PNPROC, notewatcher, "die");
exits(s);
}

84
src/cmd/page/page.h Normal file
View file

@ -0,0 +1,84 @@
#include <cursor.h>
typedef struct Document Document;
struct Document {
char *docname;
int npage;
int fwdonly;
char* (*pagename)(Document*, int);
Image* (*drawpage)(Document*, int);
int (*addpage)(Document*, char*);
int (*rmpage)(Document*, int);
Biobuf *b;
void *extra;
};
void *emalloc(int);
void *erealloc(void*, int);
char *estrdup(char*);
int spawncmd(char*, char **, int, int, int);
int spooltodisk(uchar*, int, char**);
int stdinpipe(uchar*, int);
Document *initps(Biobuf*, int, char**, uchar*, int);
Document *initpdf(Biobuf*, int, char**, uchar*, int);
Document *initgfx(Biobuf*, int, char**, uchar*, int);
Document *inittroff(Biobuf*, int, char**, uchar*, int);
Document *initdvi(Biobuf*, int, char**, uchar*, int);
Document *initmsdoc(Biobuf*, int, char**, uchar*, int);
void viewer(Document*);
extern Cursor reading;
extern int chatty;
extern int goodps;
extern int textbits, gfxbits;
extern int reverse;
extern int clean;
extern int ppi;
extern int teegs;
extern int truetoboundingbox;
extern int wctlfd;
extern int resizing;
extern int mknewwindow;
void rot180(Image*);
Image *rot90(Image*);
Image *resample(Image*, Image*);
/* ghostscript interface shared by ps, pdf */
typedef struct GSInfo GSInfo;
struct GSInfo {
int gsfd;
Biobuf gsrd;
int gspid;
int gsdfd;
int ppi;
};
void waitgs(GSInfo*);
int gscmd(GSInfo*, char*, ...);
int spawngs(GSInfo*);
void setdim(GSInfo*, Rectangle, int, int);
int spawnwriter(GSInfo*, Biobuf*);
Rectangle screenrect(void);
void newwin(void);
void zerox(void);
Rectangle winrect(void);
void resize(int, int);
int max(int, int);
int min(int, int);
void wexits(char*);
Image* xallocimage(Display*, Rectangle, ulong, int, ulong);
int bell(void*, char*);
int opentemp(char *template);
extern int stdinfd;
extern int truecolor;
/* BUG BUG BUG BUG BUG: cannot use new draw operations in drawterm,
* or in vncs, and there is a bug in the kernel for copying images
* from cpu memory -> video memory (memmove is not being used).
* until all that is settled, ignore the draw operators.
*/
#define drawop(a,b,c,d,e,f) draw(a,b,c,d,e)
#define gendrawop(a,b,c,d,e,f,g) gendraw(a,b,c,d,e,f)

155
src/cmd/page/pdf.c Normal file
View file

@ -0,0 +1,155 @@
/*
* pdf.c
*
* pdf file support for page
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <bio.h>
#include "page.h"
typedef struct PDFInfo PDFInfo;
struct PDFInfo {
GSInfo gs;
Rectangle *pagebbox;
};
static Image* pdfdrawpage(Document *d, int page);
static char* pdfpagename(Document*, int);
char *pdfprolog =
#include "pdfprolog.c"
;
Rectangle
pdfbbox(GSInfo *gs)
{
char *p;
char *f[4];
Rectangle r;
r = Rect(0,0,0,0);
waitgs(gs);
gscmd(gs, "/CropBox knownoget {} {[0 0 0 0]} ifelse PAGE==\n");
p = Brdline(&gs->gsrd, '\n');
p[Blinelen(&gs->gsrd)-1] ='\0';
if(p[0] != '[')
return r;
if(tokenize(p+1, f, 4) != 4)
return r;
r = Rect(atoi(f[0]), atoi(f[1]), atoi(f[2]), atoi(f[3]));
waitgs(gs);
return r;
}
Document*
initpdf(Biobuf *b, int argc, char **argv, uchar *buf, int nbuf)
{
Document *d;
PDFInfo *pdf;
char *p;
char *fn;
char fdbuf[20];
int fd;
int i, npage;
Rectangle bbox;
if(argc > 1) {
fprint(2, "can only view one pdf file at a time\n");
return nil;
}
fprint(2, "reading through pdf...\n");
if(b == nil){ /* standard input; spool to disk (ouch) */
fd = spooltodisk(buf, nbuf, &fn);
sprint(fdbuf, "/fd/%d", fd);
b = Bopen(fdbuf, OREAD);
if(b == nil){
fprint(2, "cannot open disk spool file\n");
wexits("Bopen temp");
}
}else
fn = argv[0];
/* sanity check */
Bseek(b, 0, 0);
if(!(p = Brdline(b, '\n')) && !(p = Brdline(b, '\r'))) {
fprint(2, "cannot find end of first line\n");
wexits("initps");
}
if(strncmp(p, "%PDF-", 5) != 0) {
werrstr("not pdf");
return nil;
}
/* setup structures so one free suffices */
p = emalloc(sizeof(*d) + sizeof(*pdf));
d = (Document*) p;
p += sizeof(*d);
pdf = (PDFInfo*) p;
d->extra = pdf;
d->b = b;
d->drawpage = pdfdrawpage;
d->pagename = pdfpagename;
d->fwdonly = 0;
if(spawngs(&pdf->gs) < 0)
return nil;
gscmd(&pdf->gs, "%s", pdfprolog);
waitgs(&pdf->gs);
setdim(&pdf->gs, Rect(0,0,0,0), ppi, 0);
gscmd(&pdf->gs, "(%s) (r) file pdfopen begin\n", fn);
gscmd(&pdf->gs, "pdfpagecount PAGE==\n");
p = Brdline(&pdf->gs.gsrd, '\n');
npage = atoi(p);
if(npage < 1) {
fprint(2, "no pages?\n");
return nil;
}
d->npage = npage;
d->docname = argv[0];
gscmd(&pdf->gs, "Trailer\n");
bbox = pdfbbox(&pdf->gs);
pdf->pagebbox = emalloc(sizeof(Rectangle)*npage);
for(i=0; i<npage; i++) {
gscmd(&pdf->gs, "%d pdfgetpage\n", i+1);
pdf->pagebbox[i] = pdfbbox(&pdf->gs);
if(Dx(pdf->pagebbox[i]) <= 0)
pdf->pagebbox[i] = bbox;
}
return d;
}
static Image*
pdfdrawpage(Document *doc, int page)
{
PDFInfo *pdf = doc->extra;
Image *im;
gscmd(&pdf->gs, "%d DoPDFPage\n", page+1);
im = readimage(display, pdf->gs.gsdfd, 0);
if(im == nil) {
fprint(2, "fatal: readimage error %r\n");
wexits("readimage");
}
waitgs(&pdf->gs);
return im;
}
static char*
pdfpagename(Document *d, int page)
{
static char str[15];
USED(d);
sprint(str, "p %d", page+1);
return str;
}

29
src/cmd/page/pdfprolog.c Normal file
View file

@ -0,0 +1,29 @@
"/Page null def\n"
"/Page# 0 def\n"
"/PDFSave null def\n"
"/DSCPageCount 0 def\n"
"/DoPDFPage {dup /Page# exch store pdfgetpage mypdfshowpage } def\n"
"\n"
"/pdfshowpage_mysetpage { % <pagedict> pdfshowpage_mysetpage <pagedict>\n"
" dup /CropBox pget {\n"
" boxrect\n"
" 2 array astore /PageSize exch 4 2 roll\n"
" neg exch neg exch 2 array astore /PageOffset exch\n"
" << 5 1 roll >> setpagedevice\n"
" } if\n"
"} bind def\n"
"\n"
"/mypdfshowpage % <pagedict> pdfshowpage -\n"
" { dup /Page exch store\n"
" pdfshowpage_init \n"
" pdfshowpage_setpage \n"
" pdfshowpage_mysetpage\n"
" save /PDFSave exch store\n"
" (before exec) VMDEBUG\n"
" pdfshowpage_finish\n"
" (after exec) VMDEBUG\n"
" PDFSave restore\n"
" } bind def\n"
"\n"
"GS_PDF_ProcSet begin\n"
"pdfdict begin\n"

450
src/cmd/page/ps.c Normal file
View 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;
}

474
src/cmd/page/rotate.c Normal file
View file

@ -0,0 +1,474 @@
/*
* rotate an image 180° in O(log Dx + log Dy) /dev/draw writes,
* using an extra buffer same size as the image.
*
* the basic concept is that you can invert an array by inverting
* the top half, inverting the bottom half, and then swapping them.
* the code does this slightly backwards to ensure O(log n) runtime.
* (If you do it wrong, you can get O(log² n) runtime.)
*
* This is usually overkill, but it speeds up slow remote
* connections quite a bit.
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include "page.h"
int ndraw = 0;
enum {
Xaxis = 0,
Yaxis = 1,
};
Image *mtmp;
void
writefile(char *name, Image *im, int gran)
{
static int c = 100;
int fd;
char buf[200];
snprint(buf, sizeof buf, "%d%s%d", c++, name, gran);
fd = create(buf, OWRITE, 0666);
if(fd < 0)
return;
writeimage(fd, im, 0);
close(fd);
}
void
moveup(Image *im, Image *tmp, int a, int b, int c, int axis)
{
Rectangle range;
Rectangle dr0, dr1;
Point p0, p1;
if(a == b || b == c)
return;
drawop(tmp, tmp->r, im, nil, im->r.min, S);
switch(axis){
case Xaxis:
range = Rect(a, im->r.min.y, c, im->r.max.y);
dr0 = range;
dr0.max.x = dr0.min.x+(c-b);
p0 = Pt(b, im->r.min.y);
dr1 = range;
dr1.min.x = dr1.max.x-(b-a);
p1 = Pt(a, im->r.min.y);
break;
case Yaxis:
range = Rect(im->r.min.x, a, im->r.max.x, c);
dr0 = range;
dr0.max.y = dr0.min.y+(c-b);
p0 = Pt(im->r.min.x, b);
dr1 = range;
dr1.min.y = dr1.max.y-(b-a);
p1 = Pt(im->r.min.x, a);
break;
}
drawop(im, dr0, tmp, nil, p0, S);
drawop(im, dr1, tmp, nil, p1, S);
}
void
interlace(Image *im, Image *tmp, int axis, int n, Image *mask, int gran)
{
Point p0, p1;
Rectangle r0, r1;
r0 = im->r;
r1 = im->r;
switch(axis) {
case Xaxis:
r0.max.x = n;
r1.min.x = n;
p0 = (Point){gran, 0};
p1 = (Point){-gran, 0};
break;
case Yaxis:
r0.max.y = n;
r1.min.y = n;
p0 = (Point){0, gran};
p1 = (Point){0, -gran};
break;
}
drawop(tmp, im->r, im, display->opaque, im->r.min, S);
gendrawop(im, r0, tmp, p0, mask, mask->r.min, S);
gendrawop(im, r0, tmp, p1, mask, p1, S);
}
/*
* Halve the grating period in the mask.
* The grating currently looks like
* ####____####____####____####____
* where #### is opacity.
*
* We want
* ##__##__##__##__##__##__##__##__
* which is achieved by shifting the mask
* and drawing on itself through itself.
* Draw doesn't actually allow this, so
* we have to copy it first.
*
* ####____####____####____####____ (dst)
* + ____####____####____####____#### (src)
* in __####____####____####____####__ (mask)
* ===========================================
* ##__##__##__##__##__##__##__##__
*/
int
nextmask(Image *mask, int axis, int maskdim)
{
Point delta;
delta = axis==Xaxis ? Pt(maskdim,0) : Pt(0,maskdim);
drawop(mtmp, mtmp->r, mask, nil, mask->r.min, S);
gendrawop(mask, mask->r, mtmp, delta, mtmp, divpt(delta,-2), S);
// writefile("mask", mask, maskdim/2);
return maskdim/2;
}
void
shuffle(Image *im, Image *tmp, int axis, int n, Image *mask, int gran,
int lastnn)
{
int nn, left;
if(gran == 0)
return;
left = n%(2*gran);
nn = n - left;
interlace(im, tmp, axis, nn, mask, gran);
// writefile("interlace", im, gran);
gran = nextmask(mask, axis, gran);
shuffle(im, tmp, axis, n, mask, gran, nn);
// writefile("shuffle", im, gran);
moveup(im, tmp, lastnn, nn, n, axis);
// writefile("move", im, gran);
}
void
rot180(Image *im)
{
Image *tmp, *tmp0;
Image *mask;
Rectangle rmask;
int gran;
if(chantodepth(im->chan) < 8){
/* this speeds things up dramatically; draw is too slow on sub-byte pixel sizes */
tmp0 = xallocimage(display, im->r, CMAP8, 0, DNofill);
drawop(tmp0, tmp0->r, im, nil, im->r.min, S);
}else
tmp0 = im;
tmp = xallocimage(display, tmp0->r, tmp0->chan, 0, DNofill);
if(tmp == nil){
if(tmp0 != im)
freeimage(tmp0);
return;
}
for(gran=1; gran<Dx(im->r); gran *= 2)
;
gran /= 4;
rmask.min = ZP;
rmask.max = (Point){2*gran, 100};
mask = xallocimage(display, rmask, GREY1, 1, DTransparent);
mtmp = xallocimage(display, rmask, GREY1, 1, DTransparent);
if(mask == nil || mtmp == nil) {
fprint(2, "out of memory during rot180: %r\n");
wexits("memory");
}
rmask.max.x = gran;
drawop(mask, rmask, display->opaque, nil, ZP, S);
// writefile("mask", mask, gran);
shuffle(im, tmp, Xaxis, Dx(im->r), mask, gran, 0);
freeimage(mask);
freeimage(mtmp);
for(gran=1; gran<Dy(im->r); gran *= 2)
;
gran /= 4;
rmask.max = (Point){100, 2*gran};
mask = xallocimage(display, rmask, GREY1, 1, DTransparent);
mtmp = xallocimage(display, rmask, GREY1, 1, DTransparent);
if(mask == nil || mtmp == nil) {
fprint(2, "out of memory during rot180: %r\n");
wexits("memory");
}
rmask.max.y = gran;
drawop(mask, rmask, display->opaque, nil, ZP, S);
shuffle(im, tmp, Yaxis, Dy(im->r), mask, gran, 0);
freeimage(mask);
freeimage(mtmp);
freeimage(tmp);
if(tmp0 != im)
freeimage(tmp0);
}
/* rotates an image 90 degrees clockwise */
Image *
rot90(Image *im)
{
Image *tmp;
int i, j, dx, dy;
dx = Dx(im->r);
dy = Dy(im->r);
tmp = xallocimage(display, Rect(0, 0, dy, dx), im->chan, 0, DCyan);
if(tmp == nil) {
fprint(2, "out of memory during rot90: %r\n");
wexits("memory");
}
for(j = 0; j < dx; j++) {
for(i = 0; i < dy; i++) {
drawop(tmp, Rect(i, j, i+1, j+1), im, nil, Pt(j, dy-(i+1)), S);
}
}
freeimage(im);
return(tmp);
}
/* from resample.c -- resize from → to using interpolation */
#define K2 7 /* from -.7 to +.7 inclusive, meaning .2 into each adjacent pixel */
#define NK (2*K2+1)
double K[NK];
double
fac(int L)
{
int i, f;
f = 1;
for(i=L; i>1; --i)
f *= i;
return f;
}
/*
* i0(x) is the modified Bessel function, Σ (x/2)^2L / (L!)²
* There are faster ways to calculate this, but we precompute
* into a table so let's keep it simple.
*/
double
i0(double x)
{
double v;
int L;
v = 1.0;
for(L=1; L<10; L++)
v += pow(x/2., 2*L)/pow(fac(L), 2);
return v;
}
double
kaiser(double x, double tau, double alpha)
{
if(fabs(x) > tau)
return 0.;
return i0(alpha*sqrt(1-(x*x/(tau*tau))))/i0(alpha);
}
void
resamplex(uchar *in, int off, int d, int inx, uchar *out, int outx)
{
int i, x, k;
double X, xx, v, rat;
rat = (double)inx/(double)outx;
for(x=0; x<outx; x++){
if(inx == outx){
/* don't resample if size unchanged */
out[off+x*d] = in[off+x*d];
continue;
}
v = 0.0;
X = x*rat;
for(k=-K2; k<=K2; k++){
xx = X + rat*k/10.;
i = xx;
if(i < 0)
i = 0;
if(i >= inx)
i = inx-1;
v += in[off+i*d] * K[K2+k];
}
out[off+x*d] = v;
}
}
void
resampley(uchar **in, int off, int iny, uchar **out, int outy)
{
int y, i, k;
double Y, yy, v, rat;
rat = (double)iny/(double)outy;
for(y=0; y<outy; y++){
if(iny == outy){
/* don't resample if size unchanged */
out[y][off] = in[y][off];
continue;
}
v = 0.0;
Y = y*rat;
for(k=-K2; k<=K2; k++){
yy = Y + rat*k/10.;
i = yy;
if(i < 0)
i = 0;
if(i >= iny)
i = iny-1;
v += in[i][off] * K[K2+k];
}
out[y][off] = v;
}
}
Image*
resample(Image *from, Image *to)
{
int i, j, bpl, nchan;
uchar **oscan, **nscan;
char tmp[20];
int xsize, ysize;
double v;
Image *t1, *t2;
ulong tchan;
for(i=-K2; i<=K2; i++){
K[K2+i] = kaiser(i/10., K2/10., 4.);
}
/* normalize */
v = 0.0;
for(i=0; i<NK; i++)
v += K[i];
for(i=0; i<NK; i++)
K[i] /= v;
switch(from->chan){
case GREY8:
case RGB24:
case RGBA32:
case ARGB32:
case XRGB32:
break;
case CMAP8:
case RGB15:
case RGB16:
tchan = RGB24;
goto Convert;
case GREY1:
case GREY2:
case GREY4:
tchan = GREY8;
Convert:
/* use library to convert to byte-per-chan form, then convert back */
t1 = xallocimage(display, Rect(0, 0, Dx(from->r), Dy(from->r)), tchan, 0, DNofill);
if(t1 == nil) {
fprint(2, "out of memory for temp image 1 in resample: %r\n");
wexits("memory");
}
drawop(t1, t1->r, from, nil, ZP, S);
t2 = xallocimage(display, to->r, tchan, 0, DNofill);
if(t2 == nil) {
fprint(2, "out of memory temp image 2 in resample: %r\n");
wexits("memory");
}
resample(t1, t2);
drawop(to, to->r, t2, nil, ZP, S);
freeimage(t1);
freeimage(t2);
return to;
default:
sysfatal("can't handle channel type %s", chantostr(tmp, from->chan));
}
xsize = Dx(to->r);
ysize = Dy(to->r);
oscan = malloc(Dy(from->r)*sizeof(uchar*));
nscan = malloc(max(ysize, Dy(from->r))*sizeof(uchar*));
if(oscan == nil || nscan == nil)
sysfatal("can't allocate: %r");
/* unload original image into scan lines */
bpl = bytesperline(from->r, from->depth);
for(i=0; i<Dy(from->r); i++){
oscan[i] = malloc(bpl);
if(oscan[i] == nil)
sysfatal("can't allocate: %r");
j = unloadimage(from, Rect(from->r.min.x, from->r.min.y+i, from->r.max.x, from->r.min.y+i+1), oscan[i], bpl);
if(j != bpl)
sysfatal("unloadimage");
}
/* allocate scan lines for destination. we do y first, so need at least Dy(from->r) lines */
bpl = bytesperline(Rect(0, 0, xsize, Dy(from->r)), from->depth);
for(i=0; i<max(ysize, Dy(from->r)); i++){
nscan[i] = malloc(bpl);
if(nscan[i] == nil)
sysfatal("can't allocate: %r");
}
/* resample in X */
nchan = from->depth/8;
for(i=0; i<Dy(from->r); i++){
for(j=0; j<nchan; j++){
if(j==0 && from->chan==XRGB32)
continue;
resamplex(oscan[i], j, nchan, Dx(from->r), nscan[i], xsize);
}
free(oscan[i]);
oscan[i] = nscan[i];
nscan[i] = malloc(bpl);
if(nscan[i] == nil)
sysfatal("can't allocate: %r");
}
/* resample in Y */
for(i=0; i<xsize; i++)
for(j=0; j<nchan; j++)
resampley(oscan, nchan*i+j, Dy(from->r), nscan, ysize);
/* pack data into destination */
bpl = bytesperline(to->r, from->depth);
for(i=0; i<ysize; i++){
j = loadimage(to, Rect(0, i, xsize, i+1), nscan[i], bpl);
if(j != bpl)
sysfatal("loadimage: %r");
}
for(i=0; i<Dy(from->r); i++){
free(oscan[i]);
free(nscan[i]);
}
free(oscan);
free(nscan);
return to;
}

131
src/cmd/page/util.c Normal file
View file

@ -0,0 +1,131 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <bio.h>
#include "page.h"
void*
emalloc(int sz)
{
void *v;
v = malloc(sz);
if(v == nil) {
fprint(2, "out of memory allocating %d\n", sz);
wexits("mem");
}
memset(v, 0, sz);
return v;
}
void*
erealloc(void *v, int sz)
{
v = realloc(v, sz);
if(v == nil) {
fprint(2, "out of memory allocating %d\n", sz);
wexits("mem");
}
return v;
}
char*
estrdup(char *s)
{
char *t;
if((t = strdup(s)) == nil) {
fprint(2, "out of memory in strdup(%.10s)\n", s);
wexits("mem");
}
return t;
}
int
opentemp(char *template)
{
int fd, i;
char *p;
p = estrdup(template);
fd = -1;
for(i=0; i<10; i++){
mktemp(p);
if(access(p, 0) < 0 && (fd=create(p, ORDWR|ORCLOSE, 0400)) >= 0)
break;
strcpy(p, template);
}
if(fd < 0){
fprint(2, "couldn't make temporary file\n");
wexits("Ecreat");
}
strcpy(template, p);
free(p);
return fd;
}
/*
* spool standard input to /tmp.
* we've already read the initial in bytes into ibuf.
*/
int
spooltodisk(uchar *ibuf, int in, char **name)
{
uchar buf[8192];
int fd, n;
char temp[40];
strcpy(temp, "/tmp/pagespoolXXXXXXXXX");
fd = opentemp(temp);
if(name)
*name = estrdup(temp);
if(write(fd, ibuf, in) != in){
fprint(2, "error writing temporary file\n");
wexits("write temp");
}
while((n = read(stdinfd, buf, sizeof buf)) > 0){
if(write(fd, buf, n) != n){
fprint(2, "error writing temporary file\n");
wexits("write temp0");
}
}
seek(fd, 0, 0);
return fd;
}
/*
* spool standard input into a pipe.
* we've already ready the first in bytes into ibuf
*/
int
stdinpipe(uchar *ibuf, int in)
{
uchar buf[8192];
int n;
int p[2];
if(pipe(p) < 0){
fprint(2, "pipe fails: %r\n");
wexits("pipe");
}
switch(rfork(RFPROC|RFFDG)){
case -1:
fprint(2, "fork fails: %r\n");
wexits("fork");
default:
close(p[1]);
return p[0];
case 0:
break;
}
close(p[0]);
write(p[1], ibuf, in);
while((n = read(stdinfd, buf, sizeof buf)) > 0)
write(p[1], buf, n);
_exits(0);
return -1; /* not reached */
}

1022
src/cmd/page/view.c Normal file

File diff suppressed because it is too large Load diff