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
|
.B -r
|
||||||
option reverses the order in which pages are displayed.
|
option reverses the order in which pages are displayed.
|
||||||
.PP
|
.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
|
When viewing images with
|
||||||
.IR page ,
|
.IR page ,
|
||||||
it listens to the
|
it listens to the
|
||||||
|
@ -117,21 +96,6 @@ to not load any graphics files nor to read
|
||||||
from standard input but rather to listen
|
from standard input but rather to listen
|
||||||
for ones to load from the plumbing channel.
|
for ones to load from the plumbing channel.
|
||||||
.PP
|
.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.
|
Pressing and holding button 1 permits panning about the page.
|
||||||
.PP
|
.PP
|
||||||
Button 2 raises a menu of operations on the current image or the
|
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.
|
Restores the image to the original. All modifications are lost.
|
||||||
.TP
|
.TP
|
||||||
.B Zoom
|
.B Zoom
|
||||||
Prompts the user to sweep a rectangle on the image which is
|
controls magnification.
|
||||||
expanded proportionally to the rectangle.
|
|
||||||
.TP
|
.TP
|
||||||
.B Fit window
|
.B Fit
|
||||||
Resizes the image so that it fits in the current window.
|
Resizes the image so that it fits in the current window.
|
||||||
.TP
|
.TP
|
||||||
.B Rotate 90
|
.B Rotate 90
|
||||||
|
@ -166,12 +129,6 @@ Displays the previous page.
|
||||||
.B Zerox
|
.B Zerox
|
||||||
Displays the current image in a new page window.
|
Displays the current image in a new page window.
|
||||||
Useful for selecting important pages from large documents.
|
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
|
.PD
|
||||||
.PP
|
.PP
|
||||||
Button 3 raises a menu of the
|
Button 3 raises a menu of the
|
||||||
|
@ -182,23 +139,6 @@ Typing a
|
||||||
.B q
|
.B q
|
||||||
or
|
or
|
||||||
control-D exits the program.
|
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
|
.PP
|
||||||
To go to a specific page, one can type its number followed by enter.
|
To go to a specific page, one can type its number followed by enter.
|
||||||
Typing left arrow, backspace, or minus displays the previous page.
|
Typing left arrow, backspace, or minus displays the previous page.
|
||||||
|
@ -238,34 +178,7 @@ Preview this manual in a new window.
|
||||||
.IR troff (1)
|
.IR troff (1)
|
||||||
.SH SOURCE
|
.SH SOURCE
|
||||||
.B /sys/src/cmd/page
|
.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
|
.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,
|
The interface to the plumber is unsatisfactory. In particular,
|
||||||
document references cannot be sent
|
document references cannot be sent
|
||||||
via plumbing messages.
|
via plumbing messages.
|
||||||
.PP
|
|
||||||
There are too many keyboard commands.
|
|
||||||
|
|
|
@ -872,6 +872,26 @@ translate(Page *p, Point d)
|
||||||
flushimage(display, 1);
|
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*
|
Page*
|
||||||
pageat(int i)
|
pageat(int i)
|
||||||
{
|
{
|
||||||
|
@ -1006,6 +1026,7 @@ void
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
enum { Eplumb = 4 };
|
enum { Eplumb = 4 };
|
||||||
|
char jump[32];
|
||||||
Plumbmsg *pm;
|
Plumbmsg *pm;
|
||||||
Point o;
|
Point o;
|
||||||
Mouse m;
|
Mouse m;
|
||||||
|
@ -1061,6 +1082,7 @@ main(int argc, char *argv[])
|
||||||
for(; *argv; argv++)
|
for(; *argv; argv++)
|
||||||
addpage(root, shortname(*argv), popenfile, strdup(*argv), -1);
|
addpage(root, shortname(*argv), popenfile, strdup(*argv), -1);
|
||||||
|
|
||||||
|
jump[0] = 0;
|
||||||
for(;;){
|
for(;;){
|
||||||
i=event(&e);
|
i=event(&e);
|
||||||
switch(i){
|
switch(i){
|
||||||
|
@ -1183,6 +1205,8 @@ main(int argc, char *argv[])
|
||||||
qunlock(current);
|
qunlock(current);
|
||||||
if(prevpage(current))
|
if(prevpage(current))
|
||||||
pos.y = 0;
|
pos.y = 0;
|
||||||
|
case '-':
|
||||||
|
case Kbs:
|
||||||
case Kleft:
|
case Kleft:
|
||||||
showpage(prevpage(current));
|
showpage(prevpage(current));
|
||||||
break;
|
break;
|
||||||
|
@ -1201,10 +1225,22 @@ main(int argc, char *argv[])
|
||||||
qunlock(current);
|
qunlock(current);
|
||||||
if(nextpage(current))
|
if(nextpage(current))
|
||||||
pos.y = 0;
|
pos.y = 0;
|
||||||
|
case '\n':
|
||||||
|
if(jump[0]){
|
||||||
|
showpage(findpage(jump));
|
||||||
|
jump[0] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ' ':
|
case ' ':
|
||||||
case Kright:
|
case Kright:
|
||||||
showpage(nextpage(current));
|
showpage(nextpage(current));
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
i = strlen(jump);
|
||||||
|
if(i+1 < sizeof(jump)){
|
||||||
|
jump[i] = e.kbdc;
|
||||||
|
jump[i+1] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Eplumb:
|
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