Repository: aligrudi/fbpdf Branch: master Commit: 6a0d77f06f6f Files: 11 Total size: 24.7 KB Directory structure: gitextract_15yrqs0d/ ├── LICENSE ├── Makefile ├── README ├── djvulibre.c ├── doc.h ├── draw.c ├── draw.h ├── fbpdf.1 ├── fbpdf.c ├── mupdf.c └── poppler.c ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ Copyright (C) 2009-2015, Ali Gholami Rudi All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: Makefile ================================================ PREFIX = . CC = cc CFLAGS = -Wall -O2 -I$(PREFIX)/include LDFLAGS = -L$(PREFIX)/lib all: fbpdf %.o: %.c doc.h $(CC) -c $(CFLAGS) $< clean: -rm -f *.o fbpdf fbdjvu fbpdf2 # PDF support using mupdf fbpdf: fbpdf.o mupdf.o draw.o $(CC) -o $@ $^ $(LDFLAGS) -lmupdf -lmupdf-third -lmupdf-pkcs7 -lmupdf-threads -lm # DjVu support fbdjvu: fbpdf.o djvulibre.o draw.o $(CXX) -o $@ $^ $(LDFLAGS) -ldjvulibre -ljpeg -lm -lpthread # PDF support using poppler poppler.o: poppler.c $(CXX) -c $(CFLAGS) `pkg-config --cflags poppler-cpp` $< fbpdf2: fbpdf.o poppler.o draw.o $(CXX) -o $@ $^ $(LDFLAGS) `pkg-config --libs poppler-cpp` ================================================ FILE: README ================================================ FBPDF ===== Fbpdf is a framebuffer PDF and DjVu viewer. There are three make targets: * fbpdf: uses mupdf library for rendering PDF, CBZ, and EPUB files. * fbpdf2: uses poppler library for rendering PDF files. * fbdjvu: uses djvulibre library for rendering DjVu files. The default target is fbpdf; to build the other two, they must be explicitly specified. The following options are available in all three programs: fbpdf [-r rotation] [-z zoom_x10] [-p page_number] file.pdf The following table lists the commands available in fbpdf. Most of them accept a numerical prefix. For instance, '^F' tells fbpdf to show the next page while '5^F' tells it to show the fifth next page. ============== ================================================ KEY ACTION ============== ================================================ ^F/J next page ^B/K previous page G go to page (the last page if no prefix) o set page number (for 'G' command only) O set page number and go to current page z zoom; prefix multiplied by 10 (i.e. '15z' = 150%) r set rotation in degrees i print some information I invert colors (prefix specifies black level) q quit ^[/escape clear the numerical prefix mx mark page as 'x' (or any other letter) 'x jump to the page marked as 'x' `x jump to the page and position marked as 'x' j scroll down k scroll up h scroll left l scroll right [ align with the left edge of the page ] align with the right edge of the page { align with the leftmost character on the page } align with the rightmost character on the page H show page top M centre the page vertically L show page bottom C centre the page horizontally ^D/space page down ^U/^H/backspace page up ^L redraw e reload current file f zoom to fit page height w zoom to fit page width W zoom to fit page contents horizontally Z set the default zoom level for 'z' command d sleep one second before the next command ============== ================================================ ================================================ FILE: djvulibre.c ================================================ #include #include #include #include #include "doc.h" #define MIN(a, b) ((a) < (b) ? (a) : (b)) struct doc { ddjvu_context_t *ctx; ddjvu_document_t *doc; }; int djvu_handle(struct doc *doc) { ddjvu_message_t *msg; msg = ddjvu_message_wait(doc->ctx); while ((msg = ddjvu_message_peek(doc->ctx))) { if (msg->m_any.tag == DDJVU_ERROR) { fprintf(stderr,"ddjvu: %s\n", msg->m_error.message); return 1; } ddjvu_message_pop(doc->ctx); } return 0; } static void djvu_render(ddjvu_page_t *page, int iw, int ih, void *bitmap) { ddjvu_format_t *fmt; ddjvu_rect_t rect; rect.x = 0; rect.y = 0; rect.w = iw; rect.h = ih; fmt = ddjvu_format_create(DDJVU_FORMAT_RGB24, 0, 0); ddjvu_format_set_row_order(fmt, 1); memset(bitmap, 0, ih * iw * 3); ddjvu_page_render(page, DDJVU_RENDER_COLOR, &rect, &rect, fmt, iw * 3, bitmap); ddjvu_format_release(fmt); } void *doc_draw(struct doc *doc, int p, int zoom, int rotate, int bpp, int *rows, int *cols) { ddjvu_page_t *page; ddjvu_pageinfo_t info; int iw, ih, dpi; unsigned char *bmp; char *pbuf; int i, j; page = ddjvu_page_create_by_pageno(doc->doc, p - 1); if (!page) return NULL; while (!ddjvu_page_decoding_done(page)) if (djvu_handle(doc)) return NULL; if (rotate) ddjvu_page_set_rotation(page, (4 - (rotate / 90 % 4)) & 3); ddjvu_document_get_pageinfo(doc->doc, p - 1, &info); dpi = ddjvu_page_get_resolution(page); iw = ddjvu_page_get_width(page) * zoom / dpi; ih = ddjvu_page_get_height(page) * zoom / dpi; if (!(bmp = malloc(ih * iw * 3))) { ddjvu_page_release(page); return NULL; } djvu_render(page, iw, ih, bmp); ddjvu_page_release(page); if (!(pbuf = malloc(ih * iw * bpp))) { free(bmp); return NULL; } for (i = 0; i < ih; i++) { unsigned char *s = bmp + i * iw * 3; char *d = pbuf + (i * iw) * bpp; for (j = 0; j < iw; j++) fb_set(d + j * bpp, s[j * 3], s[j * 3 + 1], s[j * 3 + 2]); } free(bmp); *cols = iw; *rows = ih; return pbuf; } int doc_pages(struct doc *doc) { return ddjvu_document_get_pagenum(doc->doc); } struct doc *doc_open(char *path) { struct doc *doc = malloc(sizeof(*doc)); doc->ctx = ddjvu_context_create("fbpdf"); if (!doc->ctx) goto fail; doc->doc = ddjvu_document_create_by_filename(doc->ctx, path, 1); if (!doc->doc) goto fail; while (!ddjvu_document_decoding_done(doc->doc)) if (djvu_handle(doc)) goto fail; return doc; fail: doc_close(doc); return NULL; } void doc_close(struct doc *doc) { if (doc->doc) ddjvu_context_release(doc->ctx); if (doc->ctx) ddjvu_document_release(doc->doc); free(doc); } ================================================ FILE: doc.h ================================================ struct doc *doc_open(char *path); int doc_pages(struct doc *doc); void *doc_draw(struct doc *doc, int page, int zoom, int rotate, int bpp, int *rows, int *cols); void doc_close(struct doc *doc); void fb_set(char *d, unsigned r, unsigned g, unsigned b); ================================================ FILE: draw.c ================================================ #include #include #include #include #include #include #include #include #include "draw.h" #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define NLEVELS (1 << 8) static struct fb_var_screeninfo vinfo; /* linux-specific FB structure */ static struct fb_fix_screeninfo finfo; /* linux-specific FB structure */ static int fd; /* FB device file descriptor */ static void *fb; /* mmap()ed FB memory */ static int bpp; /* bytes per pixel */ static int nr, ng, nb; /* color levels */ static int rl, rr, gl, gr, bl, br; /* shifts per color */ static int xres, yres, xoff, yoff; /* drawing region */ static int fb_len(void) { return finfo.line_length * vinfo.yres_virtual; } static void fb_cmap_save(int save) { static unsigned short red[NLEVELS], green[NLEVELS], blue[NLEVELS]; struct fb_cmap cmap; if (finfo.visual == FB_VISUAL_TRUECOLOR) return; cmap.start = 0; cmap.len = MAX(nr, MAX(ng, nb)); cmap.red = red; cmap.green = green; cmap.blue = blue; cmap.transp = NULL; ioctl(fd, save ? FBIOGETCMAP : FBIOPUTCMAP, &cmap); } void fb_cmap(void) { unsigned short red[NLEVELS], green[NLEVELS], blue[NLEVELS]; struct fb_cmap cmap; int i; if (finfo.visual == FB_VISUAL_TRUECOLOR) return; for (i = 0; i < nr; i++) red[i] = (65535 / (nr - 1)) * i; for (i = 0; i < ng; i++) green[i] = (65535 / (ng - 1)) * i; for (i = 0; i < nb; i++) blue[i] = (65535 / (nb - 1)) * i; cmap.start = 0; cmap.len = MAX(nr, MAX(ng, nb)); cmap.red = red; cmap.green = green; cmap.blue = blue; cmap.transp = NULL; ioctl(fd, FBIOPUTCMAP, &cmap); } unsigned fb_mode(void) { return ((rl < gl) << 22) | ((rl < bl) << 21) | ((gl < bl) << 20) | (bpp << 16) | (vinfo.red.length << 8) | (vinfo.green.length << 4) | (vinfo.blue.length); } static void init_colors(void) { nr = 1 << vinfo.red.length; ng = 1 << vinfo.blue.length; nb = 1 << vinfo.green.length; rr = 8 - vinfo.red.length; rl = vinfo.red.offset; gr = 8 - vinfo.green.length; gl = vinfo.green.offset; br = 8 - vinfo.blue.length; bl = vinfo.blue.offset; } int fb_init(char *dev) { char *path = dev ? dev : FBDEV; char *geom = dev ? strchr(dev, ':') : NULL; if (geom) { *geom = '\0'; sscanf(geom + 1, "%dx%d%d%d", &xres, &yres, &xoff, &yoff); } fd = open(path, O_RDWR); if (fd < 0) goto failed; if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) < 0) goto failed; if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) < 0) goto failed; fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); bpp = (vinfo.bits_per_pixel + 7) >> 3; fb = mmap(NULL, fb_len(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (fb == MAP_FAILED) goto failed; init_colors(); fb_cmap_save(1); fb_cmap(); return 0; failed: perror("fb_init()"); close(fd); return 1; } void fb_free(void) { fb_cmap_save(0); munmap(fb, fb_len()); close(fd); } int fb_rows(void) { return yres ? yres : vinfo.yres; } int fb_cols(void) { return xres ? xres : vinfo.xres; } void *fb_mem(int r) { return fb + (r + vinfo.yoffset + yoff) * finfo.line_length + (vinfo.xoffset + xoff) * bpp; } unsigned fb_val(int r, int g, int b) { return ((r >> rr) << rl) | ((g >> gr) << gl) | ((b >> br) << bl); } ================================================ FILE: draw.h ================================================ /* fbpad's framebuffer interface */ #define FBDEV "/dev/fb0" /* fb_mode() interpretation */ #define FBM_BPP(m) (((m) >> 16) & 0x0f) /* bytes per pixel (4 bits) */ #define FBM_CLR(m) ((m) & 0x0fff) /* bits per color (12 bits) */ #define FBM_ORD(m) (((m) >> 20) & 0x07) /* color order (3 bits) */ /* main functions */ int fb_init(char *dev); void fb_free(void); unsigned fb_mode(void); void *fb_mem(int r); int fb_rows(void); int fb_cols(void); void fb_cmap(void); unsigned fb_val(int r, int g, int b); ================================================ FILE: fbpdf.1 ================================================ .TH FBPDF 1 "JUNE 2022" .SH NAME fbpdf \- framebuffer PDF viewer .SH SYNOPSIS .B fbpdf .RB [ -r .IR rotation ] .RB [ -z .IR zoom_x10 ] .RB [ -p .IR page_number ] .IR filename .SH DESCRIPTION .P .B fbpdf is a small framebuffer PDF viewer which uses the MuPDF backend. .P .B fbpdf2 is similar to fbpdf, but uses poppler as backend. .P .B fbdjvu is a djvu viewer. .P The options documented here are available in all three programs. .SH OPTIONS .TP .BI "\-r " rotation Rotate specified number of degrees .TP .BI "\-z " zoom_x10 Zoom by a multiple of ten .TP .BI "\-p " page_number Go to specified page .SH KEY BINDINGS Most of the key bindings accept a numerical prefix. For instance, '^F' tells fbpdf to show the next page while '5^F' tells it to show the fifth next page. .TP .B h Scroll left .TP .B j Scroll down .TP .B k Scroll up .TP .B l Scroll right .TP .B ^d | Page down .TP .B ^u | Page up .TP .B ^f | J Go to next page .TP .B ^b | K Go to previous page .TP .B H Show page top .TP .B M Center the page vertically .TP .B L Show page bottom .TP .B C Center the page horizontally .TP .B f Fit page height .TP .B w Fit page width .TP .B W Fit page contents horizontally .TP .B [ Align with the left edge of the page .TP .B ] Align with the right edge of the page .TP .B { Align with the leftmost character on the page .TP .B } Align with the rightmost character on the page .TP .B G Go to specified page, default to the last page if no prefix .TP .B o Set page number (for 'G' command only) .TP .B O Set page number and go to current page .TP .B r Rotate specified number of degrees .TP .B z Zoom by a multiple of ten in percentage (i.e. '15z' = 150%) .TP .B Z Set the default zoom level for 'z' command .TP .B m Mark page as , could be any letter .TP .B ' Jump to the page marked as .TP .B ` Jump to the page and position marked as .TP .B ^[ / Clear numerical prefix .TP .B d Sleep specified seconds before the next command .TP .B I Invert colors .TP .B i Display file information .TP .B e Reload file .TP .B ^l Redraw screen .TP .B q Quit program .SH AUTHOR Written by fbpdf contributors. ================================================ FILE: fbpdf.c ================================================ /* * FBPDF LINUX FRAMEBUFFER PDF VIEWER * * Copyright (C) 2009-2025 Ali Gholami Rudi * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "draw.h" #include "doc.h" #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define PAGESTEPS 8 #define MAXZOOM 1000 #define MARGIN 1 #define CTRLKEY(x) ((x) - 96) #define ISMARK(x) (isalpha(x) || (x) == '\'' || (x) == '`') static struct doc *doc; static char *pbuf; /* current page */ static int srows, scols; /* screen dimentions */ static int prows, pcols; /* current page dimensions */ static int prow, pcol; /* page position */ static int srow, scol; /* screen position */ static int bpp; /* bytes per pixel */ static struct termios termios; static char filename[256]; static int mark[128]; /* mark page number */ static int mark_row[128]; /* mark head position */ static int num = 1; /* page number */ static int numdiff; /* G command page number difference */ static int zoom = 150; static int zoom_def = 150; /* default zoom */ static int rotate; static int count; static int invert; /* invert colors? */ static void draw(void) { int i; char *rbuf = malloc(scols * bpp); for (i = srow; i < srow + srows; i++) { int cbeg = MAX(scol, pcol); int cend = MIN(scol + scols, pcol + pcols); memset(rbuf, 0, scols * bpp); if (i >= prow && i < prow + prows && cbeg < cend) { memcpy(rbuf + (cbeg - scol) * bpp, pbuf + ((i - prow) * pcols + cbeg - pcol) * bpp, (cend - cbeg) * bpp); } memcpy(fb_mem(i - srow), rbuf, scols * bpp); } free(rbuf); } static int loadpage(int p) { int i; if (p < 1 || p > doc_pages(doc)) return 1; prows = 0; free(pbuf); pbuf = doc_draw(doc, p, zoom, rotate, bpp, &prows, &pcols); if (invert) { for (i = 0; i < prows * pcols * bpp; i++) { int val = (unsigned char) pbuf[i] ^ 0xff; pbuf[i] = val * invert / 255 + (255 - invert); } } prow = -prows / 2; pcol = -pcols / 2; num = p; return 0; } static void zoom_page(int z) { int _zoom = zoom; zoom = MIN(MAXZOOM, MAX(1, z)); if (!loadpage(num)) srow = srow * zoom / _zoom; } static void setmark(int c) { if (ISMARK(c)) { mark[c] = num; mark_row[c] = srow * 100 / zoom; } } static void jmpmark(int c, int offset) { if (c == '`') c = '\''; if (ISMARK(c) && mark[c]) { int dst = mark[c]; int dst_row = offset ? mark_row[c] * zoom / 100 : 0; setmark('\''); if (!loadpage(dst)) srow = offset ? dst_row : prow; } } static int readkey(void) { unsigned char b; if (read(0, &b, 1) <= 0) return -1; return b; } static int getcount(int def) { int result = count ? count : def; count = 0; return result; } static void printinfo(void) { printf("\x1b[H"); printf("FBPDF: file:%s page:%d(%d) zoom:%d%% \x1b[K\r", filename, num, doc_pages(doc), zoom); fflush(stdout); } static void term_setup(void) { struct termios newtermios; tcgetattr(0, &termios); newtermios = termios; newtermios.c_lflag &= ~ICANON; newtermios.c_lflag &= ~ECHO; tcsetattr(0, TCSAFLUSH, &newtermios); printf("\x1b[?25l"); /* hide the cursor */ printf("\x1b[2J"); /* clear the screen */ fflush(stdout); } static void term_cleanup(void) { tcsetattr(0, 0, &termios); printf("\x1b[?25h\n"); /* show the cursor */ } static void sigcont(int sig) { term_setup(); } static int reload(void) { doc_close(doc); doc = doc_open(filename); if (!doc || !doc_pages(doc)) { fprintf(stderr, "\nfbpdf: cannot open <%s>\n", filename); return 1; } if (!loadpage(num)) draw(); return 0; } /* this can be optimised based on framebuffer pixel format */ void fb_set(char *d, unsigned r, unsigned g, unsigned b) { unsigned c = fb_val(r, g, b); int i; for (i = 0; i < bpp; i++) d[i] = (c >> (i << 3)) & 0xff; } static int iswhite(char *pix) { int val = 255 - invert; int i; for (i = 0; i < 3 && i < bpp; i++) if (((unsigned char) pix[i]) != val) return 0; return 1; } static int rmargin(void) { int ret = 0; int i, j; for (i = 0; i < prows; i++) { j = pcols - 1; while (j > ret && iswhite(pbuf + (i * pcols + j) * bpp)) j--; if (ret < j) ret = j; } return ret; } static int lmargin(void) { int ret = pcols; int i, j; for (i = 0; i < prows; i++) { j = 0; while (j < ret && iswhite(pbuf + (i * pcols + j) * bpp)) j++; if (ret > j) ret = j; } return ret; } static void mainloop(void) { int step = srows / PAGESTEPS; int hstep = scols / PAGESTEPS; int c; term_setup(); signal(SIGCONT, sigcont); loadpage(num); srow = prow; scol = -scols / 2; draw(); while ((c = readkey()) != -1) { if (c == 'q') break; if (c == 'e' && reload()) break; switch (c) { /* commands that do not require redrawing */ case 'o': numdiff = num - getcount(num); break; case 'Z': count *= 10; zoom_def = getcount(zoom); break; case 'i': printinfo(); break; case 27: count = 0; break; case 'm': setmark(readkey()); break; case 'd': sleep(getcount(1)); break; default: if (isdigit(c)) count = count * 10 + c - '0'; } switch (c) { /* commands that require redrawing */ case CTRLKEY('f'): case 'J': if (!loadpage(num + getcount(1))) srow = prow; break; case CTRLKEY('b'): case 'K': if (!loadpage(num - getcount(1))) srow = prow; break; case 'G': setmark('\''); if (!loadpage(getcount(doc_pages(doc) - numdiff) + numdiff)) srow = prow; break; case 'O': numdiff = num - getcount(num); setmark('\''); if (!loadpage(num + numdiff)) srow = prow; break; case 'z': count *= 10; zoom_page(getcount(zoom_def)); break; case 'w': zoom_page(pcols ? zoom * scols / pcols : zoom); break; case 'W': if (lmargin() < rmargin()) zoom_page(zoom * (scols - hstep) / (rmargin() - lmargin())); break; case 'f': zoom_page(prows ? zoom * srows / prows : zoom); break; case 'r': rotate = getcount(0); if (!loadpage(num)) srow = prow; break; case '`': case '\'': jmpmark(readkey(), c == '`'); break; case 'j': srow += step * getcount(1); break; case 'k': srow -= step * getcount(1); break; case 'l': scol += hstep * getcount(1); break; case 'h': scol -= hstep * getcount(1); break; case 'H': srow = prow; break; case 'L': srow = prow + prows - srows; break; case 'M': srow = prow + prows / 2 - srows / 2; break; case 'C': scol = -scols / 2; break; case ' ': case CTRLKEY('d'): srow += srows * getcount(1) - step; break; case 127: case CTRLKEY('u'): srow -= srows * getcount(1) - step; break; case '[': scol = pcol; break; case ']': scol = pcol + pcols - scols; break; case '{': scol = pcol + lmargin() - hstep / 2; break; case '}': scol = pcol + rmargin() + hstep / 2 - scols; break; case CTRLKEY('l'): break; case 'I': invert = count || !invert ? 255 - (getcount(48) & 0xff) : 0; loadpage(num); break; default: /* no need to redraw */ continue; } srow = MAX(prow - srows + MARGIN, MIN(prow + prows - MARGIN, srow)); scol = MAX(pcol - scols + MARGIN, MIN(pcol + pcols - MARGIN, scol)); draw(); } term_cleanup(); } static char *usage = "usage: fbpdf [-r rotation] [-z zoom x10] [-p page] filename"; int main(int argc, char *argv[]) { int i = 1; if (argc < 2) { puts(usage); return 1; } strcpy(filename, argv[argc - 1]); doc = doc_open(filename); if (!doc || !doc_pages(doc)) { fprintf(stderr, "fbpdf: cannot open <%s>\n", filename); return 1; } for (i = 1; i < argc && argv[i][0] == '-'; i++) { switch (argv[i][1]) { case 'r': rotate = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]); break; case 'z': zoom = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]) * 10; break; case 'p': num = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]); break; } } printinfo(); if (fb_init(getenv("FBDEV"))) return 1; srows = fb_rows(); scols = fb_cols(); bpp = FBM_BPP(fb_mode()); mainloop(); fb_free(); free(pbuf); if (doc) doc_close(doc); return 0; } ================================================ FILE: mupdf.c ================================================ #include #include #include "mupdf/fitz.h" #include "doc.h" #define MIN_(a, b) ((a) < (b) ? (a) : (b)) struct doc { fz_context *ctx; fz_document *pdf; }; void *doc_draw(struct doc *doc, int page, int zoom, int rotate, int bpp, int *rows, int *cols) { fz_matrix ctm; fz_pixmap *pix; char *pbuf; int x, y; ctm = fz_scale((float) zoom / 100, (float) zoom / 100); ctm = fz_pre_rotate(ctm, rotate); pix = fz_new_pixmap_from_page_number(doc->ctx, doc->pdf, page - 1, ctm, fz_device_rgb(doc->ctx), 0); if (!pix) return NULL; if (!(pbuf = malloc(pix->w * pix->h * bpp))) { fz_drop_pixmap(doc->ctx, pix); return NULL; } for (y = 0; y < pix->h; y++) { unsigned char *s = &pix->samples[y * pix->stride]; char *d = pbuf + (y * pix->w) * bpp; for (x = 0; x < pix->w; x++) fb_set(d + x * bpp, s[x * pix->n], s[x * pix->n + 1], s[x * pix->n + 2]); } fz_drop_pixmap(doc->ctx, pix); *cols = pix->w; *rows = pix->h; return pbuf; } int doc_pages(struct doc *doc) { return fz_count_pages(doc->ctx, doc->pdf); } struct doc *doc_open(char *path) { struct doc *doc = malloc(sizeof(*doc)); doc->ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT); fz_register_document_handlers(doc->ctx); fz_try (doc->ctx) { doc->pdf = fz_open_document(doc->ctx, path); } fz_catch (doc->ctx) { fz_drop_context(doc->ctx); free(doc); return NULL; } return doc; } void doc_close(struct doc *doc) { fz_drop_document(doc->ctx, doc->pdf); fz_drop_context(doc->ctx); free(doc); } ================================================ FILE: poppler.c ================================================ #include #include #include #include #include #include #define MIN(a, b) ((a) < (b) ? (a) : (b)) extern "C" { #include "doc.h" } struct doc { poppler::document *doc; }; static poppler::rotation_enum rotation(int times) { if (times == 1) return poppler::rotate_90; if (times == 2) return poppler::rotate_180; if (times == 3) return poppler::rotate_270; return poppler::rotate_0; } void *doc_draw(struct doc *doc, int p, int zoom, int rotate, int bpp, int *rows, int *cols) { poppler::page *page = doc->doc->create_page(p - 1); poppler::page_renderer pr; int x, y; int h, w; char *pbuf; unsigned char *dat; pr.set_render_hint(poppler::page_renderer::antialiasing, true); pr.set_render_hint(poppler::page_renderer::text_antialiasing, true); poppler::image img = pr.render_page(page, (float) 72 * zoom / 100, (float) 72 * zoom / 100, -1, -1, -1, -1, rotation((rotate + 89) / 90)); h = img.height(); w = img.width(); dat = (unsigned char *) img.data(); if (!(pbuf = (char *) malloc(h * w * bpp))) { delete page; return NULL; } for (y = 0; y < h; y++) { unsigned char *s = dat + img.bytes_per_row() * y; char *d = pbuf + (y * w) * bpp; for (x = 0; x < w; x++) fb_set(d + x * bpp, s[x * 4 + 2], s[x * 4 + 1], s[x * 4]); } *rows = h; *cols = w; delete page; return pbuf; } int doc_pages(struct doc *doc) { return doc->doc->pages(); } struct doc *doc_open(char *path) { struct doc *doc = (struct doc *) malloc(sizeof(*doc)); if (doc == NULL) return NULL; doc->doc = poppler::document::load_from_file(path); if (!doc->doc) { doc_close(doc); return NULL; } return doc; } void doc_close(struct doc *doc) { delete doc->doc; free(doc); }