Merge remote-tracking branch 'hwhw/master' into djvu

pull/2/merge
traycold 12 years ago
commit eba1c2d5ef

2
.gitignore vendored

@ -4,7 +4,9 @@ lua-*
lsqlite3* lsqlite3*
sqlite-amalgamation* sqlite-amalgamation*
mupdf/ mupdf/
djvulibre/
luafilesystem/ luafilesystem/
.reader.kpdfview.lua
kpdfview kpdfview
djvulibre* djvulibre*

@ -2,12 +2,10 @@
LUADIR=lua LUADIR=lua
MUPDFDIR=mupdf MUPDFDIR=mupdf
DJVUDIR=djvulibre
MUPDFTARGET=build/debug MUPDFTARGET=build/debug
MUPDFLIBDIR=$(MUPDFDIR)/$(MUPDFTARGET) MUPDFLIBDIR=$(MUPDFDIR)/$(MUPDFTARGET)
DJVUDIR=djvulibre
SQLITE3DIR=sqlite-amalgamation-3070900
LSQLITE3DIR=lsqlite3_svn08
FREETYPEDIR=$(MUPDFDIR)/thirdparty/freetype-2.4.8 FREETYPEDIR=$(MUPDFDIR)/thirdparty/freetype-2.4.8
LFSDIR=luafilesystem LFSDIR=luafilesystem
@ -22,7 +20,10 @@ endif
HOSTCC:=gcc HOSTCC:=gcc
HOSTCXX:=g++ HOSTCXX:=g++
CFLAGS:=-O0 -g CFLAGS:=-O3
ARM_CFLAGS:=-march=armv6
# use this for debugging:
#CFLAGS:=-O0 -g
# you can configure an emulation for the (eink) framebuffer here. # you can configure an emulation for the (eink) framebuffer here.
# the application won't use the framebuffer (and the special e-ink ioctls) # the application won't use the framebuffer (and the special e-ink ioctls)
@ -38,6 +39,8 @@ ifdef EMULATE_READER
-DEMULATE_READER_W=$(EMULATE_READER_W) \ -DEMULATE_READER_W=$(EMULATE_READER_W) \
-DEMULATE_READER_H=$(EMULATE_READER_H) \ -DEMULATE_READER_H=$(EMULATE_READER_H) \
EMU_LDFLAGS?=$(shell sdl-config --libs) EMU_LDFLAGS?=$(shell sdl-config --libs)
else
CFLAGS+= $(ARM_CFLAGS)
endif endif
# standard includes # standard includes
@ -57,28 +60,23 @@ THIRDPARTYLIBS := $(MUPDFLIBDIR)/libfreetype.a \
$(MUPDFLIBDIR)/libjbig2dec.a \ $(MUPDFLIBDIR)/libjbig2dec.a \
$(MUPDFLIBDIR)/libz.a $(MUPDFLIBDIR)/libz.a
# comment this out to build without sqlite3
SQLITE3OBJS := lsqlite3.o sqlite3.o
SQLITE3LDFLAGS := -lpthread
LUALIB := $(LUADIR)/src/liblua.a LUALIB := $(LUADIR)/src/liblua.a
kpdfview: kpdfview.o einkfb.o pdf.o djvu.o blitbuffer.o input.o util.o ft.o $(SQLITE3OBJS) lfs.o $(MUPDFLIBS) $(DJVULIBS) $(THIRDPARTYLIBS) $(LUALIB) kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o input.o util.o ft.o lfs.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) djvu.o
$(CC) -lm -ldl -lstdc++ $(EMU_LDFLAGS) $(SQLITE3LDFLAGS) \ $(CC) -lm -ldl -lpthread $(EMU_LDFLAGS) -lstdc++ \
kpdfview.o \ kpdfview.o \
einkfb.o \ einkfb.o \
pdf.o \ pdf.o \
djvu.o \
blitbuffer.o \ blitbuffer.o \
input.o \ input.o \
util.o \ util.o \
ft.o \ ft.o \
$(SQLITE3OBJS) \
lfs.o \ lfs.o \
$(MUPDFLIBS) \ $(MUPDFLIBS) \
$(DJVULIBS) \
$(THIRDPARTYLIBS) \ $(THIRDPARTYLIBS) \
$(LUALIB) \ $(LUALIB) \
djvu.o \
$(DJVULIBS) \
-o kpdfview -o kpdfview
einkfb.o input.o: %.o: %.c einkfb.o input.o: %.o: %.c
@ -93,27 +91,17 @@ kpdfview.o pdf.o blitbuffer.o util.o: %.o: %.c
djvu.o: %.o: %.c djvu.o: %.o: %.c
$(CC) -c $(KPDFREADER_CFLAGS) -I$(DJVUDIR)/ $< -o $@ $(CC) -c $(KPDFREADER_CFLAGS) -I$(DJVUDIR)/ $< -o $@
sqlite3.o: $(SQLITE3DIR)/sqlite3.c
$(CC) -c $(CFLAGS) $(SQLITE3DIR)/sqlite3.c -o $@
lsqlite3.o: $(LSQLITE3DIR)/lsqlite3.c
$(CC) -c $(CFLAGS) -I$(LUADIR)/src -I$(SQLITE3DIR) $(LSQLITE3DIR)/lsqlite3.c -o $@
lfs.o: $(LFSDIR)/src/lfs.c lfs.o: $(LFSDIR)/src/lfs.c
$(CC) -c $(CFLAGS) -I$(LUADIR)/src -I$(LFSDIR)/src $(LFSDIR)/src/lfs.c -o $@ $(CC) -c $(CFLAGS) -I$(LUADIR)/src -I$(LFSDIR)/src $(LFSDIR)/src/lfs.c -o $@
fetchthirdparty: fetchthirdparty:
-rm -Rf mupdf -rm -Rf mupdf
-rm -Rf lua lua-5.1.4* -rm -Rf lua lua-5.1.4*
-rm -Rf lsqlite3_svn08*
-rm -Rf sqlite-amalgamation-3070900*
-rm -Rf luafilesystem* -rm -Rf luafilesystem*
-rm -Rf $(DJVUDIR) -rm -Rf $(DJVUDIR)
git clone git://git.ghostscript.com/mupdf.git git clone git://git.ghostscript.com/mupdf.git
( cd mupdf ; wget http://www.mupdf.com/download/mupdf-thirdparty.zip && unzip mupdf-thirdparty.zip ) ( cd mupdf ; wget http://www.mupdf.com/download/mupdf-thirdparty.zip && unzip mupdf-thirdparty.zip )
wget http://www.lua.org/ftp/lua-5.1.4.tar.gz && tar xvzf lua-5.1.4.tar.gz && ln -s lua-5.1.4 lua wget http://www.lua.org/ftp/lua-5.1.4.tar.gz && tar xvzf lua-5.1.4.tar.gz && ln -s lua-5.1.4 lua
wget "http://lua.sqlite.org/index.cgi/zip/lsqlite3_svn08.zip?uuid=svn_8" && unzip "lsqlite3_svn08.zip?uuid=svn_8"
wget "http://sqlite.org/sqlite-amalgamation-3070900.zip" && unzip sqlite-amalgamation-3070900.zip
git clone https://github.com/keplerproject/luafilesystem.git git clone https://github.com/keplerproject/luafilesystem.git
git clone git://djvu.git.sourceforge.net/gitroot/djvu/djvulibre.git git clone git://djvu.git.sourceforge.net/gitroot/djvu/djvulibre.git

@ -1,5 +1,5 @@
/* /*
KindlePDFViewer: MuPDF abstraction for Lua KindlePDFViewer: DjvuLibre abstraction for Lua
Copyright (C) 2011 Hans-Werner Hilse <hilse@web.de> Copyright (C) 2011 Hans-Werner Hilse <hilse@web.de>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
@ -117,6 +117,7 @@ static int closeDocument(lua_State *L) {
doc->context = NULL; doc->context = NULL;
} }
return 0;
} }
static int getNumberOfPages(lua_State *L) { static int getNumberOfPages(lua_State *L) {
@ -125,6 +126,18 @@ static int getNumberOfPages(lua_State *L) {
return 1; return 1;
} }
/* not supported yet, so return empty table */
static int getTableOfContent(lua_State *L) {
/*int count = 1;*/
DjvuDocument *doc = (DjvuDocument*) luaL_checkudata(L, 1, "djvudocument");
/*ol = djvu_load_outline(doc->doc_ref);*/
lua_newtable(L);
/*walkTableOfContent(L, ol, &count, 0);*/
return 1;
}
static int newDrawContext(lua_State *L) { static int newDrawContext(lua_State *L) {
int rotate = luaL_optint(L, 1, 0); int rotate = luaL_optint(L, 1, 0);
double zoom = luaL_optnumber(L, 2, (double) 1.0); double zoom = luaL_optnumber(L, 2, (double) 1.0);
@ -238,34 +251,17 @@ static int getPageSize(lua_State *L) {
return 2; return 2;
} }
/*static int getUsedBBox(lua_State *L) {*/ /* unsupported so fake it */
/*fz_bbox result;*/ static int getUsedBBox(lua_State *L) {
/*fz_matrix ctm;*/ DjvuPage *page = (DjvuPage*) luaL_checkudata(L, 1, "djvupage");
/*fz_device *dev;*/
/*DjvuPage *page = (DjvuPage*) luaL_checkudata(L, 1, "djvupage");*/ lua_pushnumber(L, (double)0.01);
lua_pushnumber(L, (double)0.01);
/*[> returned BBox is in centi-point (n * 0.01 pt) <]*/ lua_pushnumber(L, (double)-0.01);
/*ctm = fz_scale(100, 100);*/ lua_pushnumber(L, (double)-0.01);
/*ctm = fz_concat(ctm, fz_rotate(page->page->rotate));*/
return 4;
/*fz_try(page->doc->context) {*/ }
/*dev = fz_new_bbox_device(page->doc->context, &result);*/
/*pdf_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) { static int closePage(lua_State *L) {
DjvuPage *page = (DjvuPage*) luaL_checkudata(L, 1, "djvupage"); DjvuPage *page = (DjvuPage*) luaL_checkudata(L, 1, "djvupage");
@ -376,7 +372,7 @@ static const struct luaL_reg djvu_func[] = {
static const struct luaL_reg djvudocument_meth[] = { static const struct luaL_reg djvudocument_meth[] = {
{"openPage", openPage}, {"openPage", openPage},
{"getPages", getNumberOfPages}, {"getPages", getNumberOfPages},
/*{"getTOC", getTableOfContent},*/ {"getTOC", getTableOfContent},
{"close", closeDocument}, {"close", closeDocument},
{"__gc", closeDocument}, {"__gc", closeDocument},
{NULL, NULL} {NULL, NULL}
@ -384,7 +380,7 @@ static const struct luaL_reg djvudocument_meth[] = {
static const struct luaL_reg djvupage_meth[] = { static const struct luaL_reg djvupage_meth[] = {
{"getSize", getPageSize}, {"getSize", getPageSize},
/*{"getUsedBBox", getUsedBBox},*/ {"getUsedBBox", getUsedBBox},
{"close", closePage}, {"close", closePage},
{"__gc", closePage}, {"__gc", closePage},
{"draw", drawPage}, {"draw", drawPage},

@ -1,5 +1,5 @@
/* /*
KindlePDFViewer: MuPDF abstraction for Lua KindlePDFViewer: DjvuLibre abstraction for Lua
Copyright (C) 2011 Hans-Werner Hilse <hilse@web.de> Copyright (C) 2011 Hans-Werner Hilse <hilse@web.de>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
@ -22,7 +22,7 @@
#include <lualib.h> #include <lualib.h>
#include <lauxlib.h> #include <lauxlib.h>
int luaopen_pdf(lua_State *L); int luaopen_djvu(lua_State *L);
#define True 1 #define True 1
#endif #endif

@ -1,70 +1,19 @@
require "unireader" require "unireader"
DJVUReader = UniReader:new{} DJVUReader = UniReader:new{
newDC = function()
print("djvu.newDC")
return djvu.newDC()
end,
}
function DJVUReader:init() function DJVUReader:init()
self.nulldc = djvu.newDC() self.nulldc = self.newDC()
end end
-- open a DJVU file and its settings store -- open a DJVU file and its settings store
-- DJVU does not support password yet
function DJVUReader:open(filename) function DJVUReader:open(filename)
self.doc = djvu.openDocument(filename) self.doc = djvu.openDocument(filename)
if self.doc ~= nil then return self:loadSettings(filename)
self.settings = DocSettings:open(filename)
local gamma = self.settings:readsetting("gamma")
if gamma then
self.globalgamma = gamma
end
return true
end
return false
end
-- set viewer state according to zoom state
function DJVUReader:setzoom(page)
local dc = djvu.newDC()
local pwidth, pheight = page:getSize(self.nulldc)
if self.globalzoommode == self.ZOOM_FIT_TO_PAGE then
self.globalzoom = width / pwidth
self.offset_x = 0
self.offset_y = (height - (self.globalzoom * pheight)) / 2
if height / pheight < self.globalzoom then
self.globalzoom = height / pheight
print(width, (self.globalzoom * pwidth))
self.offset_x = (width - (self.globalzoom * pwidth)) / 2
self.offset_y = 0
end
elseif self.globalzoommode == self.ZOOM_FIT_TO_PAGE_WIDTH then
self.globalzoom = width / pwidth
self.offset_x = 0
self.offset_y = (height - (self.globalzoom * pheight)) / 2
elseif self.globalzoommode == self.ZOOM_FIT_TO_PAGE_HEIGHT then
self.globalzoom = height / pheight
self.offset_x = (width - (self.globalzoom * pwidth)) / 2
self.offset_y = 0
end
dc:setZoom(self.globalzoom)
-- record globalzoom for manual zoom in/out
self.globalzoom_orig = self.globalzoom
dc:setRotate(self.globalrotate);
dc:setOffset(self.offset_x, self.offset_y)
self.fullwidth, self.fullheight = page:getSize(dc)
self.min_offset_x = fb.bb:getWidth() - self.fullwidth
self.min_offset_y = fb.bb:getHeight() - self.fullheight
if(self.min_offset_x > 0) then
self.min_offset_x = 0
end
if(self.min_offset_y > 0) then
self.min_offset_y = 0
end
-- set gamma here, we don't have any other good place for this right now:
if self.globalgamma ~= self.GAMMA_NO_GAMMA then
print("gamma correction: "..self.globalgamma)
dc:setGamma(self.globalgamma)
end
return dc
end end

@ -65,7 +65,7 @@ function FileChooser:readdir()
if lfs.attributes(self.path.."/"..f, "mode") == "directory" and f ~= "." and not (f==".." and self.path=="/") and not string.match(f, "^%.[^.]") then if lfs.attributes(self.path.."/"..f, "mode") == "directory" and f ~= "." and not (f==".." and self.path=="/") and not string.match(f, "^%.[^.]") then
--print(self.path.." -> adding: '"..f.."'") --print(self.path.." -> adding: '"..f.."'")
table.insert(self.dirs, f) table.insert(self.dirs, f)
elseif string.match(f, ".+%.[pP][dD][fF]$") or string.match(f, ".+%.[dD][jJ][vV][uU]$")then elseif string.match(f, ".+%.[pP][dD][fF]$") or string.match(f, ".+%.[dD][jJ][vV][uU]$") then
table.insert(self.files, f) table.insert(self.files, f)
end end
end end

@ -113,6 +113,7 @@ end
function set_emu_keycodes() function set_emu_keycodes()
KEY_PGFWD = 117 KEY_PGFWD = 117
KEY_PGBCK = 112 KEY_PGBCK = 112
KEY_HOME = 110 -- home
KEY_BACK = 22 -- backspace KEY_BACK = 22 -- backspace
KEY_DEL = 119 -- Delete KEY_DEL = 119 -- Delete
KEY_MENU = 67 -- F1 KEY_MENU = 67 -- F1

@ -32,9 +32,6 @@
#include "lfs.h" #include "lfs.h"
/* forward declaration for luasqlite3: */
LUALIB_API int luaopen_lsqlite3(lua_State *L);
lua_State *L; lua_State *L;
int main(int argc, char **argv) { int main(int argc, char **argv) {
@ -58,7 +55,6 @@ int main(int argc, char **argv) {
luaopen_util(L); luaopen_util(L);
luaopen_ft(L); luaopen_ft(L);
luaopen_lsqlite3(L);
luaopen_lfs(L); luaopen_lfs(L);
lua_newtable(L); lua_newtable(L);

11
pdf.c

@ -79,6 +79,7 @@ static int closeDocument(lua_State *L) {
fz_free_context(doc->context); fz_free_context(doc->context);
doc->context = NULL; doc->context = NULL;
} }
return 0;
} }
static int getNumberOfPages(lua_State *L) { static int getNumberOfPages(lua_State *L) {
@ -105,16 +106,6 @@ static int walkTableOfContent(lua_State *L, fz_outline* ol, int *count, int dept
lua_settable(L, -3); lua_settable(L, -3);
lua_pushstring(L, "title"); lua_pushstring(L, "title");
/* workaround for misplaced carriage ret in toc entry */
int i = 0;
while (ol->title[i]) {
if (ol->title[i] == 0x0d) {
ol->title[i] = ' ';
}
/*printf("%x|", ol->title[i]);*/
i++;
}
lua_pushstring(L, ol->title); lua_pushstring(L, ol->title);
lua_settable(L, -3); lua_settable(L, -3);

@ -1,113 +1,18 @@
require "unireader" require "unireader"
PDFReader = UniReader:new{} PDFReader = UniReader:new{
newDC = function()
print("pdf.newDC")
return pdf.newDC()
end,
}
function PDFReader:init() function PDFReader:init()
self.nulldc = pdf.newDC() self.nulldc = self.newDC();
end end
-- open a PDF file and its settings store -- open a PDF file and its settings store
function PDFReader:open(filename, password) function PDFReader:open(filename, password)
self.doc = pdf.openDocument(filename, password or "") self.doc = pdf.openDocument(filename, password or "")
if self.doc ~= nil then return self:loadSettings(filename)
self.settings = DocSettings:open(filename)
local gamma = self.settings:readsetting("gamma")
if gamma then
self.globalgamma = gamma
end
return true
end
return false
end
-- set viewer state according to zoom state
function PDFReader:setzoom(page)
local dc = pdf.newDC()
local pwidth, pheight = page:getSize(self.nulldc)
if self.globalzoommode == self.ZOOM_FIT_TO_PAGE
or self.globalzoommode == self.ZOOM_FIT_TO_CONTENT then
self.globalzoom = width / pwidth
self.offset_x = 0
self.offset_y = (height - (self.globalzoom * pheight)) / 2
if height / pheight < self.globalzoom then
self.globalzoom = height / pheight
self.offset_x = (width - (self.globalzoom * pwidth)) / 2
self.offset_y = 0
end
elseif self.globalzoommode == self.ZOOM_FIT_TO_PAGE_WIDTH
or self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_WIDTH then
self.globalzoom = width / pwidth
self.offset_x = 0
self.offset_y = (height - (self.globalzoom * pheight)) / 2
elseif self.globalzoommode == self.ZOOM_FIT_TO_PAGE_HEIGHT
or self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_HEIGHT then
self.globalzoom = height / pheight
self.offset_x = (width - (self.globalzoom * pwidth)) / 2
self.offset_y = 0
end
if self.globalzoommode == self.ZOOM_FIT_TO_CONTENT then
local x0, y0, x1, y1 = page:getUsedBBox()
if (x1 - x0) < pwidth then
self.globalzoom = width / (x1 - x0)
self.offset_x = -1 * x0 * self.globalzoom
self.offset_y = -1 * y0 * self.globalzoom + (height - (self.globalzoom * (y1 - y0))) / 2
if height / (y1 - y0) < self.globalzoom then
self.globalzoom = height / (y1 - y0)
self.offset_x = -1 * x0 * self.globalzoom + (width - (self.globalzoom * (x1 - x0))) / 2
self.offset_y = -1 * y0 * self.globalzoom
end
end
elseif self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_WIDTH then
local x0, y0, x1, y1 = page:getUsedBBox()
if (x1 - x0) < pwidth then
self.globalzoom = width / (x1 - x0)
self.offset_x = -1 * x0 * self.globalzoom
self.offset_y = -1 * y0 * self.globalzoom + (height - (self.globalzoom * (y1 - y0))) / 2
end
elseif self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_HEIGHT then
local x0, y0, x1, y1 = page:getUsedBBox()
if (y1 - y0) < pheight then
self.globalzoom = height / (y1 - y0)
self.offset_x = -1 * x0 * self.globalzoom + (width - (self.globalzoom * (x1 - x0))) / 2
self.offset_y = -1 * y0 * self.globalzoom
end
elseif self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_HALF_WIDTH then
local x0, y0, x1, y1 = page:getUsedBBox()
self.globalzoom = width / (x1 - x0 + self.pan_margin)
self.offset_x = -1 * x0 * self.globalzoom * 2 + self.pan_margin
self.globalzoom = height / (y1 - y0)
self.offset_y = -1 * y0 * self.globalzoom * 2 + self.pan_margin
self.globalzoom = width / (x1 - x0 + self.pan_margin) * 2
print("column mode offset:"..self.offset_x.."*"..self.offset_y.." zoom:"..self.globalzoom);
self.globalzoommode = self.ZOOM_BY_VALUE -- enable pan mode
self.pan_x = self.offset_x
self.pan_y = self.offset_y
self.pan_by_page = true
end
dc:setZoom(self.globalzoom)
-- record globalzoom for manual zoom in/out
self.globalzoom_orig = self.globalzoom
dc:setRotate(self.globalrotate);
dc:setOffset(self.offset_x, self.offset_y)
self.fullwidth, self.fullheight = page:getSize(dc)
self.min_offset_x = fb.bb:getWidth() - self.fullwidth
self.min_offset_y = fb.bb:getHeight() - self.fullheight
if(self.min_offset_x > 0) then
self.min_offset_x = 0
end
if(self.min_offset_y > 0) then
self.min_offset_y = 0
end
-- set gamma here, we don't have any other good place for this right now:
if self.globalgamma ~= self.GAMMA_NO_GAMMA then
print("gamma correction: "..self.globalgamma)
dc:setGamma(self.globalgamma)
end
return dc
end end

@ -38,15 +38,15 @@ function openFile(filename)
if DJVUReader:open(filename) then if DJVUReader:open(filename) then
page_num = DJVUReader.settings:readsetting("last_page") or 1 page_num = DJVUReader.settings:readsetting("last_page") or 1
DJVUReader:goto(tonumber(page_num)) DJVUReader:goto(tonumber(page_num))
DJVUReader:inputloop()
reader_settings:savesetting("lastfile", filename) reader_settings:savesetting("lastfile", filename)
return DJVUReader:inputloop()
end end
elseif file_type == "pdf" then elseif file_type == "pdf" then
if PDFReader:open(filename,"") then -- TODO: query for password if PDFReader:open(filename,"") then -- TODO: query for password
page_num = PDFReader.settings:readsetting("last_page") or 1 page_num = PDFReader.settings:readsetting("last_page") or 1
PDFReader:goto(tonumber(page_num)) PDFReader:goto(tonumber(page_num))
PDFReader:inputloop()
reader_settings:savesetting("lastfile", filename) reader_settings:savesetting("lastfile", filename)
return PDFReader:inputloop()
end end
end end
end end
@ -124,19 +124,19 @@ PDFReader:init()
DJVUReader:init() DJVUReader:init()
-- display directory or open file -- display directory or open file
local patharg = ARGV[optind] or reader_settings:readsetting("lastfile") local patharg = reader_settings:readsetting("lastfile")
if patharg and lfs.attributes(patharg, "mode") == "directory" then if ARGV[optind] and lfs.attributes(ARGV[optind], "mode") == "directory" then
local running = true local running = true
FileChooser:setPath(patharg) FileChooser:setPath(ARGV[optind])
while running do while running do
local file = FileChooser:choose(0,height) local file = FileChooser:choose(0,height)
if file ~= nil then if file ~= nil then
openFile(file) running = openFile(file)
else else
running = false running = false
end end
end end
elseif patharg then elseif patharg and lfs.attributes(patharg, "mode") == "file" then
openFile(patharg, optarg["p"]) openFile(patharg, optarg["p"])
else else
return showusage() return showusage()

@ -1,38 +1,66 @@
DocSettings = {} DocSettings = {}
function DocSettings:open(docfile) function DocSettings:open(docfile)
local new = {} local new = { file = docfile..".kpdfview.lua", data = {} }
new.docdb, errno, errstr = sqlite3.open(docfile..".kpdfview") local ok, stored = pcall(dofile,new.file)
if new.docdb ~= nil then if ok then
new.docdb:exec("CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT);") new.data = stored
new.stmt_readsetting = new.docdb:prepare("SELECT value FROM settings WHERE key = ?;")
new.stmt_savesetting = new.docdb:prepare("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?);")
end end
return setmetatable(new, { __index = DocSettings}) return setmetatable(new, { __index = DocSettings})
end end
function DocSettings:readsetting(key) function DocSettings:readsetting(key)
if self.docdb ~= nil then return self.data[key]
self.stmt_readsetting:reset() end
self.stmt_readsetting:bind_values(key)
local result = self.stmt_readsetting:step() function DocSettings:savesetting(key, value)
if result == sqlite3.ROW then self.data[key] = value
return self.stmt_readsetting:get_value(0) end
-- simple serialization function, won't do uservalues, functions, loops
function DocSettings:_serialize(what, outt, indent)
if type(what) == "table" then
local didrun = false
table.insert(outt, "{")
for k, v in pairs(what) do
if didrun then
table.insert(outt, ",")
end
table.insert(outt, "\n")
table.insert(outt, string.rep("\t", indent+1))
table.insert(outt, "[")
self:_serialize(k, outt, indent+1)
table.insert(outt, "] = ")
self:_serialize(v, outt, indent+1)
didrun = true
end end
if didrun then
table.insert(outt, "\n")
table.insert(outt, string.rep("\t", indent))
end
table.insert(outt, "}")
elseif type(what) == "string" then
table.insert(outt, string.format("%q", what))
elseif type(what) == "number" or type(what) == "boolean" then
table.insert(outt, tostring(what))
end end
end end
function DocSettings:savesetting(key, value) function DocSettings:flush()
if self.docdb ~= nil then -- write a serialized version of the data table
self.stmt_savesetting:reset() if not self.file then
self.stmt_savesetting:bind_values(key, value) return
self.stmt_savesetting:step() end
local f_out = io.open(self.file, "w")
if f_out ~= nil then
local out = {"-- we can read Lua syntax here!\nreturn "}
self:_serialize(self.data, out, 0)
table.insert(out, "\n")
f_out:write(table.concat(out))
f_out:close()
end end
end end
function DocSettings:close() function DocSettings:close()
if self.docdb ~= nil then self:flush()
self.docdb:close()
self.docdb = nil
end
end end

@ -11,7 +11,8 @@ UniReader = {
ZOOM_FIT_TO_CONTENT = -4, ZOOM_FIT_TO_CONTENT = -4,
ZOOM_FIT_TO_CONTENT_WIDTH = -5, ZOOM_FIT_TO_CONTENT_WIDTH = -5,
ZOOM_FIT_TO_CONTENT_HEIGHT = -6, ZOOM_FIT_TO_CONTENT_HEIGHT = -6,
ZOOM_FIT_TO_CONTENT_HALF_WIDTH = -7, ZOOM_FIT_TO_CONTENT_HALF_WIDTH_MARGIN = -7,
ZOOM_FIT_TO_CONTENT_HALF_WIDTH = -8,
GAMMA_NO_GAMMA = 1.0, GAMMA_NO_GAMMA = 1.0,
@ -43,16 +44,18 @@ UniReader = {
pan_by_page = false, -- using shift_[xy] or width/height pan_by_page = false, -- using shift_[xy] or width/height
pan_x = 0, -- top-left offset of page when pan activated pan_x = 0, -- top-left offset of page when pan activated
pan_y = 0, pan_y = 0,
pan_margin = 20, pan_margin = 20, -- horizontal margin for two-column zoom
pan_overlap_vertical = 30,
-- the document: -- the document:
doc = nil, doc = nil,
-- the document's setting store: -- the document's setting store:
settings = nil, settings = nil,
-- you have to initialize newDC, nulldc in specific reader
newDC = function() return nil end,
-- we will use this one often, so keep it "static": -- we will use this one often, so keep it "static":
--nulldc = pdf.newDC(), nulldc = nil,
nulldc = nil, -- you have to initialize it in specific reader
-- tile cache configuration: -- tile cache configuration:
cache_max_memsize = 1024*1024*5, -- 5MB tile cache cache_max_memsize = 1024*1024*5, -- 5MB tile cache
@ -62,6 +65,7 @@ UniReader = {
cache_current_memsize = 0, cache_current_memsize = 0,
cache = {}, cache = {},
jump_stack = {}, jump_stack = {},
toc = nil,
} }
function UniReader:new(o) function UniReader:new(o)
@ -71,10 +75,46 @@ function UniReader:new(o)
return o return o
end end
--[[
For a new specific reader,
you must always overwrite following two methods:
* self:init()
* self:open()
overwrite other methods if needed.
--]]
function UniReader:init() function UniReader:init()
print("empty initialization method!") print("empty initialization method!")
end end
-- open a file and its settings store
-- tips: you can use self:loadSettings in open() method.
function UniReader:open(filename, password)
return false
end
--[ following are default methods ]--
function UniReader:loadSettings(filename)
if self.doc ~= nil then
self.settings = DocSettings:open(filename)
local gamma = self.settings:readsetting("gamma")
if gamma then
self.globalgamma = gamma
end
local jumpstack = self.settings:readsetting("jumpstack")
self.jump_stack = jumpstack or {}
return true
end
return false
end
-- guarantee that we have enough memory in cache -- guarantee that we have enough memory in cache
function UniReader:cacheclaim(size) function UniReader:cacheclaim(size)
if(size > self.cache_max_memsize) then if(size > self.cache_max_memsize) then
@ -135,14 +175,113 @@ function UniReader:clearcache()
self.cache_current_memsize = 0 self.cache_current_memsize = 0
end end
-- open a file and its settings store
function UniReader:open(filename, password)
return false
end
-- set viewer state according to zoom state -- set viewer state according to zoom state
function UniReader:setzoom(page) function UniReader:setzoom(page)
return nil local dc = self.newDC()
local pwidth, pheight = page:getSize(self.nulldc)
print("# page::getSize "..pwidth.."*"..pheight);
local x0, y0, x1, y1 = page:getUsedBBox()
if x0 == 0.01 and y0 == 0.01 and x1 == -0.01 and y1 == -0.01 then
x0 = 0
y0 = 0
x1 = pwidth
y1 = pheight
end
-- clamp to page BBox
if x0 < 0 then x0 = 0 end
if x1 > pwidth then x1 = pwidth end
if y0 < 0 then y0 = 0 end
if y1 > pheight then y1 = pheight end
print("# page::getUsedBBox "..x0.."*"..y0.." "..x1.."*"..y1);
if self.globalzoommode == self.ZOOM_FIT_TO_PAGE
or self.globalzoommode == self.ZOOM_FIT_TO_CONTENT then
self.globalzoom = width / pwidth
self.offset_x = 0
self.offset_y = (height - (self.globalzoom * pheight)) / 2
if height / pheight < self.globalzoom then
self.globalzoom = height / pheight
self.offset_x = (width - (self.globalzoom * pwidth)) / 2
self.offset_y = 0
end
self.pan_by_page = false
elseif self.globalzoommode == self.ZOOM_FIT_TO_PAGE_WIDTH
or self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_WIDTH then
self.globalzoom = width / pwidth
self.offset_x = 0
self.offset_y = (height - (self.globalzoom * pheight)) / 2
self.pan_by_page = false
elseif self.globalzoommode == self.ZOOM_FIT_TO_PAGE_HEIGHT
or self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_HEIGHT then
self.globalzoom = height / pheight
self.offset_x = (width - (self.globalzoom * pwidth)) / 2
self.offset_y = 0
self.pan_by_page = false
end
if self.globalzoommode == self.ZOOM_FIT_TO_CONTENT then
if (x1 - x0) < pwidth then
self.globalzoom = width / (x1 - x0)
self.offset_x = -1 * x0 * self.globalzoom
self.offset_y = -1 * y0 * self.globalzoom + (height - (self.globalzoom * (y1 - y0))) / 2
if height / (y1 - y0) < self.globalzoom then
self.globalzoom = height / (y1 - y0)
self.offset_x = -1 * x0 * self.globalzoom + (width - (self.globalzoom * (x1 - x0))) / 2
self.offset_y = -1 * y0 * self.globalzoom
end
end
elseif self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_WIDTH then
if (x1 - x0) < pwidth then
self.globalzoom = width / (x1 - x0)
self.offset_x = -1 * x0 * self.globalzoom
self.offset_y = -1 * y0 * self.globalzoom + (height - (self.globalzoom * (y1 - y0))) / 2
end
elseif self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_HEIGHT then
if (y1 - y0) < pheight then
self.globalzoom = height / (y1 - y0)
self.offset_x = -1 * x0 * self.globalzoom + (width - (self.globalzoom * (x1 - x0))) / 2
self.offset_y = -1 * y0 * self.globalzoom
end
elseif self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_HALF_WIDTH
or self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_HALF_WIDTH_MARGIN then
local margin = self.pan_margin
if self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_HALF_WIDTH then margin = 0 end
self.globalzoom = width / (x1 - x0 + margin)
self.offset_x = -1 * x0 * self.globalzoom * 2 + margin
self.globalzoom = height / (y1 - y0)
self.offset_y = -1 * y0 * self.globalzoom * 2 + margin
self.globalzoom = width / (x1 - x0 + margin) * 2
print("column mode offset:"..self.offset_x.."*"..self.offset_y.." zoom:"..self.globalzoom);
self.globalzoommode = self.ZOOM_BY_VALUE -- enable pan mode
self.pan_x = self.offset_x
self.pan_y = self.offset_y
self.pan_by_page = true
end
dc:setZoom(self.globalzoom)
self.globalzoom_orig = self.globalzoom
dc:setRotate(self.globalrotate);
dc:setOffset(self.offset_x, self.offset_y)
self.fullwidth, self.fullheight = page:getSize(dc)
self.min_offset_x = fb.bb:getWidth() - self.fullwidth
self.min_offset_y = fb.bb:getHeight() - self.fullheight
if(self.min_offset_x > 0) then
self.min_offset_x = 0
end
if(self.min_offset_y > 0) then
self.min_offset_y = 0
end
print("# Reader:setzoom globalzoom:"..self.globalzoom.." globalrotate:"..self.globalrotate.." offset:"..self.offset_x.."*"..self.offset_y.." pagesize:"..self.fullwidth.."*"..self.fullheight.." min_offset:"..self.min_offset_x.."*"..self.min_offset_y)
-- set gamma here, we don't have any other good place for this right now:
if self.globalgamma ~= self.GAMMA_NO_GAMMA then
print("gamma correction: "..self.globalgamma)
dc:setGamma(self.globalgamma)
end
return dc
end end
-- render and blit a page -- render and blit a page
@ -166,46 +305,74 @@ function UniReader:show(no)
self.slot_visible = slot; self.slot_visible = slot;
end end
function UniReader:add_jump(pageno) --[[
@ pageno is the page you want to add to jump_stack
--]]
function UniReader:add_jump(pageno, notes)
local jump_item = nil local jump_item = nil
-- add current page to jump_stack if no in local notes_to_add = notes
if not notes_to_add then
-- no notes given, auto generate from TOC entry
notes_to_add = self:getTOCTitleByPage(self.pageno)
if notes_to_add ~= "" then
notes_to_add = "in "..notes_to_add
end
end
-- move pageno page to jump_stack top if already in
for _t,_v in ipairs(self.jump_stack) do for _t,_v in ipairs(self.jump_stack) do
if _v.page == pageno then if _v.page == pageno then
jump_item = _v jump_item = _v
table.remove(self.jump_stack, _t) table.remove(self.jump_stack, _t)
elseif _v.page == no then -- if original notes is not empty, probably defined by users,
-- the page we jumped to should not be show in stack -- we use the original notes to overwrite auto generated notes
table.remove(self.jump_stack, _t) -- from TOC entry
if jump_item.notes ~= "" then
notes_to_add = jump_item.notes
end
jump_item.notes = notes or notes_to_add
break
end end
end end
-- create a new one if not found -- create a new one if page not found in stack
if not jump_item then if not jump_item then
jump_item = { jump_item = {
page = pageno, page = pageno,
datetime = os.date("%Y-%m-%d %H:%M:%S"), datetime = os.date("%Y-%m-%d %H:%M:%S"),
notes = notes_to_add,
} }
end end
-- insert at the start
-- insert item at the start
table.insert(self.jump_stack, 1, jump_item) table.insert(self.jump_stack, 1, jump_item)
if #self.jump_stack > 10 then if #self.jump_stack > 10 then
-- remove the last element to keep the size less than 10 -- remove the last element to keep the size less than 10
table.remove(self.jump_stack) table.remove(self.jump_stack)
end end
end end
function UniReader:del_jump(pageno)
for _t,_v in ipairs(self.jump_stack) do
if _v.page == pageno then
table.remove(self.jump_stack, _t)
end
end
end
-- change current page and cache next page after rendering -- change current page and cache next page after rendering
function UniReader:goto(no) function UniReader:goto(no)
if no < 1 or no > self.doc:getPages() then if no < 1 or no > self.doc:getPages() then
return return
end end
-- for jump_stack -- for jump_stack, distinguish jump from normal page turn
if self.pageno and math.abs(self.pageno - no) > 1 then if self.pageno and math.abs(self.pageno - no) > 1 then
self:add_jump(self.pageno) self:add_jump(self.pageno)
end end
self.pageno = no self.pageno = no
self:show(no) self:show(no)
if no < self.doc:getPages() then if no < self.doc:getPages() then
if self.globalzoommode ~= self.ZOOM_BY_VALUE then if self.globalzoommode ~= self.ZOOM_BY_VALUE then
-- pre-cache next page -- pre-cache next page
@ -245,13 +412,44 @@ function UniReader:setrotate(rotate)
self:goto(self.pageno) self:goto(self.pageno)
end end
function UniReader:cleanUpTOCTitle(title)
return title:gsub("\13", "")
end
function UniReader:fillTOC()
self.toc = self.doc:getTOC()
end
function UniReader:getTOCTitleByPage(pageno)
if not self.toc then
-- build toc when needed.
self:fillTOC()
end
for _k,_v in ipairs(self.toc) do
if _v.page >= pageno then
return self:cleanUpTOCTitle(_v.title)
end
end
return ""
end
function UniReader:showTOC() function UniReader:showTOC()
toc = self.doc:getTOC() if not self.toc then
-- build toc when needed.
self:fillTOC()
end
local menu_items = {} local menu_items = {}
local filtered_toc = {}
local curr_page = -1
-- build menu items -- build menu items
for _k,_v in ipairs(toc) do for _k,_v in ipairs(self.toc) do
table.insert(menu_items, if(_v.page >= curr_page) then
(" "):rep(_v.depth-1).._v.title) table.insert(menu_items,
(" "):rep(_v.depth-1)..self:cleanUpTOCTitle(_v.title))
table.insert(filtered_toc,_v.page)
curr_page = _v.page
end
end end
toc_menu = SelectMenu:new{ toc_menu = SelectMenu:new{
menu_title = "Table of Contents", menu_title = "Table of Contents",
@ -260,7 +458,7 @@ function UniReader:showTOC()
} }
item_no = toc_menu:choose(0, fb.bb:getHeight()) item_no = toc_menu:choose(0, fb.bb:getHeight())
if item_no then if item_no then
self:goto(toc[item_no].page) self:goto(filtered_toc[item_no])
else else
self:goto(self.pageno) self:goto(self.pageno)
end end
@ -270,7 +468,7 @@ function UniReader:showJumpStack()
local menu_items = {} local menu_items = {}
for _k,_v in ipairs(self.jump_stack) do for _k,_v in ipairs(self.jump_stack) do
table.insert(menu_items, table.insert(menu_items,
_v.datetime.." -> Page ".._v.page) _v.datetime.." -> Page ".._v.page.." ".._v.notes)
end end
jump_menu = SelectMenu:new{ jump_menu = SelectMenu:new{
menu_title = "Jump Keeper (current page: "..self.pageno..")", menu_title = "Jump Keeper (current page: "..self.pageno..")",
@ -289,6 +487,7 @@ end
-- wait for input and handle it -- wait for input and handle it
function UniReader:inputloop() function UniReader:inputloop()
local keep_running = true
while 1 do while 1 do
local ev = input.waitForEvent() local ev = input.waitForEvent()
ev.code = adjustKeyEvents(ev) ev.code = adjustKeyEvents(ev)
@ -321,16 +520,7 @@ function UniReader:inputloop()
elseif ev.code == KEY_BACK then elseif ev.code == KEY_BACK then
if Keys.altmode then if Keys.altmode then
-- altmode, exit reader -- altmode, exit reader
self:clearcache() break
if self.doc ~= nil then
self.doc:close()
end
if self.settings ~= nil then
self.settings:savesetting("last_page", self.pageno)
self.settings:savesetting("gamma", self.globalgamma)
self.settings:close()
end
return
else else
-- not altmode, back to last jump -- not altmode, back to last jump
if #self.jump_stack ~= 0 then if #self.jump_stack ~= 0 then
@ -360,7 +550,11 @@ function UniReader:inputloop()
self:setglobalzoommode(self.ZOOM_FIT_TO_PAGE_HEIGHT) self:setglobalzoommode(self.ZOOM_FIT_TO_PAGE_HEIGHT)
end end
elseif ev.code == KEY_F then elseif ev.code == KEY_F then
self:setglobalzoommode(self.ZOOM_FIT_TO_CONTENT_HALF_WIDTH) if Keys.shiftmode then
self:setglobalzoommode(self.ZOOM_FIT_TO_CONTENT_HALF_WIDTH)
else
self:setglobalzoommode(self.ZOOM_FIT_TO_CONTENT_HALF_WIDTH_MARGIN)
end
elseif ev.code == KEY_T then elseif ev.code == KEY_T then
self:showTOC() self:showTOC()
elseif ev.code == KEY_B then elseif ev.code == KEY_B then
@ -373,6 +567,10 @@ function UniReader:inputloop()
self:setrotate( self.globalrotate + 10 ) self:setrotate( self.globalrotate + 10 )
elseif ev.code == KEY_K then elseif ev.code == KEY_K then
self:setrotate( self.globalrotate - 10 ) self:setrotate( self.globalrotate - 10 )
elseif ev.code == KEY_HOME then
-- signal quit
keep_running = false
break
end end
if self.globalzoommode == self.ZOOM_BY_VALUE then if self.globalzoommode == self.ZOOM_BY_VALUE then
@ -387,7 +585,7 @@ function UniReader:inputloop()
y = self.shift_y / 5 y = self.shift_y / 5
elseif self.pan_by_page then elseif self.pan_by_page then
x = width; x = width;
y = height - self.pan_margin; -- overlap for lines which didn't fit y = height - self.pan_overlap_vertical; -- overlap for lines which didn't fit
else else
x = self.shift_x x = self.shift_x
y = self.shift_y y = self.shift_y
@ -398,30 +596,32 @@ function UniReader:inputloop()
local old_offset_y = self.offset_y local old_offset_y = self.offset_y
if ev.code == KEY_FW_LEFT then if ev.code == KEY_FW_LEFT then
print("# KEY_FW_LEFT "..self.offset_x.." + "..x.." > 0");
self.offset_x = self.offset_x + x self.offset_x = self.offset_x + x
if self.offset_x > 0 then if self.pan_by_page then
self.offset_x = 0 if self.offset_x > 0 and self.pageno > 1 then
if self.pan_by_page and self.pageno > 1 then
self.offset_x = self.pan_x self.offset_x = self.pan_x
self.offset_y = self.min_offset_y -- bottom self.offset_y = self.min_offset_y -- bottom
self:goto(self.pageno - 1) self:goto(self.pageno - 1)
else
self.offset_y = self.min_offset_y
end end
end elseif self.offset_x > 0 then
if self.pan_by_page then self.offset_x = 0
self.offset_y = self.min_offset_y
end end
elseif ev.code == KEY_FW_RIGHT then elseif ev.code == KEY_FW_RIGHT then
print("# KEY_FW_RIGHT "..self.offset_x.." - "..x.." < "..self.min_offset_x);
self.offset_x = self.offset_x - x self.offset_x = self.offset_x - x
if self.offset_x < self.min_offset_x then if self.pan_by_page then
self.offset_x = self.min_offset_x if self.offset_x < self.min_offset_x - self.pan_margin and self.pageno < self.doc:getPages() then
if self.pan_by_page and self.pageno < self.doc:getPages() then
self.offset_x = self.pan_x self.offset_x = self.pan_x
self.offset_y = self.pan_y self.offset_y = self.pan_y
self:goto(self.pageno + 1) self:goto(self.pageno + 1)
else
self.offset_y = self.pan_y
end end
end elseif self.offset_x < self.min_offset_x then
if self.pan_by_page then self.offset_x = self.min_offset_x
self.offset_y = self.pan_y
end end
elseif ev.code == KEY_FW_UP then elseif ev.code == KEY_FW_UP then
self.offset_y = self.offset_y + y self.offset_y = self.offset_y + y
@ -461,6 +661,19 @@ function UniReader:inputloop()
print("E: T="..ev.type.." V="..ev.value.." C="..ev.code.." DUR="..dur) print("E: T="..ev.type.." V="..ev.value.." C="..ev.code.." DUR="..dur)
end end
end end
end
-- do clean up stuff
self:clearcache()
self.toc = nil
if self.doc ~= nil then
self.doc:close()
end
if self.settings ~= nil then
self.settings:savesetting("last_page", self.pageno)
self.settings:savesetting("gamma", self.globalgamma)
self.settings:savesetting("jumpstack", self.jump_stack)
self.settings:close()
end
return keep_running
end

Loading…
Cancel
Save