replaceing page with npage
This commit is contained in:
parent
d02a0b7766
commit
d9657f8274
16 changed files with 38 additions and 4020 deletions
|
@ -80,27 +80,6 @@ The
|
|||
.B -r
|
||||
option reverses the order in which pages are displayed.
|
||||
.PP
|
||||
When viewing a document,
|
||||
.I page
|
||||
will try to guess the true bounding box, usually rounding up from
|
||||
the file's bounding box to
|
||||
8½×11 or A4 size.
|
||||
The
|
||||
.B -b
|
||||
option causes it to respect the bounding box given in the file.
|
||||
As a more general problem,
|
||||
some PostScript files claim to conform to Adobe's
|
||||
Document Structuring Conventions but do not.
|
||||
The
|
||||
.B -P
|
||||
option enables a slightly slower and slightly more
|
||||
skeptical version of the PostScript processing code.
|
||||
Unfortunately, there are PostScript documents
|
||||
that can only be viewed with the
|
||||
.B -P
|
||||
option, and there are PostScript documents that
|
||||
can only be viewed without it.
|
||||
.PP
|
||||
When viewing images with
|
||||
.IR page ,
|
||||
it listens to the
|
||||
|
@ -117,21 +96,6 @@ to not load any graphics files nor to read
|
|||
from standard input but rather to listen
|
||||
for ones to load from the plumbing channel.
|
||||
.PP
|
||||
The
|
||||
.B -v
|
||||
option turns on extra debugging output, and
|
||||
the
|
||||
.B -V
|
||||
option turns on even more debugging output.
|
||||
The
|
||||
.B -a
|
||||
option causes
|
||||
.I page
|
||||
to call
|
||||
.IR abort (2)
|
||||
rather than exit cleanly on errors,
|
||||
to facilitate debugging.
|
||||
.PP
|
||||
Pressing and holding button 1 permits panning about the page.
|
||||
.PP
|
||||
Button 2 raises a menu of operations on the current image or the
|
||||
|
@ -145,10 +109,9 @@ The button 2 menu operations are:
|
|||
Restores the image to the original. All modifications are lost.
|
||||
.TP
|
||||
.B Zoom
|
||||
Prompts the user to sweep a rectangle on the image which is
|
||||
expanded proportionally to the rectangle.
|
||||
controls magnification.
|
||||
.TP
|
||||
.B Fit window
|
||||
.B Fit
|
||||
Resizes the image so that it fits in the current window.
|
||||
.TP
|
||||
.B Rotate 90
|
||||
|
@ -166,12 +129,6 @@ Displays the previous page.
|
|||
.B Zerox
|
||||
Displays the current image in a new page window.
|
||||
Useful for selecting important pages from large documents.
|
||||
.TP
|
||||
.B Reverse
|
||||
Reverses the order in which pages are displayed.
|
||||
.TP
|
||||
.B Write
|
||||
Writes the image to file.
|
||||
.PD
|
||||
.PP
|
||||
Button 3 raises a menu of the
|
||||
|
@ -182,23 +139,6 @@ Typing a
|
|||
.B q
|
||||
or
|
||||
control-D exits the program.
|
||||
Typing a
|
||||
.B u
|
||||
toggles whether images are displayed upside-down.
|
||||
(This is useful in the common case of mistransmitted upside-down faxes).
|
||||
Typing a
|
||||
.B r
|
||||
reverses the order in which pages are displayed.
|
||||
Typing a
|
||||
.B w
|
||||
will write the currently viewed page to a new file as a compressed
|
||||
.IR image (6)
|
||||
file.
|
||||
When possible, the filename is of the form
|
||||
.IR basename . pagenum . bit .
|
||||
Typing a
|
||||
.B d
|
||||
removes an image from the working set.
|
||||
.PP
|
||||
To go to a specific page, one can type its number followed by enter.
|
||||
Typing left arrow, backspace, or minus displays the previous page.
|
||||
|
@ -238,34 +178,7 @@ Preview this manual in a new window.
|
|||
.IR troff (1)
|
||||
.SH SOURCE
|
||||
.B /sys/src/cmd/page
|
||||
.SH DIAGNOSTICS
|
||||
The mouse cursor changes to an arrow and ellipsis
|
||||
when
|
||||
.I page
|
||||
is reading or writing a file.
|
||||
.SH BUGS
|
||||
.I Page
|
||||
supports reading of only one document
|
||||
file at a time, and
|
||||
the user interface is clumsy when viewing very large documents.
|
||||
.PP
|
||||
When viewing multipage PostScript files that do not contain
|
||||
.RB `` %%Page ''
|
||||
comments, the button 3 menu only contains
|
||||
``this page'' and ``next page'':
|
||||
correctly determining
|
||||
page boundaries in Postscript code is not computable
|
||||
in the general case.
|
||||
.PP
|
||||
If
|
||||
.I page
|
||||
has trouble viewing a Postscript file,
|
||||
it might not be exactly conforming: try viewing it with the
|
||||
.B -P
|
||||
option.
|
||||
.PP
|
||||
The interface to the plumber is unsatisfactory. In particular,
|
||||
document references cannot be sent
|
||||
via plumbing messages.
|
||||
.PP
|
||||
There are too many keyboard commands.
|
||||
|
|
|
@ -872,6 +872,26 @@ translate(Page *p, Point d)
|
|||
flushimage(display, 1);
|
||||
}
|
||||
|
||||
Page*
|
||||
findpage(char *name)
|
||||
{
|
||||
Page *p;
|
||||
int n;
|
||||
|
||||
n = strlen(name);
|
||||
/* look in current document first */
|
||||
if(current && current->up){
|
||||
for(p = current->up->down; p; p = p->next)
|
||||
if(cistrncmp(p->label, name, n) == 0)
|
||||
return p;
|
||||
}
|
||||
/* look everywhere */
|
||||
for(p = root->down; p; p = nextpage(p))
|
||||
if(cistrncmp(p->label, name, n) == 0)
|
||||
return p;
|
||||
return nil;
|
||||
}
|
||||
|
||||
Page*
|
||||
pageat(int i)
|
||||
{
|
||||
|
@ -1006,6 +1026,7 @@ void
|
|||
main(int argc, char *argv[])
|
||||
{
|
||||
enum { Eplumb = 4 };
|
||||
char jump[32];
|
||||
Plumbmsg *pm;
|
||||
Point o;
|
||||
Mouse m;
|
||||
|
@ -1061,6 +1082,7 @@ main(int argc, char *argv[])
|
|||
for(; *argv; argv++)
|
||||
addpage(root, shortname(*argv), popenfile, strdup(*argv), -1);
|
||||
|
||||
jump[0] = 0;
|
||||
for(;;){
|
||||
i=event(&e);
|
||||
switch(i){
|
||||
|
@ -1183,6 +1205,8 @@ main(int argc, char *argv[])
|
|||
qunlock(current);
|
||||
if(prevpage(current))
|
||||
pos.y = 0;
|
||||
case '-':
|
||||
case Kbs:
|
||||
case Kleft:
|
||||
showpage(prevpage(current));
|
||||
break;
|
||||
|
@ -1201,10 +1225,22 @@ main(int argc, char *argv[])
|
|||
qunlock(current);
|
||||
if(nextpage(current))
|
||||
pos.y = 0;
|
||||
case '\n':
|
||||
if(jump[0]){
|
||||
showpage(findpage(jump));
|
||||
jump[0] = 0;
|
||||
break;
|
||||
}
|
||||
case ' ':
|
||||
case Kright:
|
||||
showpage(nextpage(current));
|
||||
break;
|
||||
default:
|
||||
i = strlen(jump);
|
||||
if(i+1 < sizeof(jump)){
|
||||
jump[i] = e.kbdc;
|
||||
jump[i+1] = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Eplumb:
|
|
@ -1,187 +0,0 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <draw.h>
|
||||
#include <cursor.h>
|
||||
#include <event.h>
|
||||
#include <bio.h>
|
||||
#include <plumb.h>
|
||||
#include <ctype.h>
|
||||
#include <keyboard.h>
|
||||
#include "page.h"
|
||||
|
||||
typedef struct Cached Cached;
|
||||
struct Cached
|
||||
{
|
||||
Document *doc;
|
||||
int page;
|
||||
int angle;
|
||||
Image *im;
|
||||
};
|
||||
|
||||
static Cached cache[5];
|
||||
|
||||
static Image*
|
||||
questionmark(void)
|
||||
{
|
||||
static Image *im;
|
||||
|
||||
if(im)
|
||||
return im;
|
||||
im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack);
|
||||
if(im == nil)
|
||||
return nil;
|
||||
string(im, ZP, display->white, ZP, display->defaultfont, "?");
|
||||
return im;
|
||||
}
|
||||
|
||||
void
|
||||
cacheflush(void)
|
||||
{
|
||||
int i;
|
||||
Cached *c;
|
||||
|
||||
for(i=0; i<nelem(cache); i++){
|
||||
c = &cache[i];
|
||||
if(c->im)
|
||||
freeimage(c->im);
|
||||
c->im = nil;
|
||||
c->doc = nil;
|
||||
}
|
||||
}
|
||||
|
||||
static Image*
|
||||
_cachedpage(Document *doc, int angle, int page, char *ra)
|
||||
{
|
||||
int i;
|
||||
Cached *c, old;
|
||||
Image *im, *tmp;
|
||||
static int lastpage = -1;
|
||||
|
||||
if((page < 0 || page >= doc->npage) && !doc->fwdonly)
|
||||
return nil;
|
||||
|
||||
Again:
|
||||
for(i=0; i<nelem(cache); i++){
|
||||
c = &cache[i];
|
||||
if(c->doc == doc && c->angle == angle && c->page == page){
|
||||
if(chatty) fprint(2, "cache%s hit %d\n", ra, page);
|
||||
goto Found;
|
||||
}
|
||||
if(c->doc == nil)
|
||||
break;
|
||||
}
|
||||
|
||||
if(i >= nelem(cache))
|
||||
i = nelem(cache)-1;
|
||||
c = &cache[i];
|
||||
if(c->im)
|
||||
freeimage(c->im);
|
||||
c->im = nil;
|
||||
c->doc = nil;
|
||||
c->page = -1;
|
||||
|
||||
if(chatty) fprint(2, "cache%s load %d\n", ra, page);
|
||||
im = doc->drawpage(doc, page);
|
||||
if(im == nil){
|
||||
if(doc->fwdonly) /* end of file */
|
||||
wexits(0);
|
||||
im = questionmark();
|
||||
if(im == nil){
|
||||
Flush:
|
||||
if(i > 0){
|
||||
cacheflush();
|
||||
goto Again;
|
||||
}
|
||||
fprint(2, "out of memory: %r\n");
|
||||
wexits("memory");
|
||||
}
|
||||
return im;
|
||||
}
|
||||
|
||||
if(im->r.min.x != 0 || im->r.min.y != 0){
|
||||
/* translate to 0,0 */
|
||||
tmp = xallocimage(display, Rect(0, 0, Dx(im->r), Dy(im->r)), im->chan, 0, DNofill);
|
||||
if(tmp == nil){
|
||||
freeimage(im);
|
||||
goto Flush;
|
||||
}
|
||||
drawop(tmp, tmp->r, im, nil, im->r.min, S);
|
||||
freeimage(im);
|
||||
im = tmp;
|
||||
}
|
||||
|
||||
switch(angle){
|
||||
case 90:
|
||||
im = rot90(im);
|
||||
break;
|
||||
case 180:
|
||||
rot180(im);
|
||||
break;
|
||||
case 270:
|
||||
im = rot270(im);
|
||||
break;
|
||||
}
|
||||
if(im == nil)
|
||||
goto Flush;
|
||||
|
||||
c->doc = doc;
|
||||
c->page = page;
|
||||
c->angle = angle;
|
||||
c->im = im;
|
||||
|
||||
Found:
|
||||
if(chatty) fprint(2, "cache%s mtf %d @%d:", ra, c->page, i);
|
||||
old = *c;
|
||||
memmove(cache+1, cache, (c-cache)*sizeof cache[0]);
|
||||
cache[0] = old;
|
||||
if(chatty){
|
||||
for(i=0; i<nelem(cache); i++)
|
||||
fprint(2, " %d", cache[i].page);
|
||||
fprint(2, "\n");
|
||||
}
|
||||
if(chatty) fprint(2, "cache%s return %d %p\n", ra, old.page, old.im);
|
||||
return old.im;
|
||||
}
|
||||
|
||||
Image*
|
||||
cachedpage(Document *doc, int angle, int page)
|
||||
{
|
||||
static int lastpage = -1;
|
||||
static int rabusy;
|
||||
Image *im;
|
||||
int ra;
|
||||
|
||||
if(doc->npage < 1)
|
||||
return display->white;
|
||||
|
||||
im = _cachedpage(doc, angle, page, "");
|
||||
if(im == nil)
|
||||
return nil;
|
||||
|
||||
/* readahead */
|
||||
ra = -1;
|
||||
if(!rabusy){
|
||||
if(page == lastpage+1)
|
||||
ra = page+1;
|
||||
else if(page == lastpage-1)
|
||||
ra = page-1;
|
||||
}
|
||||
lastpage = page;
|
||||
if(ra >= 0){
|
||||
rabusy = 1;
|
||||
switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
|
||||
case -1:
|
||||
rabusy = 0;
|
||||
break;
|
||||
case 0:
|
||||
lockdisplay(display);
|
||||
_cachedpage(doc, angle, ra, "-ra");
|
||||
rabusy = 0;
|
||||
unlockdisplay(display);
|
||||
_exits(nil);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return im;
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
#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);
|
||||
}
|
|
@ -1,332 +0,0 @@
|
|||
/*
|
||||
* 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 void waitrc(void);
|
||||
static int spawnpost(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*, int argc, char **argv, uchar *buf, int nbuf)
|
||||
{
|
||||
GfxInfo *gfx;
|
||||
Document *doc;
|
||||
int i;
|
||||
|
||||
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];
|
||||
}
|
||||
|
|
@ -1,342 +0,0 @@
|
|||
/*
|
||||
* 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 *safer)
|
||||
{
|
||||
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++] = safer;
|
||||
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);
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
TARG=page
|
||||
|
||||
HFILES=page.h
|
||||
OFILES=\
|
||||
cache.$O\
|
||||
filter.$O\
|
||||
gfx.$O\
|
||||
gs.$O\
|
||||
page.$O\
|
||||
pdf.$O\
|
||||
ps.$O\
|
||||
rotate.$O\
|
||||
util.$O\
|
||||
view.$O\
|
||||
|
||||
LIB=/$objtype/lib/libdraw.a
|
||||
|
||||
UPDATE=\
|
||||
mkfile\
|
||||
${OFILES:%.$O=%.c}\
|
||||
pdfprolog.ps\
|
||||
$HFILES\
|
||||
/sys/man/1/page\
|
||||
/386/bin/page\
|
||||
|
||||
</sys/src/cmd/mkone
|
||||
|
||||
BIN=/$objtype/bin
|
||||
|
||||
pdfprolog.c: pdfprolog.ps
|
||||
cat pdfprolog.ps | sed 's/.*/"&\\n"/g' >pdfprolog.c
|
||||
|
||||
pdf.$O: pdfprolog.c
|
||||
|
|
@ -1,277 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -1,238 +0,0 @@
|
|||
#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*, char *x)
|
||||
{
|
||||
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");
|
||||
exits(0);
|
||||
default:
|
||||
break;
|
||||
case 0:
|
||||
atnotify(watcher, 1);
|
||||
for(;;)
|
||||
sleep(1000);
|
||||
/* not reached */
|
||||
}
|
||||
|
||||
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(mknewwindow)
|
||||
newwin();
|
||||
|
||||
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");
|
||||
}
|
||||
display->locking = 1;
|
||||
|
||||
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);
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
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 *rot270(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*, char*);
|
||||
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);
|
||||
Image* cachedpage(Document*, int, int);
|
||||
void cacheflush(void);
|
||||
|
||||
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)
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
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, "-dDELAYSAFER") < 0)
|
||||
return nil;
|
||||
|
||||
gscmd(pdf, "%s", pdfprolog);
|
||||
waitgs(pdf);
|
||||
|
||||
setdim(pdf, Rect(0,0,0,0), ppi, 0);
|
||||
gscmd(pdf, "(%s) (r) file { DELAYSAFER { .setsafe } if } stopped pop pdfopen begin\n", fn);
|
||||
gscmd(pdf, "pdfpagecount PAGE==\n");
|
||||
p = Brdline(&pdf->gsrd, '\n');
|
||||
npage = atoi(p);
|
||||
if(npage < 1) {
|
||||
fprint(2, "no pages?\n");
|
||||
return nil;
|
||||
}
|
||||
d->npage = npage;
|
||||
d->docname = argv[0];
|
||||
|
||||
gscmd(pdf, "Trailer\n");
|
||||
bbox = pdfbbox(pdf);
|
||||
|
||||
pdf->pagebbox = emalloc(sizeof(Rectangle)*npage);
|
||||
for(i=0; i<npage; i++) {
|
||||
gscmd(pdf, "%d pdfgetpage\n", i+1);
|
||||
pdf->pagebbox[i] = pdfbbox(pdf);
|
||||
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, "%d DoPDFPage\n", page+1);
|
||||
im = readimage(display, pdf->gsdfd, 0);
|
||||
if(im == nil) {
|
||||
fprint(2, "fatal: readimage error %r\n");
|
||||
wexits("readimage");
|
||||
}
|
||||
waitgs(pdf);
|
||||
return im;
|
||||
}
|
||||
|
||||
static char*
|
||||
pdfpagename(Document*, int page)
|
||||
{
|
||||
static char str[15];
|
||||
sprint(str, "p %d", page+1);
|
||||
return str;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/Page null def
|
||||
/Page# 0 def
|
||||
/PDFSave null def
|
||||
/DSCPageCount 0 def
|
||||
/DoPDFPage {dup /Page# exch store pdfgetpage pdfshowpage } def
|
||||
|
||||
/pdfshowpage_mysetpage { % <pagedict> pdfshowpage_mysetpage <pagedict>
|
||||
dup /CropBox pget {
|
||||
boxrect
|
||||
2 array astore /PageSize exch 4 2 roll
|
||||
4 index /Rotate pget {
|
||||
dup 0 lt {360 add} if 90 idiv {exch neg} repeat
|
||||
} if
|
||||
exch neg exch 2 array astore /PageOffset exch
|
||||
<< 5 1 roll >> setpagedevice
|
||||
} if
|
||||
} bind def
|
||||
|
||||
GS_PDF_ProcSet begin
|
||||
pdfdict begin
|
|
@ -1,450 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
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 > 1) 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, "-dSAFER") < 0)
|
||||
return nil;
|
||||
|
||||
if(!cantranslate)
|
||||
bbox.min = ZP;
|
||||
setdim(ps, 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->gsfd, -1);
|
||||
waitgs(ps);
|
||||
}
|
||||
|
||||
if(dumb) {
|
||||
fprint(ps->gsfd, "(%s) run\n", argv[0]);
|
||||
fprint(ps->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->gsdfd, 0);
|
||||
|
||||
waitgs(ps);
|
||||
|
||||
if(goodps)
|
||||
pswritepage(d, ps->gsfd, page);
|
||||
else {
|
||||
pswritepage(d, ps->gsfd, -1);
|
||||
pswritepage(d, ps->gsfd, page);
|
||||
pswritepage(d, ps->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->gsfd, "\n", 1);
|
||||
im = readimage(display, ps->gsdfd, 0);
|
||||
if(im == nil) {
|
||||
fprint(2, "fatal: readimage error %r\n");
|
||||
wexits("readimage");
|
||||
}
|
||||
waitgs(ps);
|
||||
|
||||
return im;
|
||||
}
|
||||
|
||||
static char*
|
||||
pspagename(Document *d, int page)
|
||||
{
|
||||
PSInfo *ps = (PSInfo *) d->extra;
|
||||
return ps->page[page].name;
|
||||
}
|
|
@ -1,500 +0,0 @@
|
|||
/*
|
||||
* 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 δ;
|
||||
|
||||
δ = axis==Xaxis ? Pt(maskdim,0) : Pt(0,maskdim);
|
||||
drawop(mtmp, mtmp->r, mask, nil, mask->r.min, S);
|
||||
gendrawop(mask, mask->r, mtmp, δ, mtmp, divpt(δ,-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);
|
||||
}
|
||||
|
||||
/* rotates an image 270 degrees clockwise */
|
||||
Image *
|
||||
rot270(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 rot270: %r\n");
|
||||
wexits("memory");
|
||||
}
|
||||
|
||||
for(i = 0; i < dy; i++) {
|
||||
for(j = 0; j < dx; j++) {
|
||||
drawop(tmp, Rect(i, j, i+1, j+1), im, nil, Pt(dx-(j+1), i), 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 τ, double α)
|
||||
{
|
||||
if(fabs(x) > τ)
|
||||
return 0.;
|
||||
return i0(α*sqrt(1-(x*x/(τ*τ))))/i0(α);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
#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(RFMEM|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 */
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue