mirror of
https://github.com/koreader/koreader
synced 2024-10-31 21:20:20 +00:00
1d018ee5bf
Because the lua reader is single threaded on which both user inputloop and background page rendering is processed. Although there is a pretty good precache system to save user's time spending on waiting for the rendering when going to the next page, user input is indeed blocked when running the precache thing. The situation is even worse in koptreader as reflowing on page would usually take several second, in this period users cannot move to the next page view even it's already in the cache. This patch will let precache run in the background in a seperate thread so that the koptreader is still responsive when precaching the next page. Now it only just works. Welcome to find out bugs in it.
822 lines
20 KiB
C
822 lines
20 KiB
C
/*
|
|
KindlePDFViewer: MuPDF abstraction for Lua
|
|
Copyright (C) 2011 Hans-Werner Hilse <hilse@web.de>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <stddef.h>
|
|
#include <pthread.h>
|
|
#include <fitz/fitz-internal.h>
|
|
|
|
#include "blitbuffer.h"
|
|
#include "drawcontext.h"
|
|
#include "koptcontext.h"
|
|
#include "k2pdfopt.h"
|
|
#include "pdf.h"
|
|
|
|
typedef struct PdfDocument {
|
|
fz_document *xref;
|
|
fz_context *context;
|
|
} PdfDocument;
|
|
|
|
typedef struct PdfPage {
|
|
int num;
|
|
#ifdef USE_DISPLAY_LIST
|
|
fz_display_list *list;
|
|
#endif
|
|
fz_page *page;
|
|
PdfDocument *doc;
|
|
} PdfPage;
|
|
|
|
|
|
static double LOG_TRESHOLD_PERC = 0.05; // 5%
|
|
|
|
enum {
|
|
MAGIC = 0x3795d42b,
|
|
};
|
|
|
|
typedef struct header {
|
|
int magic;
|
|
size_t sz;
|
|
} header;
|
|
|
|
static size_t msize=0;
|
|
static size_t msize_prev;
|
|
static size_t msize_max;
|
|
static size_t msize_min;
|
|
static size_t msize_iniz;
|
|
static int is_realloc=0;
|
|
|
|
#if 0
|
|
char* readable_fs(double size/*in bytes*/, char *buf) {
|
|
int i = 0;
|
|
const char* units[] = {"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
|
|
while (size > 1024) {
|
|
size /= 1024;
|
|
i++;
|
|
}
|
|
sprintf(buf, "%.*f %s", i, size, units[i]);
|
|
return buf;
|
|
}
|
|
#endif
|
|
|
|
static void resetMsize(){
|
|
msize_iniz = msize;
|
|
msize_prev = 0;
|
|
msize_max = 0;
|
|
msize_min = (size_t)-1;
|
|
}
|
|
|
|
static void showMsize(){
|
|
char buf[15],buf2[15],buf3[15],buf4[15];
|
|
//printf("§§§ now: %s was: %s - min: %s - max: %s\n",readable_fs(msize,buf),readable_fs(msize_iniz,buf2),readable_fs(msize_min,buf3),readable_fs(msize_max,buf4));
|
|
resetMsize();
|
|
}
|
|
|
|
static void log_size(char *funcName){
|
|
if(msize_max < msize)
|
|
msize_max = msize;
|
|
if(msize_min > msize)
|
|
msize_min = msize;
|
|
if(1==0 && abs(msize-msize_prev)>msize_prev*LOG_TRESHOLD_PERC){
|
|
char buf[15],buf2[15];
|
|
//printf("§§§ %s - total: %s (was %s)\n",funcName, readable_fs(msize,buf),readable_fs(msize_prev,buf2));
|
|
msize_prev = msize;
|
|
}
|
|
}
|
|
|
|
static void *
|
|
my_malloc_default(void *opaque, unsigned int size)
|
|
{
|
|
struct header * h = malloc(size + sizeof(header));
|
|
if (h == NULL)
|
|
return NULL;
|
|
|
|
h -> magic = MAGIC;
|
|
h -> sz = size;
|
|
msize += size + sizeof(struct header);
|
|
if(is_realloc!=1)
|
|
log_size("alloc");
|
|
return (void *)(h + 1);
|
|
}
|
|
|
|
static void
|
|
my_free_default(void *opaque, void *ptr)
|
|
{
|
|
if (ptr != NULL) {
|
|
struct header * h = ((struct header *)ptr) - 1;
|
|
if (h -> magic != MAGIC) { /* Not allocated by us */
|
|
} else {
|
|
msize -= h -> sz + sizeof(struct header);
|
|
free(h);
|
|
}
|
|
}
|
|
if(is_realloc!=1)
|
|
log_size("free");
|
|
}
|
|
|
|
static void *
|
|
my_realloc_default(void *opaque, void *old, unsigned int size)
|
|
{
|
|
void * newp;
|
|
if (old==NULL) { //practically, it's a malloc
|
|
newp = my_malloc_default(opaque, size);
|
|
} else {
|
|
struct header * h = ((struct header *)old) - 1;
|
|
if (h -> magic != MAGIC) { // Not allocated by my_malloc_default
|
|
//printf("§§§ warn: not allocated by my_malloc_default, new size: %i\n",size);
|
|
newp = realloc(old,size);
|
|
} else { // malloc + free
|
|
is_realloc = 1;
|
|
size_t oldsize = h -> sz;
|
|
//printf("realloc %i -> %i\n",oldsize,size);
|
|
newp = my_malloc_default(opaque, size);
|
|
if (NULL != newp) {
|
|
memcpy(newp, old, oldsize<size?oldsize:size);
|
|
my_free_default(opaque, old);
|
|
}
|
|
log_size("realloc");
|
|
is_realloc = 0;
|
|
}
|
|
}
|
|
|
|
return(newp);
|
|
}
|
|
|
|
fz_alloc_context my_alloc_default =
|
|
{
|
|
NULL,
|
|
my_malloc_default,
|
|
my_realloc_default,
|
|
my_free_default
|
|
};
|
|
|
|
|
|
|
|
static int openDocument(lua_State *L) {
|
|
char *filename = strdup(luaL_checkstring(L, 1));
|
|
int cache_size = luaL_optint(L, 2, 64 << 20); // 64 MB limit default
|
|
char buf[15];
|
|
//printf("## cache_size: %s\n",readable_fs(cache_size,buf));
|
|
|
|
PdfDocument *doc = (PdfDocument*) lua_newuserdata(L, sizeof(PdfDocument));
|
|
|
|
luaL_getmetatable(L, "pdfdocument");
|
|
lua_setmetatable(L, -2);
|
|
|
|
doc->context = fz_new_context(&my_alloc_default, NULL, cache_size);
|
|
|
|
fz_try(doc->context) {
|
|
doc->xref = fz_open_document(doc->context, filename);
|
|
}
|
|
fz_catch(doc->context) {
|
|
free(filename);
|
|
return luaL_error(L, "cannot open PDF file");
|
|
}
|
|
|
|
free(filename);
|
|
return 1;
|
|
}
|
|
|
|
static int needsPassword(lua_State *L) {
|
|
PdfDocument *doc = (PdfDocument*) luaL_checkudata(L, 1, "pdfdocument");
|
|
lua_pushboolean(L, fz_needs_password(doc->xref));
|
|
return 1;
|
|
}
|
|
|
|
static int authenticatePassword(lua_State *L) {
|
|
PdfDocument *doc = (PdfDocument*) luaL_checkudata(L, 1, "pdfdocument");
|
|
char *password = strdup(luaL_checkstring(L, 2));
|
|
|
|
if (!fz_authenticate_password(doc->xref, password)) {
|
|
lua_pushboolean(L, 0);
|
|
} else {
|
|
lua_pushboolean(L, 1);
|
|
}
|
|
free(password);
|
|
return 1;
|
|
}
|
|
|
|
static int closeDocument(lua_State *L) {
|
|
PdfDocument *doc = (PdfDocument*) luaL_checkudata(L, 1, "pdfdocument");
|
|
|
|
// should be save if called twice
|
|
if(doc->xref != NULL) {
|
|
fz_close_document(doc->xref);
|
|
doc->xref = NULL;
|
|
}
|
|
if(doc->context != NULL) {
|
|
fz_free_context(doc->context);
|
|
doc->context = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int getNumberOfPages(lua_State *L) {
|
|
PdfDocument *doc = (PdfDocument*) luaL_checkudata(L, 1, "pdfdocument");
|
|
fz_try(doc->context) {
|
|
lua_pushinteger(L, fz_count_pages(doc->xref));
|
|
}
|
|
fz_catch(doc->context) {
|
|
return luaL_error(L, "cannot access page tree");
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* helper function for getTableOfContent()
|
|
*/
|
|
static int walkTableOfContent(lua_State *L, fz_outline* ol, int *count, int depth) {
|
|
depth++;
|
|
while(ol) {
|
|
lua_pushnumber(L, *count);
|
|
|
|
/* set subtable */
|
|
lua_newtable(L);
|
|
lua_pushstring(L, "page");
|
|
lua_pushnumber(L, ol->dest.ld.gotor.page + 1);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "depth");
|
|
lua_pushnumber(L, depth);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "title");
|
|
lua_pushstring(L, ol->title);
|
|
lua_settable(L, -3);
|
|
|
|
|
|
lua_settable(L, -3);
|
|
(*count)++;
|
|
if (ol->down) {
|
|
walkTableOfContent(L, ol->down, count, depth);
|
|
}
|
|
ol = ol->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Return a table like this:
|
|
* {
|
|
* {page=12, depth=1, title="chapter1"},
|
|
* {page=54, depth=1, title="chapter2"},
|
|
* }
|
|
*/
|
|
static int getTableOfContent(lua_State *L) {
|
|
fz_outline *ol;
|
|
int count = 1;
|
|
|
|
PdfDocument *doc = (PdfDocument*) luaL_checkudata(L, 1, "pdfdocument");
|
|
ol = fz_load_outline(doc->xref);
|
|
|
|
lua_newtable(L);
|
|
walkTableOfContent(L, ol, &count, 0);
|
|
return 1;
|
|
}
|
|
|
|
static int openPage(lua_State *L) {
|
|
fz_device *dev;
|
|
|
|
PdfDocument *doc = (PdfDocument*) luaL_checkudata(L, 1, "pdfdocument");
|
|
|
|
int pageno = luaL_checkint(L, 2);
|
|
|
|
fz_try(doc->context) {
|
|
if(pageno < 1 || pageno > fz_count_pages(doc->xref)) {
|
|
return luaL_error(L, "cannot open page #%d, out of range (1-%d)",
|
|
pageno, fz_count_pages(doc->xref));
|
|
}
|
|
|
|
PdfPage *page = (PdfPage*) lua_newuserdata(L, sizeof(PdfPage));
|
|
|
|
luaL_getmetatable(L, "pdfpage");
|
|
lua_setmetatable(L, -2);
|
|
|
|
page->page = fz_load_page(doc->xref, pageno - 1);
|
|
|
|
page->doc = doc;
|
|
}
|
|
fz_catch(doc->context) {
|
|
return luaL_error(L, "cannot open page #%d", pageno);
|
|
}
|
|
showMsize();
|
|
return 1;
|
|
}
|
|
|
|
static void load_lua_text_page(lua_State *L, fz_text_page *page)
|
|
{
|
|
fz_text_block *block;
|
|
fz_text_line *aline;
|
|
fz_text_span *span;
|
|
|
|
fz_rect bbox, linebbox;
|
|
int i;
|
|
int word, line;
|
|
int len, c;
|
|
int start;
|
|
char chars[4]; // max length of UTF-8 encoded rune
|
|
luaL_Buffer textbuf;
|
|
|
|
/* table that contains all the lines */
|
|
lua_newtable(L);
|
|
|
|
line = 1;
|
|
|
|
for (block = page->blocks; block < page->blocks + page->len; block++)
|
|
{
|
|
for (aline = block->lines; aline < block->lines + block->len; aline++)
|
|
{
|
|
linebbox = fz_empty_rect;
|
|
/* will hold information about a line: */
|
|
lua_newtable(L);
|
|
|
|
word = 1;
|
|
|
|
for (span = aline->spans; span < aline->spans + aline->len; span++)
|
|
{
|
|
for(i = 0; i < span->len; ) {
|
|
/* will hold information about a word: */
|
|
lua_newtable(L);
|
|
|
|
luaL_buffinit(L, &textbuf);
|
|
bbox = span->text[i].bbox; // start with sensible default
|
|
for(; i < span->len; i++) {
|
|
/* check for space characters */
|
|
if(span->text[i].c == ' ' ||
|
|
span->text[i].c == '\t' ||
|
|
span->text[i].c == '\n' ||
|
|
span->text[i].c == '\v' ||
|
|
span->text[i].c == '\f' ||
|
|
span->text[i].c == '\r' ||
|
|
span->text[i].c == 0xA0 ||
|
|
span->text[i].c == 0x1680 ||
|
|
span->text[i].c == 0x180E ||
|
|
(span->text[i].c >= 0x2000 && span->text[i].c <= 0x200A) ||
|
|
span->text[i].c == 0x202F ||
|
|
span->text[i].c == 0x205F ||
|
|
span->text[i].c == 0x3000) {
|
|
// ignore and end word
|
|
i++;
|
|
break;
|
|
}
|
|
len = fz_runetochar(chars, span->text[i].c);
|
|
for(c = 0; c < len; c++) {
|
|
luaL_addchar(&textbuf, chars[c]);
|
|
}
|
|
bbox = fz_union_rect(bbox, span->text[i].bbox);
|
|
linebbox = fz_union_rect(linebbox, span->text[i].bbox);
|
|
}
|
|
lua_pushstring(L, "word");
|
|
luaL_pushresult(&textbuf);
|
|
lua_settable(L, -3);
|
|
|
|
/* bbox for a word: */
|
|
lua_pushstring(L, "x0");
|
|
lua_pushinteger(L, bbox.x0);
|
|
lua_settable(L, -3);
|
|
lua_pushstring(L, "y0");
|
|
lua_pushinteger(L, bbox.y0);
|
|
lua_settable(L, -3);
|
|
lua_pushstring(L, "x1");
|
|
lua_pushinteger(L, bbox.x1);
|
|
lua_settable(L, -3);
|
|
lua_pushstring(L, "y1");
|
|
lua_pushinteger(L, bbox.y1);
|
|
lua_settable(L, -3);
|
|
|
|
lua_rawseti(L, -2, word++);
|
|
}
|
|
}
|
|
/* bbox for a whole line */
|
|
lua_pushstring(L, "x0");
|
|
lua_pushinteger(L, linebbox.x0);
|
|
lua_settable(L, -3);
|
|
lua_pushstring(L, "y0");
|
|
lua_pushinteger(L, linebbox.y0);
|
|
lua_settable(L, -3);
|
|
lua_pushstring(L, "x1");
|
|
lua_pushinteger(L, linebbox.x1);
|
|
lua_settable(L, -3);
|
|
lua_pushstring(L, "y1");
|
|
lua_pushinteger(L, linebbox.y1);
|
|
lua_settable(L, -3);
|
|
|
|
lua_rawseti(L, -2, line++);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* get the text of the given page
|
|
*
|
|
* will return text in a Lua table that is modeled after
|
|
* djvu.c creates this table.
|
|
*
|
|
* note that the definition of "line" is somewhat arbitrary
|
|
* here (for now)
|
|
*
|
|
* MuPDFs API provides text as single char information
|
|
* that is collected in "spans". we use a span as a "line"
|
|
* in Lua output and segment spans into words by looking
|
|
* for space characters.
|
|
*
|
|
* will return an empty table if we have no text
|
|
*/
|
|
static int getPageText(lua_State *L) {
|
|
fz_text_page *text_page;
|
|
fz_text_sheet *text_sheet;
|
|
fz_device *tdev;
|
|
|
|
PdfPage *page = (PdfPage*) luaL_checkudata(L, 1, "pdfpage");
|
|
|
|
text_page = fz_new_text_page(page->doc->context, fz_bound_page(page->doc->xref, page->page));
|
|
text_sheet = fz_new_text_sheet(page->doc->context);
|
|
tdev = fz_new_text_device(page->doc->context, text_sheet, text_page);
|
|
fz_run_page(page->doc->xref, page->page, tdev, fz_identity, NULL);
|
|
fz_free_device(tdev);
|
|
tdev = NULL;
|
|
|
|
load_lua_text_page(L, text_page);
|
|
|
|
fz_free_text_page(page->doc->context, text_page);
|
|
fz_free_text_sheet(page->doc->context, text_sheet);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int getPageSize(lua_State *L) {
|
|
fz_matrix ctm;
|
|
fz_rect bounds;
|
|
fz_rect bbox;
|
|
PdfPage *page = (PdfPage*) luaL_checkudata(L, 1, "pdfpage");
|
|
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 2, "drawcontext");
|
|
|
|
bounds = fz_bound_page(page->doc->xref, page->page);
|
|
ctm = fz_scale(dc->zoom, dc->zoom) ;
|
|
ctm = fz_concat(ctm, fz_rotate(dc->rotate));
|
|
bbox = fz_transform_rect(ctm, bounds);
|
|
|
|
lua_pushnumber(L, bbox.x1-bbox.x0);
|
|
lua_pushnumber(L, bbox.y1-bbox.y0);
|
|
|
|
return 2;
|
|
}
|
|
|
|
static int getUsedBBox(lua_State *L) {
|
|
fz_bbox result;
|
|
fz_matrix ctm;
|
|
fz_device *dev;
|
|
PdfPage *page = (PdfPage*) luaL_checkudata(L, 1, "pdfpage");
|
|
|
|
/* returned BBox is in centi-point (n * 0.01 pt) */
|
|
ctm = fz_scale(100, 100);
|
|
|
|
fz_try(page->doc->context) {
|
|
dev = fz_new_bbox_device(page->doc->context, &result);
|
|
fz_run_page(page->doc->xref, page->page, dev, ctm, NULL);
|
|
}
|
|
fz_always(page->doc->context) {
|
|
fz_free_device(dev);
|
|
}
|
|
fz_catch(page->doc->context) {
|
|
return luaL_error(L, "cannot calculate bbox for page");
|
|
}
|
|
|
|
lua_pushnumber(L, ((double)result.x0)/100);
|
|
lua_pushnumber(L, ((double)result.y0)/100);
|
|
lua_pushnumber(L, ((double)result.x1)/100);
|
|
lua_pushnumber(L, ((double)result.y1)/100);
|
|
|
|
return 4;
|
|
}
|
|
|
|
static int closePage(lua_State *L) {
|
|
PdfPage *page = (PdfPage*) luaL_checkudata(L, 1, "pdfpage");
|
|
if(page->page != NULL) {
|
|
fz_free_page(page->doc->xref, page->page);
|
|
page->page = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* bmpmupdf.c from willuslib */
|
|
static int bmpmupdf_pixmap_to_bmp(WILLUSBITMAP *bmp, fz_context *ctx, fz_pixmap *pixmap) {
|
|
unsigned char *p;
|
|
int ncomp, i, row, col;
|
|
|
|
bmp->width = fz_pixmap_width(ctx, pixmap);
|
|
bmp->height = fz_pixmap_height(ctx, pixmap);
|
|
ncomp = fz_pixmap_components(ctx, pixmap);
|
|
/* Has to be 8-bit or RGB */
|
|
if (ncomp != 2 && ncomp != 4)
|
|
return (-1);
|
|
bmp->bpp = (ncomp == 2) ? 8 : 24;
|
|
bmp_alloc(bmp);
|
|
if (ncomp == 2)
|
|
for (i = 0; i < 256; i++)
|
|
bmp->red[i] = bmp->green[i] = bmp->blue[i] = i;
|
|
p = fz_pixmap_samples(ctx, pixmap);
|
|
if (ncomp == 1)
|
|
for (row = 0; row < bmp->height; row++) {
|
|
unsigned char *dest;
|
|
dest = bmp_rowptr_from_top(bmp, row);
|
|
memcpy(dest, p, bmp->width);
|
|
p += bmp->width;
|
|
}
|
|
else if (ncomp == 2)
|
|
for (row = 0; row < bmp->height; row++) {
|
|
unsigned char *dest;
|
|
dest = bmp_rowptr_from_top(bmp, row);
|
|
for (col = 0; col < bmp->width; col++, dest++, p += 2)
|
|
dest[0] = p[0];
|
|
}
|
|
else
|
|
for (row = 0; row < bmp->height; row++) {
|
|
unsigned char *dest;
|
|
dest = bmp_rowptr_from_top(bmp, row);
|
|
for (col = 0; col < bmp->width;
|
|
col++, dest += ncomp - 1, p += ncomp)
|
|
memcpy(dest, p, ncomp - 1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int reflowPage(lua_State *L) {
|
|
PdfPage *page = (PdfPage*) luaL_checkudata(L, 1, "pdfpage");
|
|
KOPTContext *kctx = (KOPTContext*) luaL_checkudata(L, 2, "koptcontext");
|
|
fz_device *dev;
|
|
fz_pixmap *pix;
|
|
fz_rect bounds,bounds2;
|
|
fz_matrix ctm;
|
|
fz_bbox bbox;
|
|
|
|
pix = NULL;
|
|
fz_var(pix);
|
|
bounds.x0 = kctx->bbox.x0;
|
|
bounds.y0 = kctx->bbox.y0;
|
|
bounds.x1 = kctx->bbox.x1;
|
|
bounds.y1 = kctx->bbox.y1;
|
|
|
|
double dpp,zoom;
|
|
zoom = kctx->zoom;
|
|
double dpi = 250*zoom*kctx->quality;
|
|
|
|
do {
|
|
dpp = dpi / 72.;
|
|
ctm = fz_scale(dpp, dpp);
|
|
// ctm=fz_concat(ctm,fz_rotate(rotation));
|
|
bounds2 = fz_transform_rect(ctm, bounds);
|
|
bbox = fz_round_rect(bounds2);
|
|
printf("reading page:%d,%d,%d,%d zoom:%.2f dpi:%.0f\n",bbox.x0,bbox.y0,bbox.x1,bbox.y1,zoom,dpi);
|
|
kctx->zoom = zoom;
|
|
zoom *= kctx->shrink_factor;
|
|
dpi *= kctx->shrink_factor;
|
|
} while (bbox.x1 > kctx->read_max_width | bbox.y1 > kctx->read_max_height);
|
|
|
|
pix = fz_new_pixmap_with_bbox(page->doc->context, fz_device_gray, bbox);
|
|
fz_clear_pixmap_with_value(page->doc->context, pix, 0xff);
|
|
dev = fz_new_draw_device(page->doc->context, pix);
|
|
|
|
#ifdef MUPDF_TRACE
|
|
fz_device *tdev;
|
|
fz_try(page->doc->context) {
|
|
tdev = fz_new_trace_device(page->doc->context);
|
|
fz_run_page(page->doc->xref, page->page, tdev, ctm, NULL);
|
|
}
|
|
fz_always(page->doc->context) {
|
|
fz_free_device(tdev);
|
|
}
|
|
#endif
|
|
|
|
fz_run_page(page->doc->xref, page->page, dev, ctm, NULL);
|
|
fz_free_device(dev);
|
|
|
|
WILLUSBITMAP *src = malloc(sizeof(WILLUSBITMAP));
|
|
bmp_init(src);
|
|
|
|
int status = bmpmupdf_pixmap_to_bmp(src, page->doc->context, pix);
|
|
fz_drop_pixmap(page->doc->context, pix);
|
|
|
|
kctx->src = src;
|
|
if (kctx->precache) {
|
|
pthread_t rf_thread;
|
|
pthread_create( &rf_thread, NULL, k2pdfopt_reflow_bmp, (void*) kctx);
|
|
} else {
|
|
k2pdfopt_reflow_bmp(kctx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int drawReflowedPage(lua_State *L) {
|
|
PdfPage *page = (PdfPage*) luaL_checkudata(L, 1, "pdfpage");
|
|
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 2, "koptcontext");
|
|
BlitBuffer *bb = (BlitBuffer*) luaL_checkudata(L, 3, "blitbuffer");
|
|
|
|
uint8_t *koptr = kc->data;
|
|
uint8_t *bbptr = bb->data;
|
|
|
|
int x_offset = 0;
|
|
int y_offset = 0;
|
|
|
|
bbptr += bb->pitch * y_offset;
|
|
int x, y;
|
|
for(y = y_offset; y < bb->h; y++) {
|
|
for(x = x_offset/2; x < (bb->w/2); x++) {
|
|
int p = x*2 - x_offset;
|
|
bbptr[x] = (((koptr[p + 1] & 0xF0) >> 4) | (koptr[p] & 0xF0)) ^ 0xFF;
|
|
}
|
|
bbptr += bb->pitch;
|
|
koptr += bb->w;
|
|
if (bb->w & 1) {
|
|
bbptr[x] = 255 - (koptr[x*2] & 0xF0);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int drawPage(lua_State *L) {
|
|
fz_pixmap *pix;
|
|
fz_device *dev;
|
|
fz_matrix ctm;
|
|
fz_bbox bbox;
|
|
|
|
PdfPage *page = (PdfPage*) luaL_checkudata(L, 1, "pdfpage");
|
|
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 2, "drawcontext");
|
|
BlitBuffer *bb = (BlitBuffer*) luaL_checkudata(L, 3, "blitbuffer");
|
|
bbox.x0 = luaL_checkint(L, 4);
|
|
bbox.y0 = luaL_checkint(L, 5);
|
|
bbox.x1 = bbox.x0 + bb->w;
|
|
bbox.y1 = bbox.y0 + bb->h;
|
|
pix = fz_new_pixmap_with_bbox(page->doc->context, fz_device_gray, bbox);
|
|
fz_clear_pixmap_with_value(page->doc->context, pix, 0xff);
|
|
|
|
ctm = fz_scale(dc->zoom, dc->zoom);
|
|
ctm = fz_concat(ctm, fz_rotate(dc->rotate));
|
|
ctm = fz_concat(ctm, fz_translate(dc->offset_x, dc->offset_y));
|
|
dev = fz_new_draw_device(page->doc->context, pix);
|
|
#ifdef MUPDF_TRACE
|
|
fz_device *tdev;
|
|
fz_try(page->doc->context) {
|
|
tdev = fz_new_trace_device(page->doc->context);
|
|
fz_run_page(page->doc->xref, page->page, tdev, ctm, NULL);
|
|
}
|
|
fz_always(page->doc->context) {
|
|
fz_free_device(tdev);
|
|
}
|
|
#endif
|
|
fz_run_page(page->doc->xref, page->page, dev, ctm, NULL);
|
|
fz_free_device(dev);
|
|
|
|
if(dc->gamma >= 0.0) {
|
|
fz_gamma_pixmap(page->doc->context, pix, dc->gamma);
|
|
}
|
|
|
|
uint8_t *bbptr = (uint8_t*)bb->data;
|
|
uint16_t *pmptr = (uint16_t*)pix->samples;
|
|
int x, y;
|
|
|
|
for(y = 0; y < bb->h; y++) {
|
|
for(x = 0; x < (bb->w / 2); x++) {
|
|
bbptr[x] = (((pmptr[x*2 + 1] & 0xF0) >> 4) | (pmptr[x*2] & 0xF0)) ^ 0xFF;
|
|
}
|
|
if(bb->w & 1) {
|
|
bbptr[x] = (pmptr[x*2] & 0xF0) ^ 0xF0;
|
|
}
|
|
bbptr += bb->pitch;
|
|
pmptr += bb->w;
|
|
}
|
|
|
|
fz_drop_pixmap(page->doc->context, pix);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int getCacheSize(lua_State *L) {
|
|
//printf("## mupdf getCacheSize = %zu\n", msize);
|
|
lua_pushnumber(L, msize);
|
|
return 1;
|
|
}
|
|
|
|
static int cleanCache(lua_State *L) {
|
|
//printf("## mupdf cleanCache NOP\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int getPageLinks(lua_State *L) {
|
|
fz_link *page_links;
|
|
fz_link *link;
|
|
|
|
int link_count;
|
|
|
|
PdfPage *page = (PdfPage*) luaL_checkudata(L, 1, "pdfpage");
|
|
|
|
page_links = fz_load_links(page->doc->xref, page->page); // page->doc->xref?
|
|
|
|
lua_newtable(L); // all links
|
|
|
|
link_count = 0;
|
|
|
|
for (link = page_links; link; link = link->next) {
|
|
lua_newtable(L); // new link
|
|
|
|
lua_pushstring(L, "x0");
|
|
lua_pushinteger(L, link->rect.x0);
|
|
lua_settable(L, -3);
|
|
lua_pushstring(L, "y0");
|
|
lua_pushinteger(L, link->rect.y0);
|
|
lua_settable(L, -3);
|
|
lua_pushstring(L, "x1");
|
|
lua_pushinteger(L, link->rect.x1);
|
|
lua_settable(L, -3);
|
|
lua_pushstring(L, "y1");
|
|
lua_pushinteger(L, link->rect.y1);
|
|
lua_settable(L, -3);
|
|
|
|
if (link->dest.kind == FZ_LINK_URI) {
|
|
lua_pushstring(L, "uri");
|
|
lua_pushstring(L, link->dest.ld.uri.uri);
|
|
lua_settable(L, -3);
|
|
} else if (link->dest.kind == FZ_LINK_GOTO) {
|
|
lua_pushstring(L, "page");
|
|
lua_pushinteger(L, link->dest.ld.gotor.page); // FIXME page+1?
|
|
lua_settable(L, -3);
|
|
} else {
|
|
printf("ERROR: unkown link kind: %x", link->dest.kind);
|
|
}
|
|
|
|
lua_rawseti(L, -2, ++link_count);
|
|
}
|
|
|
|
//printf("## getPageLinks found %d links in document\n", link_count);
|
|
|
|
fz_drop_link(page->doc->context, page_links);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static const struct luaL_Reg pdf_func[] = {
|
|
{"openDocument", openDocument},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const struct luaL_Reg pdfdocument_meth[] = {
|
|
{"needsPassword", needsPassword},
|
|
{"authenticatePassword", authenticatePassword},
|
|
{"openPage", openPage},
|
|
{"getPages", getNumberOfPages},
|
|
{"getToc", getTableOfContent},
|
|
{"close", closeDocument},
|
|
{"getCacheSize", getCacheSize},
|
|
{"cleanCache", cleanCache},
|
|
{"__gc", closeDocument},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const struct luaL_Reg pdfpage_meth[] = {
|
|
{"getSize", getPageSize},
|
|
{"getUsedBBox", getUsedBBox},
|
|
{"getPageText", getPageText},
|
|
{"getPageLinks", getPageLinks},
|
|
{"close", closePage},
|
|
{"__gc", closePage},
|
|
{"reflow", reflowPage},
|
|
{"rfdraw", drawReflowedPage},
|
|
{"draw", drawPage},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
int luaopen_pdf(lua_State *L) {
|
|
luaL_newmetatable(L, "pdfdocument");
|
|
lua_pushstring(L, "__index");
|
|
lua_pushvalue(L, -2);
|
|
lua_settable(L, -3);
|
|
luaL_register(L, NULL, pdfdocument_meth);
|
|
lua_pop(L, 1);
|
|
luaL_newmetatable(L, "pdfpage");
|
|
lua_pushstring(L, "__index");
|
|
lua_pushvalue(L, -2);
|
|
lua_settable(L, -3);
|
|
luaL_register(L, NULL, pdfpage_meth);
|
|
lua_pop(L, 1);
|
|
luaL_register(L, "pdf", pdf_func);
|
|
return 1;
|
|
}
|