From d9657f82748a397eed451a4cfd0bbc6f19551488 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Thu, 15 Sep 2011 20:59:31 +0200 Subject: replaceing page with npage --- sys/man/1/page | 91 +-- sys/src/cmd/npage.c | 1246 --------------------------------------- sys/src/cmd/page.c | 1282 +++++++++++++++++++++++++++++++++++++++++ sys/src/cmd/page/cache.c | 187 ------ sys/src/cmd/page/filter.c | 107 ---- sys/src/cmd/page/gfx.c | 332 ----------- sys/src/cmd/page/gs.c | 342 ----------- sys/src/cmd/page/mkfile | 36 -- sys/src/cmd/page/nrotate.c | 277 --------- sys/src/cmd/page/page.c | 238 -------- sys/src/cmd/page/page.h | 85 --- sys/src/cmd/page/pdf.c | 153 ----- sys/src/cmd/page/pdfprolog.ps | 20 - sys/src/cmd/page/ps.c | 450 --------------- sys/src/cmd/page/rotate.c | 500 ---------------- sys/src/cmd/page/util.c | 131 ----- sys/src/cmd/page/view.c | 1073 ---------------------------------- 17 files changed, 1284 insertions(+), 5266 deletions(-) delete mode 100644 sys/src/cmd/npage.c create mode 100644 sys/src/cmd/page.c delete mode 100644 sys/src/cmd/page/cache.c delete mode 100644 sys/src/cmd/page/filter.c delete mode 100644 sys/src/cmd/page/gfx.c delete mode 100644 sys/src/cmd/page/gs.c delete mode 100644 sys/src/cmd/page/mkfile delete mode 100644 sys/src/cmd/page/nrotate.c delete mode 100644 sys/src/cmd/page/page.c delete mode 100644 sys/src/cmd/page/page.h delete mode 100644 sys/src/cmd/page/pdf.c delete mode 100644 sys/src/cmd/page/pdfprolog.ps delete mode 100644 sys/src/cmd/page/ps.c delete mode 100644 sys/src/cmd/page/rotate.c delete mode 100644 sys/src/cmd/page/util.c delete mode 100644 sys/src/cmd/page/view.c diff --git a/sys/man/1/page b/sys/man/1/page index 4246080c0..3272c5825 100644 --- a/sys/man/1/page +++ b/sys/man/1/page @@ -80,27 +80,6 @@ The .B -r option reverses the order in which pages are displayed. .PP -When viewing a document, -.I page -will try to guess the true bounding box, usually rounding up from -the file's bounding box to -8½×11 or A4 size. -The -.B -b -option causes it to respect the bounding box given in the file. -As a more general problem, -some PostScript files claim to conform to Adobe's -Document Structuring Conventions but do not. -The -.B -P -option enables a slightly slower and slightly more -skeptical version of the PostScript processing code. -Unfortunately, there are PostScript documents -that can only be viewed with the -.B -P -option, and there are PostScript documents that -can only be viewed without it. -.PP When viewing images with .IR page , it listens to the @@ -117,21 +96,6 @@ to not load any graphics files nor to read from standard input but rather to listen for ones to load from the plumbing channel. .PP -The -.B -v -option turns on extra debugging output, and -the -.B -V -option turns on even more debugging output. -The -.B -a -option causes -.I page -to call -.IR abort (2) -rather than exit cleanly on errors, -to facilitate debugging. -.PP Pressing and holding button 1 permits panning about the page. .PP Button 2 raises a menu of operations on the current image or the @@ -145,10 +109,9 @@ The button 2 menu operations are: Restores the image to the original. All modifications are lost. .TP .B Zoom -Prompts the user to sweep a rectangle on the image which is -expanded proportionally to the rectangle. +controls magnification. .TP -.B Fit window +.B Fit Resizes the image so that it fits in the current window. .TP .B Rotate 90 @@ -166,12 +129,6 @@ Displays the previous page. .B Zerox Displays the current image in a new page window. Useful for selecting important pages from large documents. -.TP -.B Reverse -Reverses the order in which pages are displayed. -.TP -.B Write -Writes the image to file. .PD .PP Button 3 raises a menu of the @@ -182,23 +139,6 @@ Typing a .B q or control-D exits the program. -Typing a -.B u -toggles whether images are displayed upside-down. -(This is useful in the common case of mistransmitted upside-down faxes). -Typing a -.B r -reverses the order in which pages are displayed. -Typing a -.B w -will write the currently viewed page to a new file as a compressed -.IR image (6) -file. -When possible, the filename is of the form -.IR basename . pagenum . bit . -Typing a -.B d -removes an image from the working set. .PP To go to a specific page, one can type its number followed by enter. Typing left arrow, backspace, or minus displays the previous page. @@ -238,34 +178,7 @@ Preview this manual in a new window. .IR troff (1) .SH SOURCE .B /sys/src/cmd/page -.SH DIAGNOSTICS -The mouse cursor changes to an arrow and ellipsis -when -.I page -is reading or writing a file. .SH BUGS -.I Page -supports reading of only one document -file at a time, and -the user interface is clumsy when viewing very large documents. -.PP -When viewing multipage PostScript files that do not contain -.RB `` %%Page '' -comments, the button 3 menu only contains -``this page'' and ``next page'': -correctly determining -page boundaries in Postscript code is not computable -in the general case. -.PP -If -.I page -has trouble viewing a Postscript file, -it might not be exactly conforming: try viewing it with the -.B -P -option. -.PP The interface to the plumber is unsatisfactory. In particular, document references cannot be sent via plumbing messages. -.PP -There are too many keyboard commands. diff --git a/sys/src/cmd/npage.c b/sys/src/cmd/npage.c deleted file mode 100644 index 5bd1653c0..000000000 --- a/sys/src/cmd/npage.c +++ /dev/null @@ -1,1246 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -typedef struct Page Page; -struct Page { - char *label; - - QLock; - void *data; - int (*open)(Page *); - - char *text; - Image *image; - int fd; - int gen; - - Page *up; - Page *next; - Page *down; - Page *tail; -}; - -int zoom = 1; -int ppi = 100; -int imode; -int newwin; -int rotate; -int viewgen; -int pagegen; -Point resize, pos; -Page *root, *current; -QLock pagelock; -int nullfd; - -char pagespool[] = "/tmp/pagespool."; - -enum { - NPROC = 4, - NAHEAD = 2, - NBUF = 8*1024, - NPATH = 1024, -}; - -char *pagemenugen(int i); - -char *menuitems[] = { - "orig size", - "rotate 90", - "upside down", - "", - "fit width", - "fit height", - "", - "zoom in", - "zoom out", - "", - "next", - "prev", - "zerox", - "", - "quit", - nil -}; - -Menu pagemenu = { - nil, - pagemenugen, - -1, -}; - -Menu menu = { - menuitems, - nil, - -1, -}; - -Cursor reading = { - {-1, -1}, - {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00, - 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0, - 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0, - 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, }, - {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00, - 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0, - 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40, - 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, } -}; - -void showpage(Page *); -void drawpage(Page *); -Point pagesize(Page *); - -Page* -addpage(Page *up, char *label, int (*popen)(Page *), void *pdata, int fd) -{ - Page *p; - - p = mallocz(sizeof(*p), 1); - p->label = strdup(label); - p->gen = pagegen; - p->text = nil; - p->image = nil; - p->data = pdata; - p->open = popen; - p->fd = fd; - - p->down = nil; - p->tail = nil; - p->next = nil; - - qlock(&pagelock); - if(p->up = up){ - if(up->tail == nil) - up->down = up->tail = p; - else { - up->tail->next = p; - up->tail = p; - } - } - qunlock(&pagelock); - - if(up && current == up) - showpage(p); - return p; -} - -void -resizewin(Point size) -{ - int wctl; - - if((wctl = open("/dev/wctl", OWRITE)) < 0) - return; - /* add rio border */ - size = addpt(size, Pt(Borderwidth*2, Borderwidth*2)); - fprint(wctl, "resize -dx %d -dy %d\n", size.x, size.y); - close(wctl); -} - -int -createtmp(ulong id, char *pfx) -{ - char nam[64]; - - snprint(nam, sizeof nam, "%s%s%.12d%.8lux", pagespool, pfx, getpid(), id ^ 0xcafebabe); - return create(nam, OEXCL|ORCLOSE|ORDWR, 0600); -} - -int -catchnote(void *, char *msg) -{ - if(strstr(msg, "sys: write on closed pipe")) - return 1; - if(strstr(msg, "hangup")) - return 1; - if(strstr(msg, "alarm")) - return 1; - return 0; -} - -void -pipeline(int fd, char *fmt, ...) -{ - char buf[128], *argv[4]; - va_list arg; - int pfd[2]; - - if(pipe(pfd) < 0){ - Err: - dup(nullfd, fd); - return; - } - switch(rfork(RFPROC|RFFDG|RFREND|RFNOWAIT)){ - case -1: - close(pfd[0]); - close(pfd[1]); - goto Err; - case 0: - if(dup(fd, 0)<0) - exits("dup"); - if(dup(pfd[1], 1)<0) - exits("dup"); - close(fd); - close(pfd[1]); - close(pfd[0]); - va_start(arg, fmt); - vsnprint(buf, sizeof buf, fmt, arg); - va_end(arg); - - argv[0] = "rc"; - argv[1] = "-c"; - argv[2] = buf; - argv[3] = nil; - exec("/bin/rc", argv); - sysfatal("exec: %r"); - } - close(pfd[1]); - dup(pfd[0], fd); - close(pfd[0]); -} - -int -popenfile(Page*); - -int -popenconv(Page *p) -{ - char nam[NPATH]; - int fd; - - if((fd = dup(p->fd, -1)) < 0){ - close(p->fd); - p->fd = -1; - return -1; - } - - seek(fd, 0, 0); - if(p->data) - pipeline(fd, "%s", (char*)p->data); - - /* - * dont keep the file descriptor arround if it can simply - * be reopened. - */ - fd2path(p->fd, nam, sizeof(nam)); - if(strncmp(nam, pagespool, strlen(pagespool))){ - close(p->fd); - p->fd = -1; - p->data = strdup(nam); - p->open = popenfile; - } - - return fd; -} - -typedef struct Ghost Ghost; -struct Ghost -{ - QLock; - - int pin; - int pout; - int pdat; -}; - -int -popenpdf(Page *p) -{ - char buf[NBUF]; - int n, pfd[2]; - Ghost *gs; - - if(pipe(pfd) < 0) - return -1; - switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){ - case -1: - close(pfd[0]); - close(pfd[1]); - return -1; - case 0: - close(pfd[0]); - gs = p->data; - qlock(gs); - fprint(gs->pin, "%s DoPDFPage\n" - "(/fd/3) (w) file " - "dup flushfile " - "dup (THIS IS NOT AN INFERNO BITMAP\\n) writestring " - "flushfile\n", p->label); - while((n = read(gs->pdat, buf, sizeof buf)) > 0){ - if(memcmp(buf, "THIS IS NOT AN INFERNO BITMAP\n", 30) == 0) - break; - if(pfd[1] < 0) - continue; - if(write(pfd[1], buf, n) != n){ - close(pfd[1]); - pfd[1]=-1; - } - } - qunlock(gs); - exits(nil); - } - close(pfd[1]); - return pfd[0]; -} - -int -infernobithdr(char *buf, int n) -{ - if(n >= 11){ - if(memcmp(buf, "compressed\n", 11) == 0) - return 1; - if(strtochan((char*)buf)) - return 1; - if(memcmp(buf, " ", 10) == 0 && - '0' <= buf[10] && buf[10] <= '9' && - buf[11] == ' ') - return 1; - } - return 0; -} - -int -popengs(Page *p) -{ - int n, i, pdf, ifd, ofd, pin[2], pout[2], pdat[2]; - char buf[NBUF], nam[32], *argv[16]; - - pdf = 0; - ifd = p->fd; - p->fd = -1; - seek(ifd, 0, 0); - if(read(ifd, buf, 5) != 5) - goto Err0; - seek(ifd, 0, 0); - if(memcmp(buf, "%PDF-", 5) == 0) - pdf = 1; - p->text = strdup(p->label); - if(pipe(pin) < 0){ - Err0: - close(ifd); - return -1; - } - if(pipe(pout) < 0){ - Err1: - close(pin[0]); - close(pin[1]); - goto Err0; - } - if(pipe(pdat) < 0){ - Err2: - close(pdat[0]); - close(pdat[1]); - goto Err1; - } - - switch(rfork(RFREND|RFPROC|RFFDG|RFNOWAIT)){ - case -1: - goto Err2; - case 0: - if(pdf){ - if(dup(pin[1], 0)<0) - exits("dup"); - if(dup(pout[1], 1)<0) - exits("dup"); - } else { - if(dup(nullfd, 0)<0) - exits("dup"); - if(dup(nullfd, 1)<0) - exits("dup"); - } - if(dup(nullfd, 2)<0) - exits("dup"); - if(dup(pdat[1], 3)<0) - exits("dup"); - if(dup(ifd, 4)<0) - exits("dup"); - - close(pin[0]); - close(pin[1]); - close(pout[0]); - close(pout[1]); - close(pdat[0]); - close(pdat[1]); - close(ifd); - - if(p->data) - pipeline(4, "%s", (char*)p->data); - - argv[0] = "gs"; - argv[1] = "-q"; - argv[2] = "-sDEVICE=plan9"; - argv[3] = "-sOutputFile=/fd/3"; - argv[4] = "-dBATCH"; - argv[5] = pdf ? "-dDELAYSAFER" : "-dSAFER"; - argv[6] = "-dQUIET"; - argv[7] = "-dTextAlphaBits=4"; - argv[8] = "-dGraphicsAlphaBits=4"; - snprint(buf, sizeof buf, "-r%d", ppi); - argv[9] = buf; - argv[10] = "-dDOINTERPOLATE"; - argv[11] = pdf ? "-" : "/fd/4"; - argv[12] = nil; - exec("/bin/gs", argv); - sysfatal("exec: %r"); - } - - close(pin[1]); - close(pout[1]); - close(pdat[1]); - close(ifd); - - if(pdf){ - Ghost *gs; - char *prolog = - "/PAGEOUT (/fd/1) (w) file def\n" - "/PAGE== { PAGEOUT exch write==only PAGEOUT (\\n) writestring PAGEOUT flushfile } def\n" - "\n" - "/Page null def\n" - "/Page# 0 def\n" - "/PDFSave null def\n" - "/DSCPageCount 0 def\n" - "/DoPDFPage {dup /Page# exch store pdfgetpage pdfshowpage } def\n" - "\n" - "GS_PDF_ProcSet begin\n" - "pdfdict begin\n" - "(/fd/4) (r) file { DELAYSAFER { .setsafe } if } stopped pop pdfopen begin\n" - "\n" - "pdfpagecount PAGE==\n"; - - n = strlen(prolog); - if(write(pin[0], prolog, n) != n) - goto Out; - if((n = read(pout[0], buf, sizeof(buf)-1)) < 0) - goto Out; - buf[n] = 0; - n = atoi(buf); - if(n <= 0){ - werrstr("no pages"); - goto Out; - } - gs = mallocz(sizeof(*gs), 1); - gs->pin = pin[0]; - gs->pout = pout[0]; - gs->pdat = pdat[0]; - for(i=1; i<=n; i++){ - snprint(nam, sizeof nam, "%d", i); - addpage(p, nam, popenpdf, gs, -1); - } - - /* keep ghostscript arround */ - return -1; - } else { - i = 0; - ofd = -1; - while((n = read(pdat[0], buf, sizeof(buf))) >= 0){ - if(ofd >= 0 && (n <= 0 || infernobithdr(buf, n))){ - snprint(nam, sizeof nam, "%d", i); - addpage(p, nam, popenconv, nil, ofd); - ofd = -1; - } - if(n <= 0) - break; - if(ofd < 0){ - snprint(nam, sizeof nam, "%.4d", ++i); - if((ofd = createtmp((ulong)p, nam)) < 0) - ofd = dup(nullfd, -1); - } - if(write(ofd, buf, n) != n) - break; - } - if(ofd >= 0) - close(ofd); - } -Out: - close(pin[0]); - close(pout[0]); - close(pdat[0]); - return -1; -} - -int -popenfile(Page *p) -{ - char buf[NBUF], *file; - int i, n, fd, tfd; - Dir *d; - - fd = p->fd; - p->fd = -1; - file = p->data; - if(fd < 0){ - if((fd = open(file, OREAD)) < 0){ - Err0: - p->data = nil; - free(file); - return -1; - } - } - seek(fd, 0, 0); - if((d = dirfstat(fd)) == nil){ - Err1: - close(fd); - goto Err0; - } - if(d->mode & DMDIR){ - free(d); - d = nil; - if((n = dirreadall(fd, &d)) < 0) - goto Err1; - for(i = 0; itext = strdup(p->label); - goto Err1; - } - free(d); - - memset(buf, 0, 32+1); - if((n = read(fd, buf, 32)) <= 0) - goto Err1; - - p->fd = fd; - p->data = nil; - p->open = popenconv; - if(memcmp(buf, "%PDF-", 5) == 0 || strstr(buf, "%!")) - p->open = popengs; - else if(memcmp(buf, "x T ", 4) == 0){ - p->data = "lp -dstdout"; - p->open = popengs; - } - else if(memcmp(buf, "\xF7\x02\x01\x83\x92\xC0\x1C;", 8) == 0){ - p->data = "dvips -Pps -r0 -q1 -f1"; - p->open = popengs; - } - else if(memcmp(buf, "\x1F\x8B", 2) == 0){ - p->data = "gunzip"; - p->open = popengs; - } - else if(memcmp(buf, "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", 8) == 0){ - p->data = "doc2ps"; - p->open = popengs; - } - else if(memcmp(buf, "GIF", 3) == 0) - p->data = "gif -t9"; - else if(memcmp(buf, "\111\111\052\000", 4) == 0) - p->data = "fb/tiff2pic | fb/3to1 rgbv | fb/pcp -tplan9"; - else if(memcmp(buf, "\115\115\000\052", 4) == 0) - p->data = "fb/tiff2pic | fb/3to1 rgbv | fb/pcp -tplan9"; - else if(memcmp(buf, "\377\330\377", 3) == 0) - p->data = "jpg -t9"; - else if(memcmp(buf, "\211PNG\r\n\032\n", 3) == 0) - p->data = "png -t9"; - else if(memcmp(buf, "\0PC Research, Inc", 17) == 0) - p->data = "aux/g3p9bit -g"; - else if(memcmp(buf, "TYPE=ccitt-g31", 14) == 0) - p->data = "aux/g3p9bit -g"; - else if(memcmp(buf, "II*", 3) == 0) - p->data = "aux/g3p9bit -g"; - else if(memcmp(buf, "TYPE=", 5) == 0) - p->data = "fb/3to1 rgbv |fb/pcp -tplan9"; - else if(buf[0] == 'P' && '0' <= buf[1] && buf[1] <= '9') - p->data = "ppm -t9"; - else if(memcmp(buf, "BM", 2) == 0) - p->data = "bmp -t9"; - else if(infernobithdr(buf, n)) - p->data = nil; - else { - werrstr("unknown image format"); - goto Err1; - } - - if(seek(fd, 0, 0) < 0) - goto Noseek; - if((i = read(fd, buf+n, n)) < 0) - goto Err1; - if(i != n || memcmp(buf, buf+n, i)){ - n += i; - Noseek: - if((tfd = createtmp((ulong)p, "file")) < 0) - goto Err1; - while(n > 0){ - if(write(tfd, buf, n) != n) - goto Err2; - if((n = read(fd, buf, sizeof(buf))) < 0) - goto Err2; - } - if(dup(tfd, fd) < 0){ - Err2: - close(tfd); - goto Err1; - } - close(tfd); - } - free(file); - return p->open(p); -} - -Page* -nextpage(Page *p) -{ - if(p){ - if(p->down) - return p->down; - if(p->next) - return p->next; - if(p->up) - return p->up->next; - } - return nil; -} - -Page* -prevpage(Page *x) -{ - Page *p, *t; - - if(x){ - for(p = root->down; p; p = t) - if((t = nextpage(p)) == x) - return p; - } - return nil; -} - -int -openpage(Page *p) -{ - int fd; - - fd = -1; - if(p->open == nil || (fd = p->open(p)) < 0) - p->open = nil; - else { - if(rotate) - pipeline(fd, "rotate -r %d", rotate); - if(resize.x) - pipeline(fd, "resize -x %d", resize.x); - else if(resize.y) - pipeline(fd, "resize -y %d", resize.y); - } - return fd; -} - -void -loadpage(Page *p) -{ - int fd; - - if(p->open && p->image == nil && p->text == nil){ - if((fd = openpage(p)) >= 0){ - pagegen++; - p->image = readimage(display, fd, 1); - close(fd); - } - if(p->image == nil && p->text == nil) - p->text = smprint("%s: %r", p->label); - } - p->gen = pagegen; -} - -void -unloadpage(Page *p) -{ - if(p->open){ - if(p->text) - free(p->text); - p->text = nil; - if(p->image){ - lockdisplay(display); - freeimage(p->image); - unlockdisplay(display); - } - p->image = nil; - } -} - -void -unloadpages(int age) -{ - Page *p; - - for(p = root->down; p; p = nextpage(p)){ - if(age == 0) /* synchronous flush */ - qlock(p); - else if(!canqlock(p)) - continue; - if((pagegen - p->gen) >= age) - unloadpage(p); - qunlock(p); - } -} - -void -loadpages(Page *p, int ahead, int oviewgen) -{ - int i; - - ahead++; /* load at least one */ - unloadpages(ahead*2); - for(i = 0; i < ahead && p; p = nextpage(p), i++){ - if(viewgen != oviewgen) - break; - if(canqlock(p)){ - loadpage(p); - if(viewgen != oviewgen){ - unloadpage(p); - qunlock(p); - break; - } - if(p == current){ - Point size; - - esetcursor(nil); - size = pagesize(p); - if(size.x && size.y && newwin){ - newwin = 0; - resizewin(size); - } - lockdisplay(display); - drawpage(p); - unlockdisplay(display); - } - qunlock(p); - } - } -} - -/* - * A draw operation that touches only the area contained in bot but not in top. - * mp and sp get aligned with bot.min. - */ -static void -gendrawdiff(Image *dst, Rectangle bot, Rectangle top, - Image *src, Point sp, Image *mask, Point mp, int op) -{ - Rectangle r; - Point origin; - Point delta; - - USED(op); - - if(Dx(bot)*Dy(bot) == 0) - return; - - /* no points in bot - top */ - if(rectinrect(bot, top)) - return; - - /* bot - top ≡ bot */ - if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){ - gendrawop(dst, bot, src, sp, mask, mp, op); - return; - } - - origin = bot.min; - /* split bot into rectangles that don't intersect top */ - /* left side */ - if(bot.min.x < top.min.x){ - r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y); - delta = subpt(r.min, origin); - gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); - bot.min.x = top.min.x; - } - - /* right side */ - if(bot.max.x > top.max.x){ - r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y); - delta = subpt(r.min, origin); - gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); - bot.max.x = top.max.x; - } - - /* top */ - if(bot.min.y < top.min.y){ - r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y); - delta = subpt(r.min, origin); - gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); - bot.min.y = top.min.y; - } - - /* bottom */ - if(bot.max.y > top.max.y){ - r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y); - delta = subpt(r.min, origin); - gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); - bot.max.y = top.max.y; - } -} - -void -zoomdraw(Image *d, Rectangle r, Rectangle top, Image *s, Point sp, int f) -{ - int w, x, y; - Image *t; - Point a; - - if(f <= 1){ - gendrawdiff(d, r, top, s, sp, nil, ZP, S); - return; - } - a = ZP; - if(r.min.x < d->r.min.x){ - sp.x += (d->r.min.x - r.min.x)/f; - a.x = (d->r.min.x - r.min.x)%f; - r.min.x = d->r.min.x; - } - if(r.min.y < d->r.min.y){ - sp.y += (d->r.min.y - r.min.y)/f; - a.y = (d->r.min.y - r.min.y)%f; - r.min.y = d->r.min.y; - } - rectclip(&r, d->r); - w = s->r.max.x - sp.x; - if(w > Dx(r)) - w = Dx(r); - t = allocimage(display, Rect(r.min.x, r.min.y, r.min.x+w, r.max.y), s->chan, 0, DNofill); - if(t == nil) - return; - for(y=r.min.y; yimage ? mulpt(subpt(p->image->r.max, p->image->r.min), zoom) : ZP; -} - -void -drawpage(Page *p) -{ - Rectangle r; - Image *i; - - if((i = p->image) == nil){ - char *s; - - if((s = p->text) == nil) - s = "..."; - r.min = ZP; - r.max = stringsize(font, p->text); - r = rectaddpt(r, addpt(subpt(divpt(subpt(screen->r.max, screen->r.min), 2), divpt(r.max, 2)), - screen->r.min)); - draw(screen, r, display->white, nil, ZP); - string(screen, r.min, display->black, ZP, font, s); - } else { - r = rectaddpt(Rpt(ZP, pagesize(p)), addpt(pos, screen->r.min)); - zoomdraw(screen, r, ZR, i, i->r.min, zoom); - } - gendrawdiff(screen, screen->r, r, display->white, ZP, nil, ZP, S); - border(screen, r, -Borderwidth, display->black, ZP); - flushimage(display, 1); -} - -void -translate(Page *p, Point d) -{ - Rectangle r, or, nr; - Image *i; - - i = p->image; - if((i==0) || (d.x==0 && d.y==0)) - return; - r = rectaddpt(Rpt(ZP, pagesize(p)), addpt(pos, screen->r.min)); - pos = addpt(pos, d); - nr = rectaddpt(r, d); - or = r; - rectclip(&or, screen->r); - draw(screen, rectaddpt(or, d), screen, nil, or.min); - zoomdraw(screen, nr, rectaddpt(or, d), i, i->r.min, zoom); - gendrawdiff(screen, screen->r, nr, display->white, ZP, nil, ZP, S); - border(screen, nr, -Borderwidth, display->black, ZP); - flushimage(display, 1); -} - -Page* -pageat(int i) -{ - Page *p; - - for(p = root->down; i > 0 && p; p = nextpage(p)) - i--; - return i ? nil : p; -} - -int -pageindex(Page *x) -{ - Page *p; - int i; - - for(i = 0, p = root->down; p && p != x; p = nextpage(p)) - i++; - return (p == x) ? i : -1; -} - -char* -pagemenugen(int i) -{ - Page *p; - if(p = pageat(i)) - return p->label; - return nil; -} - -void -showpage(Page *p) -{ - static int nproc; - int oviewgen; - - if(p == nil) - return; - esetcursor(&reading); - current = p; - oviewgen = viewgen; - if(++nproc > NPROC) - if(waitpid() > 0) - nproc--; - switch(rfork(RFPROC|RFMEM)){ - case -1: - sysfatal("rfork: %r"); - case 0: - loadpages(p, NAHEAD, oviewgen); - exits(nil); - } -} - -void -zerox(Page *p) -{ - char nam[64], *argv[4]; - int fd; - - if(p == nil) - return; - esetcursor(&reading); - qlock(p); - if((fd = openpage(p)) < 0) - goto Out; - if(rfork(RFREND|RFFDG|RFPROC|RFENVG|RFNOTEG|RFNOWAIT) == 0){ - dup(fd, 0); - close(fd); - - snprint(nam, sizeof nam, "/bin/%s", argv0); - argv[0] = argv0; - argv[1] = "-w"; - argv[2] = nil; - exec(nam, argv); - sysfatal("exec: %r"); - } - close(fd); -Out: - qunlock(p); - esetcursor(nil); -} - -void -eresized(int new) -{ - Page *p; - - lockdisplay(display); - if(new && getwindow(display, Refnone) == -1) - sysfatal("getwindow: %r"); - if(p = current){ - if(canqlock(p)){ - drawpage(p); - qunlock(p); - } - } - unlockdisplay(display); -} - -void killcohort(void) -{ - int i; - for(i=0;i!=3;i++){ /* It's a long way to the kitchen */ - postnote(PNGROUP, getpid(), "kill"); - sleep(1); - } -} - -void drawerr(Display *, char *msg) -{ - sysfatal("draw: %s", msg); -} - -char* -shortname(char *s) -{ - char *x; - if(x = strrchr(s, '/')) - if(x[1] != 0) - return x+1; - return s; -} - -void -usage(void) -{ - fprint(2, "usage: %s [ -iRw ] [ -p ppi ] [ file ... ]\n", argv0); - exits("usage"); -} - -void -main(int argc, char *argv[]) -{ - enum { Eplumb = 4 }; - Plumbmsg *pm; - Point o; - Mouse m; - Event e; - char *s; - int i; - - ARGBEGIN { - case 'a': - case 'v': - case 'V': - case 'P': - break; - case 'R': - newwin = -1; - break; - case 'w': - newwin = 1; - break; - case 'i': - imode = 1; - break; - case 'p': - ppi = atoi(EARGF(usage())); - break; - default: - usage(); - } ARGEND; - - /* - * so that we can stop all subprocesses with a note, - * and to isolate rendezvous from other processes - */ - rfork(RFNOTEG|RFNAMEG|RFREND); - atexit(killcohort); - atnotify(catchnote, 1); - if(newwin > 0){ - s = smprint("-pid %d", getpid()); - if(newwindow(s) < 0) - sysfatal("newwindow: %r"); - free(s); - } - initdraw(drawerr, nil, argv0); - display->locking = 1; - unlockdisplay(display); - einit(Ekeyboard|Emouse); - eplumb(Eplumb, "image"); - nullfd = open("/dev/null", ORDWR); - current = root = addpage(nil, "root", nil, nil, -1); - - if(*argv == nil && !imode) - addpage(root, "stdin", popenfile, strdup("/fd/0"), -1); - for(; *argv; argv++) - addpage(root, shortname(*argv), popenfile, strdup(*argv), -1); - - for(;;){ - i=event(&e); - switch(i){ - case Emouse: - lockdisplay(display); - m = e.mouse; - if(m.buttons & 1){ - if(current == nil || !canqlock(current)) - goto Unlock; - for(;;) { - o = m.xy; - m = emouse(); - if((m.buttons & 1) == 0) - break; - translate(current, subpt(m.xy, o)); - } - qunlock(current); - goto Unlock; - } - if(m.buttons & 2){ - i = emenuhit(2, &m, &menu); - if(i < 0 || i >= nelem(menuitems) || menuitems[i]==nil) - goto Unlock; - s = menuitems[i]; - if(strcmp(s, "orig size")==0){ - pos = ZP; - zoom = 1; - resize = ZP; - rotate = 0; - Unload: - viewgen++; - unlockdisplay(display); - esetcursor(&reading); - unloadpages(0); - showpage(current); - continue; - } - if(strncmp(s, "rotate ", 7)==0){ - rotate += atoi(s+7); - rotate %= 360; - goto Unload; - } - if(strcmp(s, "upside down")==0){ - rotate += 180; - goto Unload; - } - if(strcmp(s, "fit width")==0){ - pos = ZP; - zoom = 1; - resize = subpt(screen->r.max, screen->r.min); - resize.y = 0; - goto Unload; - } - if(strcmp(s, "fit height")==0){ - pos = ZP; - zoom = 1; - resize = subpt(screen->r.max, screen->r.min); - resize.x = 0; - goto Unload; - } - if(strncmp(s, "zoom", 4)==0){ - if(current && canqlock(current)){ - o = subpt(m.xy, screen->r.min); - if(strstr(s, "in")){ - if(zoom < 0x40000000){ - zoom *= 2; - pos = addpt(mulpt(subpt(pos, o), 2), o); - } - }else{ - if(zoom > 1){ - zoom /= 2; - pos = addpt(divpt(subpt(pos, o), 2), o); - } - } - drawpage(current); - qunlock(current); - } - } - unlockdisplay(display); - if(strcmp(s, "next")==0) - showpage(nextpage(current)); - if(strcmp(s, "prev")==0) - showpage(prevpage(current)); - if(strcmp(s, "zerox")==0) - zerox(current); - if(strcmp(s, "quit")==0) - exits(0); - continue; - } - if(m.buttons & 4){ - if(root->down == nil) - goto Unlock; - pagemenu.lasthit = pageindex(current); - i = emenuhit(3, &m, &pagemenu); - unlockdisplay(display); - if(i != -1) - showpage(pageat(i)); - continue; - } - Unlock: - unlockdisplay(display); - break; - case Ekeyboard: - switch(e.kbdc){ - case 'q': - case Kdel: - case Keof: - exits(0); - case Kup: - if(current == nil || !canqlock(current)) - break; - lockdisplay(display); - if(pos.y < 0){ - translate(current, Pt(0, Dy(screen->r)/2)); - unlockdisplay(display); - qunlock(current); - continue; - } - unlockdisplay(display); - qunlock(current); - if(prevpage(current)) - pos.y = 0; - case Kleft: - showpage(prevpage(current)); - break; - case Kdown: - if(current == nil || !canqlock(current)) - break; - o = addpt(pos, pagesize(current)); - lockdisplay(display); - if(o.y > Dy(screen->r)){ - translate(current, Pt(0, -Dy(screen->r)/2)); - unlockdisplay(display); - qunlock(current); - continue; - } - unlockdisplay(display); - qunlock(current); - if(nextpage(current)) - pos.y = 0; - case ' ': - case Kright: - showpage(nextpage(current)); - break; - } - break; - case Eplumb: - pm = e.v; - if(pm && pm->ndata > 0){ - int fd; - - fd = -1; - s = plumblookup(pm->attr, "action"); - if(s && strcmp(s, "quit")==0) - exits(0); - if(s && strcmp(s, "showdata")==0){ - static ulong plumbid; - - if((fd = createtmp(plumbid++, "plumb")) < 0){ - fprint(2, "plumb: createtmp: %r\n"); - goto Plumbfree; - } - s = malloc(NPATH); - if(fd2path(fd, s, NPATH) < 0){ - close(fd); - goto Plumbfree; - } - write(fd, pm->data, pm->ndata); - }else if(pm->data[0] == '/'){ - s = strdup(pm->data); - }else{ - s = malloc(strlen(pm->wdir)+1+pm->ndata+1); - sprint(s, "%s/%s", pm->wdir, pm->data); - cleanname(s); - } - showpage(addpage(root, shortname(s), popenfile, s, fd)); - } - Plumbfree: - plumbfree(pm); - break; - } - } -} diff --git a/sys/src/cmd/page.c b/sys/src/cmd/page.c new file mode 100644 index 000000000..a0751a5eb --- /dev/null +++ b/sys/src/cmd/page.c @@ -0,0 +1,1282 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef struct Page Page; +struct Page { + char *label; + + QLock; + void *data; + int (*open)(Page *); + + char *text; + Image *image; + int fd; + int gen; + + Page *up; + Page *next; + Page *down; + Page *tail; +}; + +int zoom = 1; +int ppi = 100; +int imode; +int newwin; +int rotate; +int viewgen; +int pagegen; +Point resize, pos; +Page *root, *current; +QLock pagelock; +int nullfd; + +char pagespool[] = "/tmp/pagespool."; + +enum { + NPROC = 4, + NAHEAD = 2, + NBUF = 8*1024, + NPATH = 1024, +}; + +char *pagemenugen(int i); + +char *menuitems[] = { + "orig size", + "rotate 90", + "upside down", + "", + "fit width", + "fit height", + "", + "zoom in", + "zoom out", + "", + "next", + "prev", + "zerox", + "", + "quit", + nil +}; + +Menu pagemenu = { + nil, + pagemenugen, + -1, +}; + +Menu menu = { + menuitems, + nil, + -1, +}; + +Cursor reading = { + {-1, -1}, + {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00, + 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0, + 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0, + 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, }, + {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00, + 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0, + 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40, + 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, } +}; + +void showpage(Page *); +void drawpage(Page *); +Point pagesize(Page *); + +Page* +addpage(Page *up, char *label, int (*popen)(Page *), void *pdata, int fd) +{ + Page *p; + + p = mallocz(sizeof(*p), 1); + p->label = strdup(label); + p->gen = pagegen; + p->text = nil; + p->image = nil; + p->data = pdata; + p->open = popen; + p->fd = fd; + + p->down = nil; + p->tail = nil; + p->next = nil; + + qlock(&pagelock); + if(p->up = up){ + if(up->tail == nil) + up->down = up->tail = p; + else { + up->tail->next = p; + up->tail = p; + } + } + qunlock(&pagelock); + + if(up && current == up) + showpage(p); + return p; +} + +void +resizewin(Point size) +{ + int wctl; + + if((wctl = open("/dev/wctl", OWRITE)) < 0) + return; + /* add rio border */ + size = addpt(size, Pt(Borderwidth*2, Borderwidth*2)); + fprint(wctl, "resize -dx %d -dy %d\n", size.x, size.y); + close(wctl); +} + +int +createtmp(ulong id, char *pfx) +{ + char nam[64]; + + snprint(nam, sizeof nam, "%s%s%.12d%.8lux", pagespool, pfx, getpid(), id ^ 0xcafebabe); + return create(nam, OEXCL|ORCLOSE|ORDWR, 0600); +} + +int +catchnote(void *, char *msg) +{ + if(strstr(msg, "sys: write on closed pipe")) + return 1; + if(strstr(msg, "hangup")) + return 1; + if(strstr(msg, "alarm")) + return 1; + return 0; +} + +void +pipeline(int fd, char *fmt, ...) +{ + char buf[128], *argv[4]; + va_list arg; + int pfd[2]; + + if(pipe(pfd) < 0){ + Err: + dup(nullfd, fd); + return; + } + switch(rfork(RFPROC|RFFDG|RFREND|RFNOWAIT)){ + case -1: + close(pfd[0]); + close(pfd[1]); + goto Err; + case 0: + if(dup(fd, 0)<0) + exits("dup"); + if(dup(pfd[1], 1)<0) + exits("dup"); + close(fd); + close(pfd[1]); + close(pfd[0]); + va_start(arg, fmt); + vsnprint(buf, sizeof buf, fmt, arg); + va_end(arg); + + argv[0] = "rc"; + argv[1] = "-c"; + argv[2] = buf; + argv[3] = nil; + exec("/bin/rc", argv); + sysfatal("exec: %r"); + } + close(pfd[1]); + dup(pfd[0], fd); + close(pfd[0]); +} + +int +popenfile(Page*); + +int +popenconv(Page *p) +{ + char nam[NPATH]; + int fd; + + if((fd = dup(p->fd, -1)) < 0){ + close(p->fd); + p->fd = -1; + return -1; + } + + seek(fd, 0, 0); + if(p->data) + pipeline(fd, "%s", (char*)p->data); + + /* + * dont keep the file descriptor arround if it can simply + * be reopened. + */ + fd2path(p->fd, nam, sizeof(nam)); + if(strncmp(nam, pagespool, strlen(pagespool))){ + close(p->fd); + p->fd = -1; + p->data = strdup(nam); + p->open = popenfile; + } + + return fd; +} + +typedef struct Ghost Ghost; +struct Ghost +{ + QLock; + + int pin; + int pout; + int pdat; +}; + +int +popenpdf(Page *p) +{ + char buf[NBUF]; + int n, pfd[2]; + Ghost *gs; + + if(pipe(pfd) < 0) + return -1; + switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){ + case -1: + close(pfd[0]); + close(pfd[1]); + return -1; + case 0: + close(pfd[0]); + gs = p->data; + qlock(gs); + fprint(gs->pin, "%s DoPDFPage\n" + "(/fd/3) (w) file " + "dup flushfile " + "dup (THIS IS NOT AN INFERNO BITMAP\\n) writestring " + "flushfile\n", p->label); + while((n = read(gs->pdat, buf, sizeof buf)) > 0){ + if(memcmp(buf, "THIS IS NOT AN INFERNO BITMAP\n", 30) == 0) + break; + if(pfd[1] < 0) + continue; + if(write(pfd[1], buf, n) != n){ + close(pfd[1]); + pfd[1]=-1; + } + } + qunlock(gs); + exits(nil); + } + close(pfd[1]); + return pfd[0]; +} + +int +infernobithdr(char *buf, int n) +{ + if(n >= 11){ + if(memcmp(buf, "compressed\n", 11) == 0) + return 1; + if(strtochan((char*)buf)) + return 1; + if(memcmp(buf, " ", 10) == 0 && + '0' <= buf[10] && buf[10] <= '9' && + buf[11] == ' ') + return 1; + } + return 0; +} + +int +popengs(Page *p) +{ + int n, i, pdf, ifd, ofd, pin[2], pout[2], pdat[2]; + char buf[NBUF], nam[32], *argv[16]; + + pdf = 0; + ifd = p->fd; + p->fd = -1; + seek(ifd, 0, 0); + if(read(ifd, buf, 5) != 5) + goto Err0; + seek(ifd, 0, 0); + if(memcmp(buf, "%PDF-", 5) == 0) + pdf = 1; + p->text = strdup(p->label); + if(pipe(pin) < 0){ + Err0: + close(ifd); + return -1; + } + if(pipe(pout) < 0){ + Err1: + close(pin[0]); + close(pin[1]); + goto Err0; + } + if(pipe(pdat) < 0){ + Err2: + close(pdat[0]); + close(pdat[1]); + goto Err1; + } + + switch(rfork(RFREND|RFPROC|RFFDG|RFNOWAIT)){ + case -1: + goto Err2; + case 0: + if(pdf){ + if(dup(pin[1], 0)<0) + exits("dup"); + if(dup(pout[1], 1)<0) + exits("dup"); + } else { + if(dup(nullfd, 0)<0) + exits("dup"); + if(dup(nullfd, 1)<0) + exits("dup"); + } + if(dup(nullfd, 2)<0) + exits("dup"); + if(dup(pdat[1], 3)<0) + exits("dup"); + if(dup(ifd, 4)<0) + exits("dup"); + + close(pin[0]); + close(pin[1]); + close(pout[0]); + close(pout[1]); + close(pdat[0]); + close(pdat[1]); + close(ifd); + + if(p->data) + pipeline(4, "%s", (char*)p->data); + + argv[0] = "gs"; + argv[1] = "-q"; + argv[2] = "-sDEVICE=plan9"; + argv[3] = "-sOutputFile=/fd/3"; + argv[4] = "-dBATCH"; + argv[5] = pdf ? "-dDELAYSAFER" : "-dSAFER"; + argv[6] = "-dQUIET"; + argv[7] = "-dTextAlphaBits=4"; + argv[8] = "-dGraphicsAlphaBits=4"; + snprint(buf, sizeof buf, "-r%d", ppi); + argv[9] = buf; + argv[10] = "-dDOINTERPOLATE"; + argv[11] = pdf ? "-" : "/fd/4"; + argv[12] = nil; + exec("/bin/gs", argv); + sysfatal("exec: %r"); + } + + close(pin[1]); + close(pout[1]); + close(pdat[1]); + close(ifd); + + if(pdf){ + Ghost *gs; + char *prolog = + "/PAGEOUT (/fd/1) (w) file def\n" + "/PAGE== { PAGEOUT exch write==only PAGEOUT (\\n) writestring PAGEOUT flushfile } def\n" + "\n" + "/Page null def\n" + "/Page# 0 def\n" + "/PDFSave null def\n" + "/DSCPageCount 0 def\n" + "/DoPDFPage {dup /Page# exch store pdfgetpage pdfshowpage } def\n" + "\n" + "GS_PDF_ProcSet begin\n" + "pdfdict begin\n" + "(/fd/4) (r) file { DELAYSAFER { .setsafe } if } stopped pop pdfopen begin\n" + "\n" + "pdfpagecount PAGE==\n"; + + n = strlen(prolog); + if(write(pin[0], prolog, n) != n) + goto Out; + if((n = read(pout[0], buf, sizeof(buf)-1)) < 0) + goto Out; + buf[n] = 0; + n = atoi(buf); + if(n <= 0){ + werrstr("no pages"); + goto Out; + } + gs = mallocz(sizeof(*gs), 1); + gs->pin = pin[0]; + gs->pout = pout[0]; + gs->pdat = pdat[0]; + for(i=1; i<=n; i++){ + snprint(nam, sizeof nam, "%d", i); + addpage(p, nam, popenpdf, gs, -1); + } + + /* keep ghostscript arround */ + return -1; + } else { + i = 0; + ofd = -1; + while((n = read(pdat[0], buf, sizeof(buf))) >= 0){ + if(ofd >= 0 && (n <= 0 || infernobithdr(buf, n))){ + snprint(nam, sizeof nam, "%d", i); + addpage(p, nam, popenconv, nil, ofd); + ofd = -1; + } + if(n <= 0) + break; + if(ofd < 0){ + snprint(nam, sizeof nam, "%.4d", ++i); + if((ofd = createtmp((ulong)p, nam)) < 0) + ofd = dup(nullfd, -1); + } + if(write(ofd, buf, n) != n) + break; + } + if(ofd >= 0) + close(ofd); + } +Out: + close(pin[0]); + close(pout[0]); + close(pdat[0]); + return -1; +} + +int +popenfile(Page *p) +{ + char buf[NBUF], *file; + int i, n, fd, tfd; + Dir *d; + + fd = p->fd; + p->fd = -1; + file = p->data; + if(fd < 0){ + if((fd = open(file, OREAD)) < 0){ + Err0: + p->data = nil; + free(file); + return -1; + } + } + seek(fd, 0, 0); + if((d = dirfstat(fd)) == nil){ + Err1: + close(fd); + goto Err0; + } + if(d->mode & DMDIR){ + free(d); + d = nil; + if((n = dirreadall(fd, &d)) < 0) + goto Err1; + for(i = 0; itext = strdup(p->label); + goto Err1; + } + free(d); + + memset(buf, 0, 32+1); + if((n = read(fd, buf, 32)) <= 0) + goto Err1; + + p->fd = fd; + p->data = nil; + p->open = popenconv; + if(memcmp(buf, "%PDF-", 5) == 0 || strstr(buf, "%!")) + p->open = popengs; + else if(memcmp(buf, "x T ", 4) == 0){ + p->data = "lp -dstdout"; + p->open = popengs; + } + else if(memcmp(buf, "\xF7\x02\x01\x83\x92\xC0\x1C;", 8) == 0){ + p->data = "dvips -Pps -r0 -q1 -f1"; + p->open = popengs; + } + else if(memcmp(buf, "\x1F\x8B", 2) == 0){ + p->data = "gunzip"; + p->open = popengs; + } + else if(memcmp(buf, "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", 8) == 0){ + p->data = "doc2ps"; + p->open = popengs; + } + else if(memcmp(buf, "GIF", 3) == 0) + p->data = "gif -t9"; + else if(memcmp(buf, "\111\111\052\000", 4) == 0) + p->data = "fb/tiff2pic | fb/3to1 rgbv | fb/pcp -tplan9"; + else if(memcmp(buf, "\115\115\000\052", 4) == 0) + p->data = "fb/tiff2pic | fb/3to1 rgbv | fb/pcp -tplan9"; + else if(memcmp(buf, "\377\330\377", 3) == 0) + p->data = "jpg -t9"; + else if(memcmp(buf, "\211PNG\r\n\032\n", 3) == 0) + p->data = "png -t9"; + else if(memcmp(buf, "\0PC Research, Inc", 17) == 0) + p->data = "aux/g3p9bit -g"; + else if(memcmp(buf, "TYPE=ccitt-g31", 14) == 0) + p->data = "aux/g3p9bit -g"; + else if(memcmp(buf, "II*", 3) == 0) + p->data = "aux/g3p9bit -g"; + else if(memcmp(buf, "TYPE=", 5) == 0) + p->data = "fb/3to1 rgbv |fb/pcp -tplan9"; + else if(buf[0] == 'P' && '0' <= buf[1] && buf[1] <= '9') + p->data = "ppm -t9"; + else if(memcmp(buf, "BM", 2) == 0) + p->data = "bmp -t9"; + else if(infernobithdr(buf, n)) + p->data = nil; + else { + werrstr("unknown image format"); + goto Err1; + } + + if(seek(fd, 0, 0) < 0) + goto Noseek; + if((i = read(fd, buf+n, n)) < 0) + goto Err1; + if(i != n || memcmp(buf, buf+n, i)){ + n += i; + Noseek: + if((tfd = createtmp((ulong)p, "file")) < 0) + goto Err1; + while(n > 0){ + if(write(tfd, buf, n) != n) + goto Err2; + if((n = read(fd, buf, sizeof(buf))) < 0) + goto Err2; + } + if(dup(tfd, fd) < 0){ + Err2: + close(tfd); + goto Err1; + } + close(tfd); + } + free(file); + return p->open(p); +} + +Page* +nextpage(Page *p) +{ + if(p){ + if(p->down) + return p->down; + if(p->next) + return p->next; + if(p->up) + return p->up->next; + } + return nil; +} + +Page* +prevpage(Page *x) +{ + Page *p, *t; + + if(x){ + for(p = root->down; p; p = t) + if((t = nextpage(p)) == x) + return p; + } + return nil; +} + +int +openpage(Page *p) +{ + int fd; + + fd = -1; + if(p->open == nil || (fd = p->open(p)) < 0) + p->open = nil; + else { + if(rotate) + pipeline(fd, "rotate -r %d", rotate); + if(resize.x) + pipeline(fd, "resize -x %d", resize.x); + else if(resize.y) + pipeline(fd, "resize -y %d", resize.y); + } + return fd; +} + +void +loadpage(Page *p) +{ + int fd; + + if(p->open && p->image == nil && p->text == nil){ + if((fd = openpage(p)) >= 0){ + pagegen++; + p->image = readimage(display, fd, 1); + close(fd); + } + if(p->image == nil && p->text == nil) + p->text = smprint("%s: %r", p->label); + } + p->gen = pagegen; +} + +void +unloadpage(Page *p) +{ + if(p->open){ + if(p->text) + free(p->text); + p->text = nil; + if(p->image){ + lockdisplay(display); + freeimage(p->image); + unlockdisplay(display); + } + p->image = nil; + } +} + +void +unloadpages(int age) +{ + Page *p; + + for(p = root->down; p; p = nextpage(p)){ + if(age == 0) /* synchronous flush */ + qlock(p); + else if(!canqlock(p)) + continue; + if((pagegen - p->gen) >= age) + unloadpage(p); + qunlock(p); + } +} + +void +loadpages(Page *p, int ahead, int oviewgen) +{ + int i; + + ahead++; /* load at least one */ + unloadpages(ahead*2); + for(i = 0; i < ahead && p; p = nextpage(p), i++){ + if(viewgen != oviewgen) + break; + if(canqlock(p)){ + loadpage(p); + if(viewgen != oviewgen){ + unloadpage(p); + qunlock(p); + break; + } + if(p == current){ + Point size; + + esetcursor(nil); + size = pagesize(p); + if(size.x && size.y && newwin){ + newwin = 0; + resizewin(size); + } + lockdisplay(display); + drawpage(p); + unlockdisplay(display); + } + qunlock(p); + } + } +} + +/* + * A draw operation that touches only the area contained in bot but not in top. + * mp and sp get aligned with bot.min. + */ +static void +gendrawdiff(Image *dst, Rectangle bot, Rectangle top, + Image *src, Point sp, Image *mask, Point mp, int op) +{ + Rectangle r; + Point origin; + Point delta; + + USED(op); + + if(Dx(bot)*Dy(bot) == 0) + return; + + /* no points in bot - top */ + if(rectinrect(bot, top)) + return; + + /* bot - top ≡ bot */ + if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){ + gendrawop(dst, bot, src, sp, mask, mp, op); + return; + } + + origin = bot.min; + /* split bot into rectangles that don't intersect top */ + /* left side */ + if(bot.min.x < top.min.x){ + r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.min.x = top.min.x; + } + + /* right side */ + if(bot.max.x > top.max.x){ + r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.max.x = top.max.x; + } + + /* top */ + if(bot.min.y < top.min.y){ + r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.min.y = top.min.y; + } + + /* bottom */ + if(bot.max.y > top.max.y){ + r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.max.y = top.max.y; + } +} + +void +zoomdraw(Image *d, Rectangle r, Rectangle top, Image *s, Point sp, int f) +{ + int w, x, y; + Image *t; + Point a; + + if(f <= 1){ + gendrawdiff(d, r, top, s, sp, nil, ZP, S); + return; + } + a = ZP; + if(r.min.x < d->r.min.x){ + sp.x += (d->r.min.x - r.min.x)/f; + a.x = (d->r.min.x - r.min.x)%f; + r.min.x = d->r.min.x; + } + if(r.min.y < d->r.min.y){ + sp.y += (d->r.min.y - r.min.y)/f; + a.y = (d->r.min.y - r.min.y)%f; + r.min.y = d->r.min.y; + } + rectclip(&r, d->r); + w = s->r.max.x - sp.x; + if(w > Dx(r)) + w = Dx(r); + t = allocimage(display, Rect(r.min.x, r.min.y, r.min.x+w, r.max.y), s->chan, 0, DNofill); + if(t == nil) + return; + for(y=r.min.y; yimage ? mulpt(subpt(p->image->r.max, p->image->r.min), zoom) : ZP; +} + +void +drawpage(Page *p) +{ + Rectangle r; + Image *i; + + if((i = p->image) == nil){ + char *s; + + if((s = p->text) == nil) + s = "..."; + r.min = ZP; + r.max = stringsize(font, p->text); + r = rectaddpt(r, addpt(subpt(divpt(subpt(screen->r.max, screen->r.min), 2), divpt(r.max, 2)), + screen->r.min)); + draw(screen, r, display->white, nil, ZP); + string(screen, r.min, display->black, ZP, font, s); + } else { + r = rectaddpt(Rpt(ZP, pagesize(p)), addpt(pos, screen->r.min)); + zoomdraw(screen, r, ZR, i, i->r.min, zoom); + } + gendrawdiff(screen, screen->r, r, display->white, ZP, nil, ZP, S); + border(screen, r, -Borderwidth, display->black, ZP); + flushimage(display, 1); +} + +void +translate(Page *p, Point d) +{ + Rectangle r, or, nr; + Image *i; + + i = p->image; + if((i==0) || (d.x==0 && d.y==0)) + return; + r = rectaddpt(Rpt(ZP, pagesize(p)), addpt(pos, screen->r.min)); + pos = addpt(pos, d); + nr = rectaddpt(r, d); + or = r; + rectclip(&or, screen->r); + draw(screen, rectaddpt(or, d), screen, nil, or.min); + zoomdraw(screen, nr, rectaddpt(or, d), i, i->r.min, zoom); + gendrawdiff(screen, screen->r, nr, display->white, ZP, nil, ZP, S); + border(screen, nr, -Borderwidth, display->black, ZP); + flushimage(display, 1); +} + +Page* +findpage(char *name) +{ + Page *p; + int n; + + n = strlen(name); + /* look in current document first */ + if(current && current->up){ + for(p = current->up->down; p; p = p->next) + if(cistrncmp(p->label, name, n) == 0) + return p; + } + /* look everywhere */ + for(p = root->down; p; p = nextpage(p)) + if(cistrncmp(p->label, name, n) == 0) + return p; + return nil; +} + +Page* +pageat(int i) +{ + Page *p; + + for(p = root->down; i > 0 && p; p = nextpage(p)) + i--; + return i ? nil : p; +} + +int +pageindex(Page *x) +{ + Page *p; + int i; + + for(i = 0, p = root->down; p && p != x; p = nextpage(p)) + i++; + return (p == x) ? i : -1; +} + +char* +pagemenugen(int i) +{ + Page *p; + if(p = pageat(i)) + return p->label; + return nil; +} + +void +showpage(Page *p) +{ + static int nproc; + int oviewgen; + + if(p == nil) + return; + esetcursor(&reading); + current = p; + oviewgen = viewgen; + if(++nproc > NPROC) + if(waitpid() > 0) + nproc--; + switch(rfork(RFPROC|RFMEM)){ + case -1: + sysfatal("rfork: %r"); + case 0: + loadpages(p, NAHEAD, oviewgen); + exits(nil); + } +} + +void +zerox(Page *p) +{ + char nam[64], *argv[4]; + int fd; + + if(p == nil) + return; + esetcursor(&reading); + qlock(p); + if((fd = openpage(p)) < 0) + goto Out; + if(rfork(RFREND|RFFDG|RFPROC|RFENVG|RFNOTEG|RFNOWAIT) == 0){ + dup(fd, 0); + close(fd); + + snprint(nam, sizeof nam, "/bin/%s", argv0); + argv[0] = argv0; + argv[1] = "-w"; + argv[2] = nil; + exec(nam, argv); + sysfatal("exec: %r"); + } + close(fd); +Out: + qunlock(p); + esetcursor(nil); +} + +void +eresized(int new) +{ + Page *p; + + lockdisplay(display); + if(new && getwindow(display, Refnone) == -1) + sysfatal("getwindow: %r"); + if(p = current){ + if(canqlock(p)){ + drawpage(p); + qunlock(p); + } + } + unlockdisplay(display); +} + +void killcohort(void) +{ + int i; + for(i=0;i!=3;i++){ /* It's a long way to the kitchen */ + postnote(PNGROUP, getpid(), "kill"); + sleep(1); + } +} + +void drawerr(Display *, char *msg) +{ + sysfatal("draw: %s", msg); +} + +char* +shortname(char *s) +{ + char *x; + if(x = strrchr(s, '/')) + if(x[1] != 0) + return x+1; + return s; +} + +void +usage(void) +{ + fprint(2, "usage: %s [ -iRw ] [ -p ppi ] [ file ... ]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + enum { Eplumb = 4 }; + char jump[32]; + Plumbmsg *pm; + Point o; + Mouse m; + Event e; + char *s; + int i; + + ARGBEGIN { + case 'a': + case 'v': + case 'V': + case 'P': + break; + case 'R': + newwin = -1; + break; + case 'w': + newwin = 1; + break; + case 'i': + imode = 1; + break; + case 'p': + ppi = atoi(EARGF(usage())); + break; + default: + usage(); + } ARGEND; + + /* + * so that we can stop all subprocesses with a note, + * and to isolate rendezvous from other processes + */ + rfork(RFNOTEG|RFNAMEG|RFREND); + atexit(killcohort); + atnotify(catchnote, 1); + if(newwin > 0){ + s = smprint("-pid %d", getpid()); + if(newwindow(s) < 0) + sysfatal("newwindow: %r"); + free(s); + } + initdraw(drawerr, nil, argv0); + display->locking = 1; + unlockdisplay(display); + einit(Ekeyboard|Emouse); + eplumb(Eplumb, "image"); + nullfd = open("/dev/null", ORDWR); + current = root = addpage(nil, "root", nil, nil, -1); + + if(*argv == nil && !imode) + addpage(root, "stdin", popenfile, strdup("/fd/0"), -1); + for(; *argv; argv++) + addpage(root, shortname(*argv), popenfile, strdup(*argv), -1); + + jump[0] = 0; + for(;;){ + i=event(&e); + switch(i){ + case Emouse: + lockdisplay(display); + m = e.mouse; + if(m.buttons & 1){ + if(current == nil || !canqlock(current)) + goto Unlock; + for(;;) { + o = m.xy; + m = emouse(); + if((m.buttons & 1) == 0) + break; + translate(current, subpt(m.xy, o)); + } + qunlock(current); + goto Unlock; + } + if(m.buttons & 2){ + i = emenuhit(2, &m, &menu); + if(i < 0 || i >= nelem(menuitems) || menuitems[i]==nil) + goto Unlock; + s = menuitems[i]; + if(strcmp(s, "orig size")==0){ + pos = ZP; + zoom = 1; + resize = ZP; + rotate = 0; + Unload: + viewgen++; + unlockdisplay(display); + esetcursor(&reading); + unloadpages(0); + showpage(current); + continue; + } + if(strncmp(s, "rotate ", 7)==0){ + rotate += atoi(s+7); + rotate %= 360; + goto Unload; + } + if(strcmp(s, "upside down")==0){ + rotate += 180; + goto Unload; + } + if(strcmp(s, "fit width")==0){ + pos = ZP; + zoom = 1; + resize = subpt(screen->r.max, screen->r.min); + resize.y = 0; + goto Unload; + } + if(strcmp(s, "fit height")==0){ + pos = ZP; + zoom = 1; + resize = subpt(screen->r.max, screen->r.min); + resize.x = 0; + goto Unload; + } + if(strncmp(s, "zoom", 4)==0){ + if(current && canqlock(current)){ + o = subpt(m.xy, screen->r.min); + if(strstr(s, "in")){ + if(zoom < 0x40000000){ + zoom *= 2; + pos = addpt(mulpt(subpt(pos, o), 2), o); + } + }else{ + if(zoom > 1){ + zoom /= 2; + pos = addpt(divpt(subpt(pos, o), 2), o); + } + } + drawpage(current); + qunlock(current); + } + } + unlockdisplay(display); + if(strcmp(s, "next")==0) + showpage(nextpage(current)); + if(strcmp(s, "prev")==0) + showpage(prevpage(current)); + if(strcmp(s, "zerox")==0) + zerox(current); + if(strcmp(s, "quit")==0) + exits(0); + continue; + } + if(m.buttons & 4){ + if(root->down == nil) + goto Unlock; + pagemenu.lasthit = pageindex(current); + i = emenuhit(3, &m, &pagemenu); + unlockdisplay(display); + if(i != -1) + showpage(pageat(i)); + continue; + } + Unlock: + unlockdisplay(display); + break; + case Ekeyboard: + switch(e.kbdc){ + case 'q': + case Kdel: + case Keof: + exits(0); + case Kup: + if(current == nil || !canqlock(current)) + break; + lockdisplay(display); + if(pos.y < 0){ + translate(current, Pt(0, Dy(screen->r)/2)); + unlockdisplay(display); + qunlock(current); + continue; + } + unlockdisplay(display); + qunlock(current); + if(prevpage(current)) + pos.y = 0; + case '-': + case Kbs: + case Kleft: + showpage(prevpage(current)); + break; + case Kdown: + if(current == nil || !canqlock(current)) + break; + o = addpt(pos, pagesize(current)); + lockdisplay(display); + if(o.y > Dy(screen->r)){ + translate(current, Pt(0, -Dy(screen->r)/2)); + unlockdisplay(display); + qunlock(current); + continue; + } + unlockdisplay(display); + qunlock(current); + if(nextpage(current)) + pos.y = 0; + case '\n': + if(jump[0]){ + showpage(findpage(jump)); + jump[0] = 0; + break; + } + case ' ': + case Kright: + showpage(nextpage(current)); + break; + default: + i = strlen(jump); + if(i+1 < sizeof(jump)){ + jump[i] = e.kbdc; + jump[i+1] = 0; + } + } + break; + case Eplumb: + pm = e.v; + if(pm && pm->ndata > 0){ + int fd; + + fd = -1; + s = plumblookup(pm->attr, "action"); + if(s && strcmp(s, "quit")==0) + exits(0); + if(s && strcmp(s, "showdata")==0){ + static ulong plumbid; + + if((fd = createtmp(plumbid++, "plumb")) < 0){ + fprint(2, "plumb: createtmp: %r\n"); + goto Plumbfree; + } + s = malloc(NPATH); + if(fd2path(fd, s, NPATH) < 0){ + close(fd); + goto Plumbfree; + } + write(fd, pm->data, pm->ndata); + }else if(pm->data[0] == '/'){ + s = strdup(pm->data); + }else{ + s = malloc(strlen(pm->wdir)+1+pm->ndata+1); + sprint(s, "%s/%s", pm->wdir, pm->data); + cleanname(s); + } + showpage(addpage(root, shortname(s), popenfile, s, fd)); + } + Plumbfree: + plumbfree(pm); + break; + } + } +} diff --git a/sys/src/cmd/page/cache.c b/sys/src/cmd/page/cache.c deleted file mode 100644 index 376a075ed..000000000 --- a/sys/src/cmd/page/cache.c +++ /dev/null @@ -1,187 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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; iim) - 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; idoc == 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; inpage < 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; -} diff --git a/sys/src/cmd/page/filter.c b/sys/src/cmd/page/filter.c deleted file mode 100644 index 07c3df2b2..000000000 --- a/sys/src/cmd/page/filter.c +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include -#include -#include -#include -#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); -} diff --git a/sys/src/cmd/page/gfx.c b/sys/src/cmd/page/gfx.c deleted file mode 100644 index 57ebc746b..000000000 --- a/sys/src/cmd/page/gfx.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * graphics file reading for page - */ - -#include -#include -#include -#include -#include -#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; iextra; - - assert((name == nil) ^ (buf == nil)); - assert(name != nil || doc->npage == 0); - - for(i=0; inpage; 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; inpage; 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]; -} - diff --git a/sys/src/cmd/page/gs.c b/sys/src/cmd/page/gs.c deleted file mode 100644 index 3ff04685b..000000000 --- a/sys/src/cmd/page/gs.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#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); -} diff --git a/sys/src/cmd/page/mkfile b/sys/src/cmd/page/mkfile deleted file mode 100644 index cbb8c9756..000000000 --- a/sys/src/cmd/page/mkfile +++ /dev/null @@ -1,36 +0,0 @@ -pdfprolog.c - -pdf.$O: pdfprolog.c - diff --git a/sys/src/cmd/page/nrotate.c b/sys/src/cmd/page/nrotate.c deleted file mode 100644 index 2225ec3f1..000000000 --- a/sys/src/cmd/page/nrotate.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#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); -} - diff --git a/sys/src/cmd/page/page.c b/sys/src/cmd/page/page.c deleted file mode 100644 index 85e55586c..000000000 --- a/sys/src/cmd/page/page.c +++ /dev/null @@ -1,238 +0,0 @@ -#include -#include -#include -#include -#include -#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); -} diff --git a/sys/src/cmd/page/page.h b/sys/src/cmd/page/page.h deleted file mode 100644 index ac66df6b1..000000000 --- a/sys/src/cmd/page/page.h +++ /dev/null @@ -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) diff --git a/sys/src/cmd/page/pdf.c b/sys/src/cmd/page/pdf.c deleted file mode 100644 index 5261eab59..000000000 --- a/sys/src/cmd/page/pdf.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * pdf.c - * - * pdf file support for page - */ - -#include -#include -#include -#include -#include -#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; ipagebbox[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; -} diff --git a/sys/src/cmd/page/pdfprolog.ps b/sys/src/cmd/page/pdfprolog.ps deleted file mode 100644 index 681e0587a..000000000 --- a/sys/src/cmd/page/pdfprolog.ps +++ /dev/null @@ -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 { % pdfshowpage_mysetpage - 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 diff --git a/sys/src/cmd/page/ps.c b/sys/src/cmd/page/ps.c deleted file mode 100644 index f1e4cbf93..000000000 --- a/sys/src/cmd/page/ps.c +++ /dev/null @@ -1,450 +0,0 @@ -/* - * ps.c - * - * provide postscript file reading support for page - */ - -#include -#include -#include -#include -#include -#include -#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; inpage = 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; -} diff --git a/sys/src/cmd/page/rotate.c b/sys/src/cmd/page/rotate.c deleted file mode 100644 index 7f4083e8a..000000000 --- a/sys/src/cmd/page/rotate.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#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; granr); 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; granr); 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= 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= 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; ichan){ - 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; ir); 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; ir)); i++){ - nscan[i] = malloc(bpl); - if(nscan[i] == nil) - sysfatal("can't allocate: %r"); - } - - /* resample in X */ - nchan = from->depth/8; - for(i=0; ir); i++){ - for(j=0; jchan==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; ir), nscan, ysize); - - /* pack data into destination */ - bpl = bytesperline(to->r, from->depth); - for(i=0; ir); i++){ - free(oscan[i]); - free(nscan[i]); - } - free(oscan); - free(nscan); - - return to; -} diff --git a/sys/src/cmd/page/util.c b/sys/src/cmd/page/util.c deleted file mode 100644 index 3c27f9c03..000000000 --- a/sys/src/cmd/page/util.c +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include -#include -#include -#include -#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 */ -} diff --git a/sys/src/cmd/page/view.c b/sys/src/cmd/page/view.c deleted file mode 100644 index 4a32916a9..000000000 --- a/sys/src/cmd/page/view.c +++ /dev/null @@ -1,1073 +0,0 @@ -/* - * the actual viewer that handles screen stuff - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "page.h" - -Document *doc; -Image *im; -Image *tofree; -int page; -int angle = 0; -int showbottom = 0; /* on the next showpage, move the image so the bottom is visible. */ - -Rectangle ulrange; /* the upper left corner of the image must be in this rectangle */ -Point ul; /* the upper left corner of the image is at this point on the screen */ - -Point pclip(Point, Rectangle); -Rectangle mkrange(Rectangle screenr, Rectangle imr); -void redraw(Image*); - -Cursor reading={ - {-1, -1}, - {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00, - 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0, - 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0, - 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, }, - {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00, - 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0, - 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40, - 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, } -}; - -Cursor query = { - {-7,-7}, - {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, - 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8, - 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0, - 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, }, - {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, - 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, - 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80, - 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, } -}; - -enum { - Left = 1, - Middle = 2, - Right = 4, - - RMenu = 3, -}; - -static void -delayfreeimage(Image *m) -{ - if(m == tofree) - return; - if(tofree) - freeimage(tofree); - tofree = m; -} - -void -unhide(void) -{ - static int wctl = -1; - - if(wctl < 0) - wctl = open("/dev/wctl", OWRITE); - if(wctl < 0) - return; - - write(wctl, "unhide", 6); -} - -int -max(int a, int b) -{ - return a > b ? a : b; -} - -int -min(int a, int b) -{ - return a < b ? a : b; -} - - -char* -menugen(int n) -{ - static char menustr[32]; - char *p; - int len; - - if(n == doc->npage) - return "exit"; - if(n > doc->npage) - return nil; - - if(reverse) - n = doc->npage-1-n; - - p = doc->pagename(doc, n); - len = (sizeof menustr)-2; - - if(strlen(p) > len && strrchr(p, '/')) - p = strrchr(p, '/')+1; - if(strlen(p) > len) - p = p+strlen(p)-len; - - strcpy(menustr+1, p); - if(page == n) - menustr[0] = '>'; - else - menustr[0] = ' '; - return menustr; -} - -void -showpage(int page, Menu *m) -{ - if(doc->fwdonly) - m->lasthit = 0; /* this page */ - else - m->lasthit = reverse ? doc->npage-1-page : page; - - esetcursor(&reading); - delayfreeimage(nil); - im = cachedpage(doc, angle, page); - if(im == nil) - wexits(0); - if(resizing) - resize(Dx(im->r), Dy(im->r)); - - esetcursor(nil); - if(showbottom){ - ul.y = screen->r.max.y - Dy(im->r); - showbottom = 0; - } - - redraw(screen); - flushimage(display, 1); -} - -char* -writebitmap(void) -{ - char basename[64]; - char name[64+30]; - static char result[200]; - char *p, *q; - int fd; - - if(im == nil) - return "no image"; - - memset(basename, 0, sizeof basename); - if(doc->docname) - strncpy(basename, doc->docname, sizeof(basename)-1); - else if((p = menugen(page)) && p[0] != '\0') - strncpy(basename, p+1, sizeof(basename)-1); - - if(basename[0]) { - if(q = strrchr(basename, '/')) - q++; - else - q = basename; - if(p = strchr(q, '.')) - *p = 0; - - memset(name, 0, sizeof name); - snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1); - if(access(name, 0) >= 0) { - strcat(name, "XXXX"); - mktemp(name); - } - if(access(name, 0) >= 0) - return "couldn't think of a name for bitmap"; - } else { - strcpy(name, "bitXXXX"); - mktemp(name); - if(access(name, 0) >= 0) - return "couldn't think of a name for bitmap"; - } - - if((fd = create(name, OWRITE, 0666)) < 0) { - snprint(result, sizeof result, "cannot create %s: %r", name); - return result; - } - - if(writeimage(fd, im, 0) < 0) { - snprint(result, sizeof result, "cannot writeimage: %r"); - close(fd); - return result; - } - close(fd); - - snprint(result, sizeof result, "wrote %s", name); - return result; -} - -static void translate(Point); - -static int -showdata(Plumbmsg *msg) -{ - char *s; - - s = plumblookup(msg->attr, "action"); - return s && strcmp(s, "showdata")==0; -} - -static int -plumbquit(Plumbmsg *msg) -{ - char *s; - - s = plumblookup(msg->attr, "action"); - return s && strcmp(s, "quit")==0; -} - -/* correspond to entries in miditems[] below, - * changing one means you need to change - */ -enum{ - Restore = 0, - Zin, - Fit, - Rot, - Upside, - Empty1, - Next, - Prev, - Zerox, - Empty2, - Reverse, - Del, - Write, - Empty3, - Exit, -}; - -void -viewer(Document *dd) -{ - int i, fd, n, oldpage; - int nxt; - Menu menu, midmenu; - Mouse m; - Event e; - Point dxy, oxy, xy0; - Rectangle r; - Image *tmp; - static char *fwditems[] = { "this page", "next page", "exit", 0 }; - static char *miditems[] = { - "orig size", - "zoom in", - "fit window", - "rotate 90", - "upside down", - "", - "next", - "prev", - "zerox", - "", - "reverse", - "discard", - "write", - "", - "quit", - 0 - }; - char *s; - enum { Eplumb = 4 }; - Plumbmsg *pm; - - doc = dd; /* save global for menuhit */ - ul = screen->r.min; - einit(Emouse|Ekeyboard); - if(doc->addpage != nil) - eplumb(Eplumb, "image"); - - esetcursor(&reading); - r.min = ZP; - - /* - * im is a global pointer to the current image. - * eventually, i think we will have a layer between - * the display routines and the ps/pdf/whatever routines - * to perhaps cache and handle images of different - * sizes, etc. - */ - im = 0; - page = reverse ? doc->npage-1 : 0; - - if(doc->fwdonly) { - menu.item = fwditems; - menu.gen = 0; - menu.lasthit = 0; - } else { - menu.item = 0; - menu.gen = menugen; - menu.lasthit = 0; - } - - midmenu.item = miditems; - midmenu.gen = 0; - midmenu.lasthit = Next; - - showpage(page, &menu); - esetcursor(nil); - - nxt = 0; - for(;;) { - /* - * throughout, if doc->fwdonly is set, we restrict the functionality - * a fair amount. we don't care about doc->npage anymore, and - * all that can be done is select the next page. - */ - unlockdisplay(display); - i = eread(Emouse|Ekeyboard|Eplumb, &e); - lockdisplay(display); - switch(i){ - case Ekeyboard: - if(e.kbdc <= 0xFF && isdigit(e.kbdc)) { - nxt = nxt*10+e.kbdc-'0'; - break; - } else if(e.kbdc != '\n') - nxt = 0; - switch(e.kbdc) { - case 'r': /* reverse page order */ - if(doc->fwdonly) - break; - reverse = !reverse; - menu.lasthit = doc->npage-1-menu.lasthit; - - /* - * the theory is that if we are reversing the - * document order and are on the first or last - * page then we're just starting and really want - * to view the other end. maybe the if - * should be dropped and this should happen always. - */ - if(page == 0 || page == doc->npage-1) { - page = doc->npage-1-page; - showpage(page, &menu); - } - break; - case 'w': /* write bitmap of current screen */ - esetcursor(&reading); - s = writebitmap(); - if(s) - string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP, - display->defaultfont, s); - esetcursor(nil); - flushimage(display, 1); - break; - case 'd': /* remove image from working set */ - if(doc->rmpage && page < doc->npage) { - if(doc->rmpage(doc, page) >= 0) { - if(doc->npage < 0) - wexits(0); - if(page >= doc->npage) - page = doc->npage-1; - showpage(page, &menu); - } - } - break; - case 'q': - case 0x04: /* ctrl-d */ - wexits(0); - case 'u': - if(im==nil) - break; - angle = (angle+180) % 360; - showpage(page, &menu); - break; - case '-': - case '\b': - case Kleft: - if(page > 0 && !doc->fwdonly) { - --page; - showpage(page, &menu); - } - break; - case '\n': - if(nxt) { - nxt--; - if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly) - showpage(page=nxt, &menu); - nxt = 0; - break; - } - goto Gotonext; - case Kright: - case ' ': - Gotonext: - if(doc->npage && ++page >= doc->npage && !doc->fwdonly) - wexits(0); - showpage(page, &menu); - break; - - /* - * The upper y coordinate of the image is at ul.y in screen->r. - * Panning up means moving the upper left corner down. If the - * upper left corner is currently visible, we need to go back a page. - */ - case Kup: - if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){ - if(page > 0 && !doc->fwdonly){ - --page; - showbottom = 1; - showpage(page, &menu); - } - } else { - i = Dy(screen->r)/2; - if(i > 10) - i -= 10; - if(i+ul.y > screen->r.min.y) - i = screen->r.min.y - ul.y; - translate(Pt(0, i)); - } - break; - - /* - * If the lower y coordinate is on the screen, we go to the next page. - * The lower y coordinate is at ul.y + Dy(im->r). - */ - case Kdown: - i = ul.y + Dy(im->r); - if(screen->r.min.y <= i && i <= screen->r.max.y){ - ul.y = screen->r.min.y; - goto Gotonext; - } else { - i = -Dy(screen->r)/2; - if(i < -10) - i += 10; - if(i+ul.y+Dy(im->r) <= screen->r.max.y) - i = screen->r.max.y - Dy(im->r) - ul.y - 1; - translate(Pt(0, i)); - } - break; - default: - esetcursor(&query); - sleep(1000); - esetcursor(nil); - break; - } - break; - - case Emouse: - m = e.mouse; - switch(m.buttons){ - case Left: - oxy = m.xy; - xy0 = oxy; - do { - dxy = subpt(m.xy, oxy); - oxy = m.xy; - translate(dxy); - unlockdisplay(display); - m = emouse(); - lockdisplay(display); - } while(m.buttons == Left); - if(m.buttons) { - dxy = subpt(xy0, oxy); - translate(dxy); - } - break; - - case Middle: - if(doc->npage == 0) - break; - - unlockdisplay(display); - n = emenuhit(Middle, &m, &midmenu); - lockdisplay(display); - if(n == -1) - break; - switch(n){ - case Next: /* next */ - if(reverse) - page--; - else - page++; - if(page < 0) { - if(reverse) return; - else page = 0; - } - - if((page >= doc->npage) && !doc->fwdonly) - return; - - showpage(page, &menu); - nxt = 0; - break; - case Prev: /* prev */ - if(reverse) - page++; - else - page--; - if(page < 0) { - if(reverse) return; - else page = 0; - } - - if((page >= doc->npage) && !doc->fwdonly && !reverse) - return; - - showpage(page, &menu); - nxt = 0; - break; - case Zerox: /* prev */ - zerox(); - break; - case Zin: /* zoom in */ - { - double delta; - Rectangle r; - - r = egetrect(Middle, &m); - if((rectclip(&r, rectaddpt(im->r, ul)) == 0) || - Dx(r) == 0 || Dy(r) == 0) - break; - /* use the smaller side to expand */ - if(Dx(r) < Dy(r)) - delta = (double)Dx(im->r)/(double)Dx(r); - else - delta = (double)Dy(im->r)/(double)Dy(r); - - esetcursor(&reading); - tmp = xallocimage(display, - Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)), - im->chan, 0, DBlack); - if(tmp == nil) { - fprint(2, "out of memory during zoom: %r\n"); - wexits("memory"); - } - resample(im, tmp); - im = tmp; - delayfreeimage(tmp); - esetcursor(nil); - ul = screen->r.min; - redraw(screen); - flushimage(display, 1); - break; - } - case Fit: /* fit */ - { - double delta; - Rectangle r; - - delta = (double)Dx(screen->r)/(double)Dx(im->r); - if((double)Dy(im->r)*delta > Dy(screen->r)) - delta = (double)Dy(screen->r)/(double)Dy(im->r); - - r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)); - esetcursor(&reading); - tmp = xallocimage(display, r, im->chan, 0, DBlack); - if(tmp == nil) { - fprint(2, "out of memory during fit: %r\n"); - wexits("memory"); - } - resample(im, tmp); - im = tmp; - delayfreeimage(tmp); - esetcursor(nil); - ul = screen->r.min; - redraw(screen); - flushimage(display, 1); - break; - } - case Rot: /* rotate 90 */ - angle = (angle+90) % 360; - showpage(page, &menu); - break; - case Upside: /* upside-down */ - angle = (angle+180) % 360; - showpage(page, &menu); - break; - case Restore: /* restore */ - showpage(page, &menu); - break; - case Reverse: /* reverse */ - if(doc->fwdonly) - break; - reverse = !reverse; - menu.lasthit = doc->npage-1-menu.lasthit; - - if(page == 0 || page == doc->npage-1) { - page = doc->npage-1-page; - showpage(page, &menu); - } - break; - case Write: /* write */ - esetcursor(&reading); - s = writebitmap(); - if(s) - string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP, - display->defaultfont, s); - esetcursor(nil); - flushimage(display, 1); - break; - case Del: /* delete */ - if(doc->rmpage && page < doc->npage) { - if(doc->rmpage(doc, page) >= 0) { - if(doc->npage < 0) - wexits(0); - if(page >= doc->npage) - page = doc->npage-1; - showpage(page, &menu); - } - } - break; - case Exit: /* exit */ - return; - case Empty1: - case Empty2: - case Empty3: - break; - - }; - - - - case Right: - if(doc->npage == 0) - break; - - oldpage = page; - unlockdisplay(display); - n = emenuhit(RMenu, &m, &menu); - lockdisplay(display); - if(n == -1) - break; - - if(doc->fwdonly) { - switch(n){ - case 0: /* this page */ - break; - case 1: /* next page */ - showpage(++page, &menu); - break; - case 2: /* exit */ - return; - } - break; - } - - if(n == doc->npage) - return; - else - page = reverse ? doc->npage-1-n : n; - - if(oldpage != page) - showpage(page, &menu); - nxt = 0; - break; - } - break; - - case Eplumb: - pm = e.v; - if(pm->ndata <= 0){ - plumbfree(pm); - break; - } - if(plumbquit(pm)) - exits(nil); - if(showdata(pm)) { - s = estrdup("/tmp/pageplumbXXXXXXX"); - fd = opentemp(s); - write(fd, pm->data, pm->ndata); - /* lose fd reference on purpose; the file is open ORCLOSE */ - } else if(pm->data[0] == '/') { - s = estrdup(pm->data); - } else { - s = emalloc(strlen(pm->wdir)+1+pm->ndata+1); - sprint(s, "%s/%s", pm->wdir, pm->data); - cleanname(s); - } - if((i = doc->addpage(doc, s)) >= 0) { - page = i; - unhide(); - showpage(page, &menu); - } - free(s); - plumbfree(pm); - break; - } - } -} - -Image *gray; - -/* - * A draw operation that touches only the area contained in bot but not in top. - * mp and sp get aligned with bot.min. - */ -static void -gendrawdiff(Image *dst, Rectangle bot, Rectangle top, - Image *src, Point sp, Image *mask, Point mp, int op) -{ - Rectangle r; - Point origin; - Point delta; - - USED(op); - - if(Dx(bot)*Dy(bot) == 0) - return; - - /* no points in bot - top */ - if(rectinrect(bot, top)) - return; - - /* bot - top ≡ bot */ - if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){ - gendrawop(dst, bot, src, sp, mask, mp, op); - return; - } - - origin = bot.min; - /* split bot into rectangles that don't intersect top */ - /* left side */ - if(bot.min.x < top.min.x){ - r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y); - delta = subpt(r.min, origin); - gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); - bot.min.x = top.min.x; - } - - /* right side */ - if(bot.max.x > top.max.x){ - r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y); - delta = subpt(r.min, origin); - gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); - bot.max.x = top.max.x; - } - - /* top */ - if(bot.min.y < top.min.y){ - r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y); - delta = subpt(r.min, origin); - gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); - bot.min.y = top.min.y; - } - - /* bottom */ - if(bot.max.y > top.max.y){ - r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y); - delta = subpt(r.min, origin); - gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); - bot.max.y = top.max.y; - } -} - -static void -drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op) -{ - gendrawdiff(dst, bot, top, src, p, mask, p, op); -} - -/* - * Translate the image in the window by delta. - */ -static void -translate(Point delta) -{ - Point u; - Rectangle r, or; - - if(im == nil) - return; - - u = pclip(addpt(ul, delta), ulrange); - delta = subpt(u, ul); - if(delta.x == 0 && delta.y == 0) - return; - - /* - * The upper left corner of the image is currently at ul. - * We want to move it to u. - */ - or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul); - r = rectaddpt(or, delta); - - drawop(screen, r, screen, nil, ul, S); - ul = u; - - /* fill in gray where image used to be but isn't. */ - drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S); - - /* fill in black border */ - drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S); - - /* fill in image where it used to be off the screen. */ - if(rectclip(&or, screen->r)) - drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S); - else - drawop(screen, r, im, nil, im->r.min, S); - flushimage(display, 1); -} - -void -redraw(Image *screen) -{ - Rectangle r; - - if(im == nil) - return; - - ulrange.max = screen->r.max; - ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r))); - - ul = pclip(ul, ulrange); - drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S); - - if(im->repl) - return; - - /* fill in any outer edges */ - /* black border */ - r = rectaddpt(im->r, subpt(ul, im->r.min)); - border(screen, r, -2, display->black, ZP); - r.min = subpt(r.min, Pt(2,2)); - r.max = addpt(r.max, Pt(2,2)); - - /* gray for the rest */ - if(gray == nil) { - gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF); - if(gray == nil) { - fprint(2, "g out of memory: %r\n"); - wexits("mem"); - } - } - border(screen, r, -4000, gray, ZP); -// flushimage(display, 0); -} - -void -eresized(int new) -{ - Rectangle r; - r = screen->r; - if(new && getwindow(display, Refnone) < 0) - fprint(2,"can't reattach to window"); - ul = addpt(ul, subpt(screen->r.min, r.min)); - redraw(screen); -} - -/* clip p to be in r */ -Point -pclip(Point p, Rectangle r) -{ - if(p.x < r.min.x) - p.x = r.min.x; - else if(p.x >= r.max.x) - p.x = r.max.x-1; - - if(p.y < r.min.y) - p.y = r.min.y; - else if(p.y >= r.max.y) - p.y = r.max.y-1; - - return p; -} - -/* - * resize is perhaps a misnomer. - * this really just grows the window to be at least dx across - * and dy high. if the window hits the bottom or right edge, - * it is backed up until it hits the top or left edge. - */ -void -resize(int dx, int dy) -{ - static Rectangle sr; - Rectangle r, or; - - dx += 2*Borderwidth; - dy += 2*Borderwidth; - if(wctlfd < 0){ - wctlfd = open("/dev/wctl", OWRITE); - if(wctlfd < 0) - return; - } - - r = insetrect(screen->r, -Borderwidth); - if(Dx(r) >= dx && Dy(r) >= dy) - return; - - if(Dx(sr)*Dy(sr) == 0) - sr = screenrect(); - - or = r; - - r.max.x = max(r.min.x+dx, r.max.x); - r.max.y = max(r.min.y+dy, r.max.y); - if(r.max.x > sr.max.x){ - if(Dx(r) > Dx(sr)){ - r.min.x = 0; - r.max.x = sr.max.x; - }else - r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0)); - } - if(r.max.y > sr.max.y){ - if(Dy(r) > Dy(sr)){ - r.min.y = 0; - r.max.y = sr.max.y; - }else - r = rectaddpt(r, Pt(0, sr.max.y-r.max.y)); - } - - /* - * Sometimes we can't actually grow the window big enough, - * and resizing it to the same shape makes it flash. - */ - if(Dx(r) == Dx(or) && Dy(r) == Dy(or)) - return; - - fprint(wctlfd, "resize -minx %d -miny %d -maxx %d -maxy %d\n", - r.min.x, r.min.y, r.max.x, r.max.y); -} - -/* - * If we allocimage after a resize but before flushing the draw buffer, - * we won't have seen the reshape event, and we won't have called - * getwindow, and allocimage will fail. So we flushimage before every alloc. - */ -Image* -xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val) -{ - flushimage(display, 0); - return allocimage(d, r, chan, repl, val); -} - -/* all code below this line should be in the library, but is stolen from colors instead */ -static char* -rdenv(char *name) -{ - char *v; - int fd, size; - - fd = open(name, OREAD); - if(fd < 0) - return 0; - size = seek(fd, 0, 2); - v = malloc(size+1); - if(v == 0){ - fprint(2, "page: can't malloc: %r\n"); - wexits("no mem"); - } - seek(fd, 0, 0); - read(fd, v, size); - v[size] = 0; - close(fd); - return v; -} - -void -newwin(void) -{ - char *srv, *mntsrv; - char spec[100]; - int srvfd, cons, pid; - - switch(rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){ - case -1: - fprint(2, "page: can't fork: %r\n"); - wexits("no fork"); - case 0: - break; - default: - wexits(0); - } - - srv = rdenv("/env/wsys"); - if(srv == 0){ - mntsrv = rdenv("/mnt/term/env/wsys"); - if(mntsrv == 0){ - fprint(2, "page: can't find $wsys\n"); - wexits("srv"); - } - srv = malloc(strlen(mntsrv)+10); - sprint(srv, "/mnt/term%s", mntsrv); - free(mntsrv); - pid = 0; /* can't send notes to remote processes! */ - }else - pid = getpid(); - srvfd = open(srv, ORDWR); - if(srvfd == -1){ - fprint(2, "page: can't open %s: %r\n", srv); - wexits("no srv"); - } - free(srv); - sprint(spec, "new -pid %d", pid); - if(mount(srvfd, -1, "/mnt/wsys", 0, spec) == -1){ - fprint(2, "page: can't mount /mnt/wsys: %r (spec=%s)\n", spec); - wexits("no mount"); - } - close(srvfd); - unmount("/mnt/acme", "/dev"); - bind("/mnt/wsys", "/dev", MBEFORE); - cons = open("/dev/cons", OREAD); - if(cons==-1){ - NoCons: - fprint(2, "page: can't open /dev/cons: %r"); - wexits("no cons"); - } - dup(cons, 0); - close(cons); - cons = open("/dev/cons", OWRITE); - if(cons==-1) - goto NoCons; - dup(cons, 1); - dup(cons, 2); - close(cons); -// wctlfd = open("/dev/wctl", OWRITE); -} - -Rectangle -screenrect(void) -{ - int fd; - char buf[12*5]; - - fd = open("/dev/screen", OREAD); - if(fd == -1) - fd=open("/mnt/term/dev/screen", OREAD); - if(fd == -1){ - fprint(2, "page: can't open /dev/screen: %r\n"); - wexits("window read"); - } - if(read(fd, buf, sizeof buf) != sizeof buf){ - fprint(2, "page: can't read /dev/screen: %r\n"); - wexits("screen read"); - } - close(fd); - return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48)); -} - -void -zerox(void) -{ - int pfd[2]; - - pipe(pfd); - switch(rfork(RFFDG|RFREND|RFPROC)) { - case -1: - wexits("cannot fork in zerox: %r"); - case 0: - dup(pfd[1], 0); - close(pfd[0]); - execl("/bin/page", "page", "-w", nil); - wexits("cannot exec in zerox: %r\n"); - default: - close(pfd[1]); - writeimage(pfd[0], im, 0); - close(pfd[0]); - break; - } -} -- cgit v1.2.3