Full Code of aligrudi/fbpdf for AI

master 6a0d77f06f6f cached
11 files
24.7 KB
8.5k tokens
58 symbols
1 requests
Download .txt
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 <ali@rudi.ir>
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 <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libdjvu/ddjvuapi.h>
#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 <fcntl.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#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 | <Space>
Page down
.TP
.B ^u | <Backspace>
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 <prefix>G
Go to specified page, default to the last page if no prefix
.TP
.B <prefix>o
Set page number (for 'G' command only)
.TP
.B <prefix>O
Set page number and go to current page
.TP
.B <prefix>r
Rotate specified number of degrees
.TP
.B <prefix>z
Zoom by a multiple of ten in percentage (i.e. '15z' = 150%)
.TP
.B <prefix>Z
Set the default zoom level for 'z' command
.TP
.B m<mark>
Mark page as <mark>, could be any letter
.TP
.B '<mark>
Jump to the page marked as <mark>
.TP
.B `<mark>
Jump to the page and position marked as <mark>
.TP
.B ^[ / <Esc>
Clear numerical prefix
.TP
.B <prefix>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 <ali at rudi dot ir>
 *
 * 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 <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#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 <stdlib.h>
#include <string.h>
#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 <stdlib.h>
#include <string.h>
#include <poppler/cpp/poppler-document.h>
#include <poppler/cpp/poppler-image.h>
#include <poppler/cpp/poppler-page.h>
#include <poppler/cpp/poppler-page-renderer.h>

#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);
}
Download .txt
gitextract_15yrqs0d/

├── LICENSE
├── Makefile
├── README
├── djvulibre.c
├── doc.h
├── draw.c
├── draw.h
├── fbpdf.1
├── fbpdf.c
├── mupdf.c
└── poppler.c
Download .txt
SYMBOL INDEX (58 symbols across 6 files)

FILE: djvulibre.c
  type doc (line 9) | struct doc {
  function djvu_handle (line 14) | int djvu_handle(struct doc *doc)
  function djvu_render (line 28) | static void djvu_render(ddjvu_page_t *page, int iw, int ih, void *bitmap)
  type doc (line 44) | struct doc
  function doc_pages (line 86) | int doc_pages(struct doc *doc)
  type doc (line 91) | struct doc
  type doc (line 93) | struct doc
  function doc_close (line 109) | void doc_close(struct doc *doc)

FILE: doc.h
  type doc (line 1) | struct doc
  type doc (line 2) | struct doc
  type doc (line 3) | struct doc
  type doc (line 4) | struct doc

FILE: draw.c
  type fb_var_screeninfo (line 15) | struct fb_var_screeninfo
  type fb_fix_screeninfo (line 16) | struct fb_fix_screeninfo
  function fb_len (line 24) | static int fb_len(void)
  function fb_cmap_save (line 29) | static void fb_cmap_save(int save)
  function fb_cmap (line 44) | void fb_cmap(void)
  function fb_mode (line 69) | unsigned fb_mode(void)
  function init_colors (line 76) | static void init_colors(void)
  function fb_init (line 89) | int fb_init(char *dev)
  function fb_free (line 119) | void fb_free(void)
  function fb_rows (line 126) | int fb_rows(void)
  function fb_cols (line 131) | int fb_cols(void)
  function fb_val (line 141) | unsigned fb_val(int r, int g, int b)

FILE: fbpdf.c
  type doc (line 37) | struct doc
  type termios (line 45) | struct termios
  function draw (line 57) | static void draw(void)
  function loadpage (line 75) | static int loadpage(int p)
  function zoom_page (line 95) | static void zoom_page(int z)
  function setmark (line 103) | static void setmark(int c)
  function jmpmark (line 111) | static void jmpmark(int c, int offset)
  function readkey (line 124) | static int readkey(void)
  function getcount (line 132) | static int getcount(int def)
  function printinfo (line 139) | static void printinfo(void)
  function term_setup (line 147) | static void term_setup(void)
  function term_cleanup (line 160) | static void term_cleanup(void)
  function sigcont (line 166) | static void sigcont(int sig)
  function reload (line 171) | static int reload(void)
  function fb_set (line 185) | void fb_set(char *d, unsigned r, unsigned g, unsigned b)
  function iswhite (line 193) | static int iswhite(char *pix)
  function rmargin (line 203) | static int rmargin(void)
  function lmargin (line 217) | static int lmargin(void)
  function mainloop (line 231) | static void mainloop(void)
  function main (line 380) | int main(int argc, char *argv[])

FILE: mupdf.c
  type doc (line 8) | struct doc {
  type doc (line 13) | struct doc
  function doc_pages (line 41) | int doc_pages(struct doc *doc)
  type doc (line 46) | struct doc
  type doc (line 48) | struct doc
  function doc_close (line 61) | void doc_close(struct doc *doc)

FILE: poppler.c
  type doc (line 14) | struct doc {
  function poppler (line 18) | static poppler::rotation_enum rotation(int times)
  type doc (line 29) | struct doc
  function doc_pages (line 61) | int doc_pages(struct doc *doc)
  type doc (line 66) | struct doc
  type doc (line 68) | struct doc
  type doc (line 68) | struct doc
  function doc_close (line 79) | void doc_close(struct doc *doc)
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (28K chars).
[
  {
    "path": "LICENSE",
    "chars": 1509,
    "preview": "Copyright (C) 2009-2015, Ali Gholami Rudi <ali@rudi.ir>\nAll rights reserved.\n\nRedistribution and use in source and binar"
  },
  {
    "path": "Makefile",
    "chars": 627,
    "preview": "PREFIX = .\nCC = cc\nCFLAGS = -Wall -O2 -I$(PREFIX)/include\nLDFLAGS = -L$(PREFIX)/lib\n\nall: fbpdf\n\n%.o: %.c doc.h\n\t$(CC) -"
  },
  {
    "path": "README",
    "chars": 1983,
    "preview": "FBPDF\n=====\n\nFbpdf is a framebuffer PDF and DjVu viewer.  There are three make\ntargets:\n\n* fbpdf: uses mupdf library for"
  },
  {
    "path": "djvulibre.c",
    "chars": 2635,
    "preview": "#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <libdjvu/ddjvuapi.h>\n#include \"doc.h\"\n\n#define MIN("
  },
  {
    "path": "doc.h",
    "chars": 254,
    "preview": "struct doc *doc_open(char *path);\nint doc_pages(struct doc *doc);\nvoid *doc_draw(struct doc *doc, int page, int zoom, in"
  },
  {
    "path": "draw.c",
    "chars": 3279,
    "preview": "#include <fcntl.h>\n#include <linux/fb.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl."
  },
  {
    "path": "draw.h",
    "chars": 505,
    "preview": "/* fbpad's framebuffer interface */\n#define FBDEV\t\t\"/dev/fb0\"\n\n/* fb_mode() interpretation */\n#define FBM_BPP(m)\t(((m) >"
  },
  {
    "path": "fbpdf.1",
    "chars": 2213,
    "preview": ".TH FBPDF 1 \"JUNE 2022\"\n.SH NAME\nfbpdf \\- framebuffer PDF viewer\n.SH SYNOPSIS\n.B fbpdf\n.RB [ -r\n.IR rotation ]\n.RB [ -z\n"
  },
  {
    "path": "fbpdf.c",
    "chars": 8911,
    "preview": "/*\n * FBPDF LINUX FRAMEBUFFER PDF VIEWER\n *\n * Copyright (C) 2009-2025 Ali Gholami Rudi <ali at rudi dot ir>\n *\n * Permi"
  },
  {
    "path": "mupdf.c",
    "chars": 1514,
    "preview": "#include <stdlib.h>\n#include <string.h>\n#include \"mupdf/fitz.h\"\n#include \"doc.h\"\n\n#define MIN_(a, b)\t((a) < (b) ? (a) : "
  },
  {
    "path": "poppler.c",
    "chars": 1850,
    "preview": "#include <stdlib.h>\n#include <string.h>\n#include <poppler/cpp/poppler-document.h>\n#include <poppler/cpp/poppler-image.h>"
  }
]

About this extraction

This page contains the full source code of the aligrudi/fbpdf GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (24.7 KB), approximately 8.5k tokens, and a symbol index with 58 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!