mupdf: support mupdf-1.10
[fbpdf.git] / fbpdf.c
blob3b3b51cdbc78f0f1e9980ea4a0f40583d8e91358
1 /*
2 * fbpdf - a small framebuffer pdf viewer using mupdf
4 * Copyright (C) 2009-2016 Ali Gholami Rudi <ali at rudi dot ir>
6 * This program is released under the Modified BSD license.
7 */
8 #include <ctype.h>
9 #include <signal.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <termios.h>
14 #include <unistd.h>
15 #include "draw.h"
16 #include "doc.h"
18 #define MIN(a, b) ((a) < (b) ? (a) : (b))
19 #define MAX(a, b) ((a) > (b) ? (a) : (b))
21 #define PAGESTEPS 8
22 #define MAXZOOM 100
23 #define MARGIN 1
24 #define CTRLKEY(x) ((x) - 96)
25 #define ISMARK(x) (isalpha(x) || (x) == '\'' || (x) == '`')
27 static struct doc *doc;
28 static fbval_t *pbuf; /* current page */
29 static int srows, scols; /* screen dimentions */
30 static int prows, pcols; /* current page dimensions */
31 static int prow, pcol; /* page position */
32 static int srow, scol; /* screen position */
34 static struct termios termios;
35 static char filename[256];
36 static int mark[128]; /* mark page number */
37 static int mark_row[128]; /* mark head position */
38 static int num = 1; /* page number */
39 static int numdiff; /* G command page number difference */
40 static int zoom = 15;
41 static int zoom_def = 15; /* default zoom */
42 static int rotate;
43 static int count;
45 static void draw(void)
47 int i;
48 fbval_t *rbuf = malloc(scols * sizeof(rbuf[0]));
49 for (i = srow; i < srow + srows; i++) {
50 int cbeg = MAX(scol, pcol);
51 int cend = MIN(scol + scols, pcol + pcols);
52 memset(rbuf, 0, scols * sizeof(rbuf[0]));
53 if (i >= prow && i < prow + prows && cbeg < cend) {
54 memcpy(rbuf + cbeg - scol,
55 pbuf + (i - prow) * pcols + cbeg - pcol,
56 (cend - cbeg) * sizeof(rbuf[0]));
58 fb_set(i - srow, 0, rbuf, scols);
60 free(rbuf);
63 static int loadpage(int p)
65 if (p < 1 || p > doc_pages(doc))
66 return 1;
67 prows = 0;
68 free(pbuf);
69 pbuf = doc_draw(doc, p, zoom, rotate, &prows, &pcols);
70 prow = -prows / 2;
71 pcol = -pcols / 2;
72 num = p;
73 return 0;
76 static void zoom_page(int z)
78 int _zoom = zoom;
79 zoom = MIN(MAXZOOM, MAX(1, z));
80 if (!loadpage(num))
81 srow = srow * zoom / _zoom;
84 static void setmark(int c)
86 if (ISMARK(c)) {
87 mark[c] = num;
88 mark_row[c] = srow / zoom;
92 static void jmpmark(int c, int offset)
94 if (c == '`')
95 c = '\'';
96 if (ISMARK(c) && mark[c]) {
97 int dst = mark[c];
98 int dst_row = offset ? mark_row[c] * zoom : 0;
99 setmark('\'');
100 if (!loadpage(dst))
101 srow = offset ? dst_row : prow;
105 static int readkey(void)
107 unsigned char b;
108 if (read(0, &b, 1) <= 0)
109 return -1;
110 return b;
113 static int getcount(int def)
115 int result = count ? count : def;
116 count = 0;
117 return result;
120 static void printinfo(void)
122 printf("\x1b[H");
123 printf("FBPDF: file:%s page:%d(%d) zoom:%d%% \x1b[K\r",
124 filename, num, doc_pages(doc), zoom * 10);
125 fflush(stdout);
128 static void term_setup(void)
130 struct termios newtermios;
131 tcgetattr(0, &termios);
132 newtermios = termios;
133 newtermios.c_lflag &= ~ICANON;
134 newtermios.c_lflag &= ~ECHO;
135 tcsetattr(0, TCSAFLUSH, &newtermios);
136 printf("\x1b[?25l"); /* hide the cursor */
137 printf("\x1b[2J"); /* clear the screen */
138 fflush(stdout);
141 static void term_cleanup(void)
143 tcsetattr(0, 0, &termios);
144 printf("\x1b[?25h\n"); /* show the cursor */
147 static void sigcont(int sig)
149 term_setup();
152 static int reload(void)
154 doc_close(doc);
155 doc = doc_open(filename);
156 if (!doc || !doc_pages(doc)) {
157 fprintf(stderr, "\nfbpdf: cannot open <%s>\n", filename);
158 return 1;
160 if (!loadpage(num))
161 draw();
162 return 0;
165 static int rmargin(void)
167 int ret = 0;
168 int i, j;
169 for (i = 0; i < prows; i++) {
170 j = pcols - 1;
171 while (j > ret && pbuf[i * pcols + j] == FB_VAL(255, 255, 255))
172 j--;
173 if (ret < j)
174 ret = j;
176 return ret;
179 static int lmargin(void)
181 int ret = pcols;
182 int i, j;
183 for (i = 0; i < prows; i++) {
184 j = 0;
185 while (j < ret && pbuf[i * pcols + j] == FB_VAL(255, 255, 255))
186 j++;
187 if (ret > j)
188 ret = j;
190 return ret;
193 static void mainloop(void)
195 int step = srows / PAGESTEPS;
196 int hstep = scols / PAGESTEPS;
197 int c;
198 term_setup();
199 signal(SIGCONT, sigcont);
200 loadpage(num);
201 srow = prow;
202 scol = -scols / 2;
203 draw();
204 while ((c = readkey()) != -1) {
205 if (c == 'q')
206 break;
207 if (c == 'e' && reload())
208 break;
209 switch (c) { /* commands that do not require redrawing */
210 case 'o':
211 numdiff = num - getcount(num);
212 break;
213 case 'Z':
214 zoom_def = getcount(zoom);
215 break;
216 case 'i':
217 printinfo();
218 break;
219 case 27:
220 count = 0;
221 break;
222 case 'm':
223 setmark(readkey());
224 break;
225 case 'd':
226 sleep(getcount(1));
227 break;
228 default:
229 if (isdigit(c))
230 count = count * 10 + c - '0';
232 switch (c) { /* commands that require redrawing */
233 case CTRLKEY('f'):
234 case 'J':
235 if (!loadpage(num + getcount(1)))
236 srow = prow;
237 break;
238 case CTRLKEY('b'):
239 case 'K':
240 if (!loadpage(num - getcount(1)))
241 srow = prow;
242 break;
243 case 'G':
244 setmark('\'');
245 if (!loadpage(getcount(doc_pages(doc) - numdiff) + numdiff))
246 srow = prow;
247 break;
248 case 'O':
249 numdiff = num - getcount(num);
250 setmark('\'');
251 if (!loadpage(num + numdiff))
252 srow = prow;
253 break;
254 case 'z':
255 zoom_page(getcount(zoom_def));
256 break;
257 case 'w':
258 zoom_page(pcols ? zoom * scols / pcols : zoom);
259 break;
260 case 'W':
261 if (lmargin() < rmargin())
262 zoom_page(zoom * (scols - hstep) /
263 (rmargin() - lmargin()));
264 break;
265 case 'f':
266 zoom_page(prows ? zoom * srows / prows : zoom);
267 break;
268 case 'r':
269 rotate = getcount(0);
270 if (!loadpage(num))
271 srow = prow;
272 break;
273 case '`':
274 case '\'':
275 jmpmark(readkey(), c == '`');
276 break;
277 case 'j':
278 srow += step * getcount(1);
279 break;
280 case 'k':
281 srow -= step * getcount(1);
282 break;
283 case 'l':
284 scol += hstep * getcount(1);
285 break;
286 case 'h':
287 scol -= hstep * getcount(1);
288 break;
289 case 'H':
290 srow = prow;
291 break;
292 case 'L':
293 srow = prow + prows - srows;
294 break;
295 case 'M':
296 srow = prow + prows / 2 - srows / 2;
297 break;
298 case 'C':
299 scol = -scols / 2;
300 break;
301 case ' ':
302 case CTRLKEY('d'):
303 srow += srows * getcount(1) - step;
304 break;
305 case 127:
306 case CTRLKEY('u'):
307 srow -= srows * getcount(1) - step;
308 break;
309 case '[':
310 scol = pcol;
311 break;
312 case ']':
313 scol = pcol + pcols - scols;
314 break;
315 case '{':
316 scol = pcol + lmargin() - hstep / 2;
317 break;
318 case '}':
319 scol = pcol + rmargin() + hstep / 2 - scols;
320 break;
321 case CTRLKEY('l'):
322 break;
323 default: /* no need to redraw */
324 continue;
326 srow = MAX(prow - srows + MARGIN, MIN(prow + prows - MARGIN, srow));
327 scol = MAX(pcol - scols + MARGIN, MIN(pcol + pcols - MARGIN, scol));
328 draw();
330 term_cleanup();
333 static char *usage =
334 "usage: fbpdf [-r rotation] [-z zoom x10] [-p page] filename\n";
336 int main(int argc, char *argv[])
338 int i = 1;
339 if (argc < 2) {
340 printf(usage);
341 return 1;
343 strcpy(filename, argv[argc - 1]);
344 doc = doc_open(filename);
345 if (!doc || !doc_pages(doc)) {
346 fprintf(stderr, "fbpdf: cannot open <%s>\n", filename);
347 return 1;
349 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
350 switch (argv[i][1]) {
351 case 'r':
352 rotate = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
353 break;
354 case 'z':
355 zoom = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
356 break;
357 case 'p':
358 num = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
359 break;
362 printinfo();
363 if (fb_init())
364 return 1;
365 srows = fb_rows();
366 scols = fb_cols();
367 if (FBM_BPP(fb_mode()) != sizeof(fbval_t))
368 fprintf(stderr, "fbpdf: fbval_t doesn't match fb depth\n");
369 else
370 mainloop();
371 fb_free();
372 free(pbuf);
373 if (doc)
374 doc_close(doc);
375 return 0;