reactos/rosapps/smartpdf/fitz/apps/common/pdfapp.c
Klemens Friedl 435a566751 SmartPDF - lightweight pdf viewer app for rosapps
* sumatrapdf - vendor import
* everything compiles (libjpeg, poppler, fitz, sumatrapdf)
* does NOT link

(remove the comment tags in the parent directory.rbuild file (rosapps dir) to build it)

svn path=/trunk/; revision=29295
2007-09-29 08:39:35 +00:00

686 lines
13 KiB
C

#include <fitz.h>
#include <mupdf.h>
#include "pdfapp.h"
void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage);
void pdfapp_warn(pdfapp_t *app, const char *fmt, ...)
{
char buf[1024];
va_list ap;
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);
winwarn(app, buf);
}
void pdfapp_error(pdfapp_t *app, fz_error *error)
{
winerror(app, error->msg);
}
char *pdfapp_usage(pdfapp_t *app)
{
return
" l <\t\t-- rotate left\n"
" r >\t\t-- rotate right\n"
" u up\t\t-- scroll up\n"
" d down\t-- scroll down\n"
" = +\t\t-- zoom in\n"
" -\t\t-- zoom out\n"
" w\t\t-- shrinkwrap\n"
"\n"
" n pgdn space\t-- next page\n"
" b pgup back\t-- previous page\n"
" right\t\t-- next page\n"
" left\t\t-- previous page\n"
" N F\t\t-- next 10\n"
" B\t\t-- back 10\n"
" m\t\t-- mark page for snap back\n"
" t\t\t-- pop back to last mark\n"
" 123g\t\t-- go to page\n"
"\n"
" left drag to pan, right drag to copy text\n";
}
void pdfapp_init(pdfapp_t *app)
{
fz_error *error;
memset(app, 0, sizeof(pdfapp_t));
error = fz_newrenderer(&app->rast, pdf_devicergb, 0, 1024 * 512);
if (error)
pdfapp_error(app, error);
app->scrw = 640;
app->scrh = 480;
}
void pdfapp_open(pdfapp_t *app, char *filename)
{
fz_error *error;
fz_obj *obj;
char *password = "";
/*
* Open PDF and load xref table
*/
app->filename = filename;
error = pdf_newxref(&app->xref);
if (error)
pdfapp_error(app, error);
error = pdf_loadxref(app->xref, filename);
if (error)
{
if (!strncmp(error->msg, "ioerror", 7))
pdfapp_error(app, error);
pdfapp_warn(app,
"There was a problem with file \"%s\".\n"
"It may be corrupted, or generated by broken software.\n\n"
"%s\n\nTrying to continue anyway...",
filename, error->msg);
error = pdf_repairxref(app->xref, filename);
if (error)
pdfapp_error(app, error);
}
/*
* Handle encrypted PDF files
*/
error = pdf_decryptxref(app->xref);
if (error)
pdfapp_error(app, error);
if (app->xref->crypt)
{
error = pdf_setpassword(app->xref->crypt, password);
while (error)
{
fz_droperror(error);
password = winpassword(app, filename);
if (!password)
exit(1);
error = pdf_setpassword(app->xref->crypt, password);
if (error)
pdfapp_warn(app, "Invalid password.");
}
}
/*
* Load page tree
*/
error = pdf_loadpagetree(&app->pages, app->xref);
if (error)
pdfapp_error(app, error);
/*
* Load meta information
* TODO: move this into mupdf library
*/
obj = fz_dictgets(app->xref->trailer, "Root");
if (!obj)
pdfapp_error(app, fz_throw("syntaxerror: missing Root object"));
error = pdf_loadindirect(&app->xref->root, app->xref, obj);
if (error)
pdfapp_error(app, error);
obj = fz_dictgets(app->xref->trailer, "Info");
if (obj)
{
error = pdf_loadindirect(&app->xref->info, app->xref, obj);
if (error)
pdfapp_error(app, error);
}
error = pdf_loadnametrees(app->xref);
if (error)
pdfapp_error(app, error);
error = pdf_loadoutline(&app->outline, app->xref);
if (error)
pdfapp_error(app, error);
app->doctitle = filename;
if (strrchr(app->doctitle, '\\'))
app->doctitle = strrchr(app->doctitle, '\\') + 1;
if (strrchr(app->doctitle, '/'))
app->doctitle = strrchr(app->doctitle, '/') + 1;
if (app->xref->info)
{
obj = fz_dictgets(app->xref->info, "Title");
if (obj)
{
error = pdf_toutf8(&app->doctitle, obj);
if (error)
pdfapp_error(app, error);
}
}
/*
* Start at first page
*/
app->shrinkwrap = 1;
if (app->pageno < 1)
app->pageno = 1;
if (app->zoom <= 0.0)
app->zoom = 1.0;
app->rotate = 0;
app->panx = 0;
app->pany = 0;
pdfapp_showpage(app, 1, 1);
}
void pdfapp_close(pdfapp_t *app)
{
if (app->pages)
pdf_droppagetree(app->pages);
app->pages = nil;
if (app->page)
pdf_droppage(app->page);
app->page = nil;
if (app->image)
fz_droppixmap(app->image);
app->image = nil;
if (app->outline)
pdf_dropoutline(app->outline);
app->outline = nil;
if (app->xref->store)
pdf_dropstore(app->xref->store);
app->xref->store = nil;
pdf_closexref(app->xref);
app->xref = nil;
}
fz_matrix pdfapp_viewctm(pdfapp_t *app)
{
fz_matrix ctm;
ctm = fz_identity();
ctm = fz_concat(ctm, fz_translate(0, -app->page->mediabox.y1));
ctm = fz_concat(ctm, fz_scale(app->zoom, -app->zoom));
ctm = fz_concat(ctm, fz_rotate(app->rotate + app->page->rotate));
return ctm;
}
void pdfapp_panview(pdfapp_t *app, int newx, int newy)
{
if (newx > 0)
newx = 0;
if (newy > 0)
newy = 0;
if (newx + app->image->w < app->winw)
newx = app->winw - app->image->w;
if (newy + app->image->h < app->winh)
newy = app->winh - app->image->h;
if (app->winw >= app->image->w)
newx = (app->winw - app->image->w) / 2;
if (app->winh >= app->image->h)
newy = (app->winh - app->image->h) / 2;
if (newx != app->panx || newy != app->pany)
winrepaint(app);
app->panx = newx;
app->pany = newy;
}
void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage)
{
char buf[256];
fz_error *error;
fz_matrix ctm;
fz_rect bbox;
fz_obj *obj;
if (loadpage)
{
wincursor(app, WAIT);
if (app->page)
pdf_droppage(app->page);
app->page = nil;
obj = pdf_getpageobject(app->pages, app->pageno - 1);
error = pdf_loadpage(&app->page, app->xref, obj);
if (error)
pdfapp_error(app, error);
sprintf(buf, "%s - %d/%d", app->doctitle,
app->pageno, pdf_getpagecount(app->pages));
wintitle(app, buf);
}
if (drawpage)
{
wincursor(app, WAIT);
if (app->image)
fz_droppixmap(app->image);
app->image = nil;
ctm = pdfapp_viewctm(app);
bbox = fz_transformaabb(ctm, app->page->mediabox);
error = fz_rendertree(&app->image, app->rast, app->page->tree,
ctm, fz_roundrect(bbox), 1);
if (error)
pdfapp_error(app, error);
winconvert(app, app->image);
}
pdfapp_panview(app, app->panx, app->pany);
if (app->shrinkwrap)
{
int w = app->image->w;
int h = app->image->h;
if (app->winw == w)
app->panx = 0;
if (app->winh == h)
app->pany = 0;
if (w > app->scrw * 90 / 100)
w = app->scrw * 90 / 100;
if (h > app->scrh * 90 / 100)
h = app->scrh * 90 / 100;
if (w != app->winw || h != app->winh)
winresize(app, w, h);
}
winrepaint(app);
wincursor(app, ARROW);
}
void pdfapp_gotouri(pdfapp_t *app, fz_obj *uri)
{
char buf[2048];
memcpy(buf, fz_tostrbuf(uri), fz_tostrlen(uri));
buf[fz_tostrlen(uri)] = 0;
winopenuri(app, buf);
}
void pdfapp_gotopage(pdfapp_t *app, fz_obj *obj)
{
int oid = fz_tonum(obj);
int i;
for (i = 0; i < pdf_getpagecount(app->pages); i++)
{
if (fz_tonum(app->pages->pref[i]) == oid)
{
if (app->histlen + 1 == 256)
{
memmove(app->hist, app->hist + 1, sizeof(int) * 255);
app->histlen --;
}
app->hist[app->histlen++] = app->pageno;
app->pageno = i + 1;
pdfapp_showpage(app, 1, 1);
return;
}
}
}
void pdfapp_onresize(pdfapp_t *app, int w, int h)
{
if (app->winw != w || app->winh != h)
{
app->winw = w;
app->winh = h;
pdfapp_panview(app, app->panx, app->pany);
winrepaint(app);
}
}
void pdfapp_onkey(pdfapp_t *app, int c)
{
int oldpage = app->pageno;
int panto = 0; /* 0 = top, 1 = bottom, 2 = leave alone */
/*
* Save numbers typed for later
*/
if (c >= '0' && c <= '9')
app->number[app->numberlen++] = c;
else
if (c != 'g' && c != 'G')
app->numberlen = 0;
switch (c)
{
/*
* Zoom and rotate
*/
case '+': case '=':
app->zoom += 0.1;
if (app->zoom > 3.0)
app->zoom = 3.0;
pdfapp_showpage(app, 0, 1);
break;
case '-':
app->zoom -= 0.1;
if (app->zoom < 0.1)
app->zoom = 0.1;
pdfapp_showpage(app, 0, 1);
break;
case 'l': case '<':
app->rotate -= 90;
pdfapp_showpage(app, 0, 1);
break;
case 'r': case '>':
app->rotate += 90;
pdfapp_showpage(app, 0, 1);
break;
case 'a':
app->rotate -= 15;
pdfapp_showpage(app, 0, 1);
break;
case 's':
app->rotate += 15;
pdfapp_showpage(app, 0, 1);
break;
/*
* Pan view, but dont need to repaint image
*/
case 'w':
app->shrinkwrap = 1;
app->panx = app->pany = 0;
pdfapp_showpage(app, 0, 0);
break;
case 'd':
app->pany -= app->image->h / 10;
pdfapp_showpage(app, 0, 0);
break;
case 'u':
app->pany += app->image->h / 10;
pdfapp_showpage(app, 0, 0);
break;
case ',':
app->panx += app->image->w / 10;
pdfapp_showpage(app, 0, 0);
break;
case '.':
app->panx -= app->image->w / 10;
pdfapp_showpage(app, 0, 0);
break;
/*
* Page navigation
*/
case 'g':
case '\n':
case '\r':
if (app->numberlen > 0)
{
app->number[app->numberlen] = '\0';
app->pageno = atoi(app->number);
app->numberlen = 0;
}
break;
case 'G':
app->pageno = pdf_getpagecount(app->pages);
break;
case 'm':
if (app->histlen + 1 == 256)
{
memmove(app->hist, app->hist + 1, sizeof(int) * 255);
app->histlen --;
}
app->hist[app->histlen++] = app->pageno;
break;
case 't':
if (app->histlen > 0)
app->pageno = app->hist[--app->histlen];
break;
/*
* Back and forth ...
*/
case 'p':
panto = 2;
app->pageno--;
break;
case 'b': case '\b':
panto = 1;
app->pageno--;
break;
case 'n':
panto = 2;
case 'f': case ' ':
app->pageno++;
break;
case 'B':
app->pageno -= 10;
break;
case 'F':
app->pageno += 10;
break;
}
if (app->pageno < 1)
app->pageno = 1;
if (app->pageno > pdf_getpagecount(app->pages))
app->pageno = pdf_getpagecount(app->pages);
if (app->pageno != oldpage)
{
switch (panto)
{
case 0: app->pany = 0; break;
case 1: app->pany = -2000; break;
case 2: break;
}
pdfapp_showpage(app, 1, 1);
}
}
void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int state)
{
pdf_link *link;
fz_matrix ctm;
fz_point p;
p.x = x - app->panx + app->image->x;
p.y = y - app->pany + app->image->y;
ctm = pdfapp_viewctm(app);
ctm = fz_invertmatrix(ctm);
p = fz_transformpoint(ctm, p);
for (link = app->page->links; link; link = link->next)
{
if (p.x >= link->rect.x0 && p.x <= link->rect.x1)
if (p.y >= link->rect.y0 && p.y <= link->rect.y1)
break;
}
if (link)
{
wincursor(app, HAND);
if (btn == 1 && state == 1)
{
if (fz_isstring(link->dest))
pdfapp_gotouri(app, link->dest);
if (fz_isindirect(link->dest))
pdfapp_gotopage(app, link->dest);
return;
}
}
else
{
wincursor(app, ARROW);
}
if (state == 1)
{
if (btn == 1 && !app->iscopying)
{
app->ispanning = 1;
app->selx = x;
app->sely = y;
}
if (btn == 3 && !app->ispanning)
{
app->iscopying = 1;
app->selx = x;
app->sely = y;
app->selr.x0 = x;
app->selr.x1 = x;
app->selr.y0 = y;
app->selr.y1 = y;
}
if (btn == 4 || btn == 5) /* scroll wheel */
{
int dir = btn == 4 ? 1 : -1;
app->ispanning = app->iscopying = 0;
if (modifiers & (1<<2))
{
/* zoom in/out if ctrl is pressed */
app->zoom += 0.1 * dir;
if (app->zoom > 3.0)
app->zoom = 3.0;
if (app->zoom < 0.1)
app->zoom = 0.1;
pdfapp_showpage(app, 0, 1);
}
else
{
/* scroll up/down, or left/right if
shift is pressed */
int isx = (modifiers & (1<<0));
int xstep = isx ? 20 * dir : 0;
int ystep = !isx ? 20 * dir : 0;
pdfapp_panview(app, app->panx + xstep, app->pany + ystep);
}
}
}
else if (state == -1)
{
if (app->iscopying)
{
app->iscopying = 0;
app->selr.x0 = MIN(app->selx, x);
app->selr.x1 = MAX(app->selx, x);
app->selr.y0 = MIN(app->sely, y);
app->selr.y1 = MAX(app->sely, y);
winrepaint(app);
if (app->selr.x0 < app->selr.x1 && app->selr.y0 < app->selr.y1)
windocopy(app);
}
if (app->ispanning)
app->ispanning = 0;
}
else if (app->ispanning)
{
int newx = app->panx + x - app->selx;
int newy = app->pany + y - app->sely;
pdfapp_panview(app, newx, newy);
app->selx = x;
app->sely = y;
}
else if (app->iscopying)
{
app->selr.x0 = MIN(app->selx, x);
app->selr.x1 = MAX(app->selx, x);
app->selr.y0 = MIN(app->sely, y);
app->selr.y1 = MAX(app->sely, y);
winrepaint(app);
}
}
void pdfapp_oncopy(pdfapp_t *app, unsigned short *ucsbuf, int ucslen)
{
fz_error *error;
pdf_textline *line, *ln;
int y, c;
int i, p;
int bx0, bx1, by0, by1;
int x0 = app->image->x + app->selr.x0 - app->panx;
int x1 = app->image->x + app->selr.x1 - app->panx;
int y0 = app->image->y + app->selr.y0 - app->pany;
int y1 = app->image->y + app->selr.y1 - app->pany;
error = pdf_loadtextfromtree(&line, app->page->tree, pdfapp_viewctm(app));
if (error)
pdfapp_error(app, error);
p = 0;
for (ln = line; ln; ln = ln->next)
{
y = y0 - 1;
for (i = 0; i < ln->len; i++)
{
bx0 = ln->text[i].bbox.x0;
bx1 = ln->text[i].bbox.x1;
by0 = ln->text[i].bbox.y0;
by1 = ln->text[i].bbox.y1;
c = ln->text[i].c;
if (c < 32)
c = '?';
if (bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)
if (p < ucslen - 1)
ucsbuf[p++] = c;
}
if (by1 >= y0 && by0 <= y1)
{
#ifdef WIN32
if (p < ucslen - 1)
ucsbuf[p++] = '\r';
#endif
if (p < ucslen - 1)
ucsbuf[p++] = '\n';
}
}
ucsbuf[p] = 0;
pdf_droptextline(line);
}