Merge remote branch 'origin/master' into txt

* origin/master:
  using font module for getting fonts (commit f95231d789)
  Password input, handling for broken documents
  factored pages count out, better error and password handling
  factored pages counter out of document struct
  make muPDF cache size configurable from Lua
  add XPS and CBZ support (muPDF provides this)
  fixed bug for odd rendering widths
  refactored muPDF integration to use strictly the fitz.h API
  forgot Makefile change when factoring out drawcontext
  fix a Lua 5.0 syntax that was deprecated in 5.1
  separate DC out of pdf.c and djvu.c (cleanup)
  removed obsolete page rotation handling, closes #70
  allow easier customization of toolchain for libdjvu
  fix: remove page number checking in displaying TOC
  fix: handle LPGBCK and LPGFWD in selectmenu
pull/2/merge
traycold 12 years ago
commit 81f99e4921

@ -13,6 +13,7 @@ LFSDIR=luafilesystem
CC:=arm-unknown-linux-gnueabi-gcc
CXX:=arm-unknown-linux-gnueabi-g++
HOST:=arm-unknown-linux-gnueabi
ifdef SBOX_UNAME_MACHINE
CC:=gcc
CXX:=g++
@ -62,12 +63,13 @@ THIRDPARTYLIBS := $(MUPDFLIBDIR)/libfreetype.a \
LUALIB := $(LUADIR)/src/liblua.a
kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o input.o util.o ft.o lfs.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) djvu.o
kpdfview: kpdfview.o einkfb.o pdf.o blitbuffer.o drawcontext.o input.o util.o ft.o lfs.o $(MUPDFLIBS) $(THIRDPARTYLIBS) $(LUALIB) $(DJVULIBS) djvu.o
$(CC) -lm -ldl -lpthread $(EMU_LDFLAGS) -lstdc++ \
kpdfview.o \
einkfb.o \
pdf.o \
blitbuffer.o \
drawcontext.o \
input.o \
util.o \
ft.o \
@ -85,7 +87,7 @@ einkfb.o input.o: %.o: %.c
ft.o: %.o: %.c
$(CC) -c $(KPDFREADER_CFLAGS) -I$(FREETYPEDIR)/include $< -o $@
kpdfview.o pdf.o blitbuffer.o util.o: %.o: %.c
kpdfview.o pdf.o blitbuffer.o util.o drawcontext.o: %.o: %.c
$(CC) -c $(KPDFREADER_CFLAGS) -I$(LFSDIR)/src $< -o $@
djvu.o: %.o: %.c
@ -133,7 +135,7 @@ $(DJVULIBS):
ifdef EMULATE_READER
cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static
else
cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static --host=arm-kindle-linux-gnueabi
cd $(DJVUDIR)/build && ../configure --disable-desktopfiles --disable-shared --enable-static --host=$(HOST)
endif
make -C $(DJVUDIR)/build

@ -366,12 +366,12 @@ static int paintRect(lua_State *L) {
return 0;
}
static const struct luaL_reg blitbuffer_func[] = {
static const struct luaL_Reg blitbuffer_func[] = {
{"new", newBlitBuffer},
{NULL, NULL}
};
static const struct luaL_reg blitbuffer_meth[] = {
static const struct luaL_Reg blitbuffer_meth[] = {
{"getWidth", getWidth},
{"getHeight", getHeight},
{"blitFrom", blitToBuffer},

@ -11,14 +11,14 @@ function Keydef:_new(obj)
setmetatable(obj, self)
self.__index = self
self.__tostring=Keydef.tostring
return obj
return obj
end
function Keydef:new(keycode,modifier,descr)
obj = Keydef:_new()
obj.keycode = keycode
obj.modifier = modifier
obj.descr = descr
return obj
obj.descr = descr
return obj
end
function Keydef:display()
return ((self.modifier and self.modifier.."+") or "")..(self.descr or "")
@ -40,7 +40,7 @@ function Command:_new(obj)
setmetatable(obj, self)
self.__index = self
self.__tostring=Command.tostring
return obj
return obj
end
function Command:new(keydef, func, help, keygroup, order)
obj = Command:_new()
@ -63,35 +63,35 @@ Commands = {
}
function Commands:add(keycode,modifier,keydescr,help,func)
local keydef = Keydef:new(keycode,modifier,keydescr)
self:_add_impl(keydef,help,func)
end
function Commands:add_group(keygroup,keys,help,func)
self:_addImpl(keydef,help,func)
end
function Commands:addGroup(keygroup,keys,help,func)
for _k,keydef in pairs(keys) do
self:_add_impl(keydef,help,func,keygroup)
self:_addImpl(keydef,help,func,keygroup)
end
end
function Commands:_add_impl(keydef,help,func,keygroup)
function Commands:_addImpl(keydef,help,func,keygroup)
if keydef.modifier==MOD_ANY then
self:add_group(keygroup or keydef.descr,{Keydef:new(keydef.keycode,nil), Keydef:new(keydef.keycode,MOD_SHIFT), Keydef:new(keydef.keycode,MOD_ALT)},help,func)
self:addGroup(keygroup or keydef.descr,{Keydef:new(keydef.keycode,nil), Keydef:new(keydef.keycode,MOD_SHIFT), Keydef:new(keydef.keycode,MOD_ALT)},help,func)
elseif keydef.modifier==MOD_SHIFT_OR_ALT then
self:add_group(keygroup or (MOD_SHIFT.."|"..MOD_ALT.."+"..(keydef.descr or "")),{Keydef:new(keydef.keycode,MOD_SHIFT), Keydef:new(keydef.keycode,MOD_ALT)},help,func)
self:addGroup(keygroup or (MOD_SHIFT.."|"..MOD_ALT.."+"..(keydef.descr or "")),{Keydef:new(keydef.keycode,MOD_SHIFT), Keydef:new(keydef.keycode,MOD_ALT)},help,func)
else
local command = self.map[keydef]
if command == nil then
self.size = self.size + 1
command = Command:new(keydef,func,help,keygroup,self.size)
command = Command:new(keydef,func,help,keygroup,self.size)
self.map[keydef] = command
else
command.func = func
command.help = help
command.keygroup = keygroup
end
end
end
end
end
function Commands:get(keycode,modifier)
return self.map[Keydef:new(keycode, modifier)]
end
function Commands:get_by_keydef(keydef)
function Commands:getByKeydef(keydef)
return self.map[keydef]
end
function Commands:new(obj)
@ -100,10 +100,10 @@ function Commands:new(obj)
setmetatable(self.map,mt)
mt.__index=function (table, key)
return rawget(table,(key.modifier or "").."@#@"..(key.keycode or ""))
end
end
mt.__newindex=function (table, key, value)
return rawset(table,(key.modifier or "").."@#@"..(key.keycode or ""),value)
end
end
-- obj definition
obj = obj or {}
setmetatable(obj, self)

114
djvu.c

@ -20,6 +20,7 @@
#include "string.h"
#include "blitbuffer.h"
#include "drawcontext.h"
#include "djvu.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
@ -30,7 +31,6 @@
typedef struct DjvuDocument {
ddjvu_context_t *context;
ddjvu_document_t *doc_ref;
int pages;
} DjvuDocument;
typedef struct DjvuPage {
@ -40,14 +40,6 @@ typedef struct DjvuPage {
DjvuDocument *doc;
} DjvuPage;
typedef struct DrawContext {
int rotate;
double zoom;
double gamma;
int offset_x;
int offset_y;
} DrawContext;
static int handle(lua_State *L, ddjvu_context_t *ctx, int wait)
{
@ -97,7 +89,6 @@ static int openDocument(lua_State *L) {
return luaL_error(L, "cannot open DJVU file <%s>", filename);
}
doc->pages = ddjvu_document_get_pagenum(doc->doc_ref);
return 1;
}
@ -116,7 +107,7 @@ static int closeDocument(lua_State *L) {
static int getNumberOfPages(lua_State *L) {
DjvuDocument *doc = (DjvuDocument*) luaL_checkudata(L, 1, "djvudocument");
lua_pushinteger(L, doc->pages);
lua_pushinteger(L, ddjvu_document_get_pagenum(doc->doc_ref));
return 1;
}
@ -177,84 +168,13 @@ static int getTableOfContent(lua_State *L) {
return 1;
}
static int newDrawContext(lua_State *L) {
int rotate = luaL_optint(L, 1, 0);
double zoom = luaL_optnumber(L, 2, (double) 1.0);
int offset_x = luaL_optint(L, 3, 0);
int offset_y = luaL_optint(L, 4, 0);
double gamma = luaL_optnumber(L, 5, (double) -1.0);
DrawContext *dc = (DrawContext*) lua_newuserdata(L, sizeof(DrawContext));
dc->rotate = rotate;
dc->zoom = zoom;
dc->offset_x = offset_x;
dc->offset_y = offset_y;
dc->gamma = gamma;
luaL_getmetatable(L, "drawcontext");
lua_setmetatable(L, -2);
return 1;
}
static int dcSetOffset(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->offset_x = luaL_checkint(L, 2);
dc->offset_y = luaL_checkint(L, 3);
return 0;
}
static int dcGetOffset(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushinteger(L, dc->offset_x);
lua_pushinteger(L, dc->offset_y);
return 2;
}
static int dcSetRotate(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->rotate = luaL_checkint(L, 2);
return 0;
}
static int dcSetZoom(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->zoom = luaL_checknumber(L, 2);
return 0;
}
static int dcGetRotate(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushinteger(L, dc->rotate);
return 1;
}
static int dcGetZoom(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushnumber(L, dc->zoom);
return 1;
}
static int dcSetGamma(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->gamma = luaL_checknumber(L, 2);
return 0;
}
static int dcGetGamma(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushnumber(L, dc->gamma);
return 1;
}
static int openPage(lua_State *L) {
ddjvu_status_t r;
DjvuDocument *doc = (DjvuDocument*) luaL_checkudata(L, 1, "djvudocument");
int pageno = luaL_checkint(L, 2);
if(pageno < 1 || pageno > doc->pages) {
return luaL_error(L, "cannot open page #%d, out of range (1-%d)", pageno, doc->pages);
if(pageno < 1 || pageno > ddjvu_document_get_pagenum(doc->doc_ref)) {
return luaL_error(L, "cannot open page #%d, out of range (1-%d)", pageno, ddjvu_document_get_pagenum(doc->doc_ref));
}
DjvuPage *page = (DjvuPage*) lua_newuserdata(L, sizeof(DjvuPage));
@ -405,13 +325,12 @@ static int drawPage(lua_State *L) {
return 0;
}
static const struct luaL_reg djvu_func[] = {
static const struct luaL_Reg djvu_func[] = {
{"openDocument", openDocument},
{"newDC", newDrawContext},
{NULL, NULL}
};
static const struct luaL_reg djvudocument_meth[] = {
static const struct luaL_Reg djvudocument_meth[] = {
{"openPage", openPage},
{"getPages", getNumberOfPages},
{"getTOC", getTableOfContent},
@ -420,7 +339,7 @@ static const struct luaL_reg djvudocument_meth[] = {
{NULL, NULL}
};
static const struct luaL_reg djvupage_meth[] = {
static const struct luaL_Reg djvupage_meth[] = {
{"getSize", getPageSize},
{"getUsedBBox", getUsedBBox},
{"close", closePage},
@ -429,18 +348,6 @@ static const struct luaL_reg djvupage_meth[] = {
{NULL, NULL}
};
static const struct luaL_reg drawcontext_meth[] = {
{"setRotate", dcSetRotate},
{"getRotate", dcGetRotate},
{"setZoom", dcSetZoom},
{"getZoom", dcGetZoom},
{"setOffset", dcSetOffset},
{"getOffset", dcGetOffset},
{"setGamma", dcSetGamma},
{"getGamma", dcGetGamma},
{NULL, NULL}
};
int luaopen_djvu(lua_State *L) {
luaL_newmetatable(L, "djvudocument");
lua_pushstring(L, "__index");
@ -456,13 +363,6 @@ int luaopen_djvu(lua_State *L) {
luaL_register(L, NULL, djvupage_meth);
lua_pop(L, 1);
luaL_newmetatable(L, "drawcontext");
lua_pushstring(L, "__index");
lua_pushvalue(L, -2);
lua_settable(L, -3);
luaL_register(L, NULL, drawcontext_meth);
lua_pop(L, 1);
luaL_register(L, "djvu", djvu_func);
return 1;
}

@ -1,19 +1,14 @@
require "unireader"
DJVUReader = UniReader:new{
newDC = function()
print("djvu.newDC")
return djvu.newDC()
end,
}
function DJVUReader:init()
self.nulldc = self.newDC()
end
DJVUReader = UniReader:new{}
-- open a DJVU file and its settings store
-- DJVU does not support password yet
function DJVUReader:open(filename)
self.doc = djvu.openDocument(filename)
return self:loadSettings(filename)
local ok
ok, self.doc = pcall(djvu.openDocument, filename)
if not ok then
return ok, self.doc -- this will be the error message instead
end
return ok
end

@ -0,0 +1,117 @@
/*
KindlePDFViewer: a DC abstraction
Copyright (C) 2012 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 "drawcontext.h"
static int newDrawContext(lua_State *L) {
int rotate = luaL_optint(L, 1, 0);
double zoom = luaL_optnumber(L, 2, (double) 1.0);
int offset_x = luaL_optint(L, 3, 0);
int offset_y = luaL_optint(L, 4, 0);
double gamma = luaL_optnumber(L, 5, (double) -1.0);
DrawContext *dc = (DrawContext*) lua_newuserdata(L, sizeof(DrawContext));
dc->rotate = rotate;
dc->zoom = zoom;
dc->offset_x = offset_x;
dc->offset_y = offset_y;
dc->gamma = gamma;
luaL_getmetatable(L, "drawcontext");
lua_setmetatable(L, -2);
return 1;
}
static int dcSetOffset(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->offset_x = luaL_checkint(L, 2);
dc->offset_y = luaL_checkint(L, 3);
return 0;
}
static int dcGetOffset(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushinteger(L, dc->offset_x);
lua_pushinteger(L, dc->offset_y);
return 2;
}
static int dcSetRotate(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->rotate = luaL_checkint(L, 2);
return 0;
}
static int dcSetZoom(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->zoom = luaL_checknumber(L, 2);
return 0;
}
static int dcGetRotate(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushinteger(L, dc->rotate);
return 1;
}
static int dcGetZoom(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushnumber(L, dc->zoom);
return 1;
}
static int dcSetGamma(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->gamma = luaL_checknumber(L, 2);
return 0;
}
static int dcGetGamma(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushnumber(L, dc->gamma);
return 1;
}
static const struct luaL_Reg drawcontext_meth[] = {
{"setRotate", dcSetRotate},
{"getRotate", dcGetRotate},
{"setZoom", dcSetZoom},
{"getZoom", dcGetZoom},
{"setOffset", dcSetOffset},
{"getOffset", dcGetOffset},
{"setGamma", dcSetGamma},
{"getGamma", dcGetGamma},
{NULL, NULL}
};
static const struct luaL_Reg drawcontext_func[] = {
{"new", newDrawContext},
{NULL, NULL}
};
int luaopen_drawcontext(lua_State *L) {
luaL_newmetatable(L, "drawcontext");
lua_pushstring(L, "__index");
lua_pushvalue(L, -2);
lua_settable(L, -3);
luaL_register(L, NULL, drawcontext_meth);
lua_pop(L, 1);
luaL_register(L, "DrawContext", drawcontext_func);
return 1;
}

@ -0,0 +1,35 @@
/*
KindlePDFViewer: a DC abstraction
Copyright (C) 2012 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/>.
*/
#ifndef _DRAWCONTEXT_H
#define _DRAWCONTEXT_H
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
typedef struct DrawContext {
int rotate;
double zoom;
double gamma;
int offset_x;
int offset_y;
} DrawContext;
int luaopen_drawcontext(lua_State *L);
#endif

@ -203,12 +203,12 @@ static int einkSetOrientation(lua_State *L) {
}
static const struct luaL_reg einkfb_func[] = {
static const struct luaL_Reg einkfb_func[] = {
{"open", openFrameBuffer},
{NULL, NULL}
};
static const struct luaL_reg einkfb_meth[] = {
static const struct luaL_Reg einkfb_meth[] = {
{"close", closeFrameBuffer},
{"__gc", closeFrameBuffer},
{"refresh", einkUpdate},

@ -8,7 +8,7 @@ require "selectmenu"
FileChooser = {
-- Class vars:
-- spacing between lines
spacing = 40,
@ -45,15 +45,18 @@ function getAbsolutePath(aPath)
return abs_path
end
function FileChooser:readdir()
function FileChooser:readDir()
self.dirs = {}
self.files = {}
for f in lfs.dir(self.path) do
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.."'")
table.insert(self.dirs, f)
elseif string.match(f, ".+%.[pP][dD][fF]$") or string.match(f, ".+%.[dD][jJ][vV][uU]$") then
table.insert(self.files, f)
else
local file_type = string.lower(string.match(f, ".+%.([^.]+)") or "")
if file_type == "djvu" or file_type == "pdf" or file_type == "xps" or file_type == "cbz" then
table.insert(self.files, f)
end
end
end
--@TODO make sure .. is sortted to the first item 16.02 2012
@ -64,9 +67,9 @@ end
function FileChooser:setPath(newPath)
local curr_path = self.path
self.path = getAbsolutePath(newPath)
local readdir_ok, exc = pcall(self.readdir,self)
local readdir_ok, exc = pcall(self.readDir,self)
if(not readdir_ok) then
print("readdir error: "..tostring(exc))
print("readDir error: "..tostring(exc))
self.exception_message = exc
return self:setPath(curr_path)
else
@ -132,10 +135,10 @@ function FileChooser:choose(ypos, height)
end
end
renderUtf8Text(fb.bb, 5, ypos + self.spacing * perpage + 42, fface, ffhash,
"Page "..self.page.." of "..(math.floor(self.items / perpage)+1), true)
"Page "..self.page.." of "..(math.floor(self.items / perpage)+1), true)
local msg = self.exception_message and self.exception_message:match("[^%:]+:%d+: (.*)") or "Path: "..self.path
self.exception_message = nil
renderUtf8Text(fb.bb, 5, ypos + self.spacing * (perpage+1) + 27, fface, ffhash, msg, true)
renderUtf8Text(fb.bb, 5, ypos + self.spacing * (perpage+1) + 27, fface, ffhash, msg, true)
markerdirty = true
end
if markerdirty then
@ -178,14 +181,14 @@ function FileChooser:choose(ypos, height)
pagedirty = true
elseif ev.code == KEY_S then -- invoke search input
keywords = InputBox:input(height-100, 100, "Search:")
if keywords then
if keywords then
-- call FileSearcher
--[[
This might looks a little bit dirty for using callback.
But I cannot come up with a better solution for renewing
the height arguemtn according to screen rotation mode.
The callback might also be useful for calling system
The callback might also be useful for calling system
settings menu in the future.
--]]
return nil, function()

@ -21,7 +21,7 @@ FileSearcher = {
oldcurrent = 1,
}
function FileSearcher:readdir()
function FileSearcher:readDir()
self.dirs = {self.path}
self.files = {}
while #self.dirs ~= 0 do
@ -46,7 +46,7 @@ end
function FileSearcher:setPath(newPath)
self.path = newPath
self:readdir()
self:readDir()
self.items = #self.files
--@TODO check none found 19.02 2012
if self.items == 0 then
@ -116,9 +116,9 @@ function FileSearcher:choose(ypos, height, keywords)
end
end
-- if given keywords, set new result according to keywords.
-- if given keywords, set new result according to keywords.
-- Otherwise, display the previous search result.
if keywords then
if keywords then
self:setSearchResult(keywords)
end
@ -139,17 +139,17 @@ function FileSearcher:choose(ypos, height, keywords)
local c
if self.items == 0 then -- nothing found
y = ypos + self.title_H + self.spacing * 2
renderUtf8Text(fb.bb, 20, y, cface, cfhash,
"Sorry, no match found.", true)
renderUtf8Text(fb.bb, 20, y + self.spacing, cface, cfhash,
renderUtf8Text(fb.bb, 20, y, cface, cfhash,
"Sorry, no match found.", true)
renderUtf8Text(fb.bb, 20, y + self.spacing, cface, cfhash,
"Please try a different keyword.", true)
markerdirty = false
else -- found something, draw it
for c = 1, perpage do
local i = (self.page - 1) * perpage + c
local i = (self.page - 1) * perpage + c
if i <= self.items then
y = ypos + self.title_H + (self.spacing * c)
renderUtf8Text(fb.bb, 50, y, cface, cfhash,
renderUtf8Text(fb.bb, 50, y, cface, cfhash,
self.result[i].name, true)
end
end
@ -236,7 +236,7 @@ function FileSearcher:choose(ypos, height, keywords)
file_entry = self.result[perpage*(self.page-1)+self.current]
file_full_path = file_entry.dir .. "/" .. file_entry.name
-- rotation mode might be changed while reading, so
-- rotation mode might be changed while reading, so
-- record height_percent here
local height_percent = height/fb.bb:getHeight()
openFile(file_full_path)

@ -8,7 +8,7 @@ Font = {
ffont = "sans",
-- built in fonts
fonts = {"sans", "cjk", "mono",
fonts = {"sans", "cjk", "mono",
"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique",
"Helvetica", "Helvetica-Oblique", "Helvetica-BoldOblique",
"Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic",},
@ -42,6 +42,5 @@ end
function Font:update()
self.faces = {}
clearglyphcache()
clearGlyphCache()
end

@ -202,7 +202,7 @@ static int doneFace(lua_State *L) {
return 0;
}
static const struct luaL_reg ft_face_meth[] = {
static const struct luaL_Reg ft_face_meth[] = {
{"renderGlyph", renderGlyph},
{"hasKerning", hasKerning},
{"getKerning", getKerning},
@ -212,7 +212,7 @@ static const struct luaL_reg ft_face_meth[] = {
{NULL, NULL}
};
static const struct luaL_reg ft_func[] = {
static const struct luaL_Reg ft_func[] = {
{"newFace", newFace},
{"newBuiltinFace", newBuiltinFace},
{NULL, NULL}

@ -7,21 +7,7 @@ require "selectmenu"
require "commands"
HelpPage = {
-- Class vars:
-- font for displaying keys
fsize = 20,
face = freetype.newBuiltinFace("mono", 20),
fhash = "mono20",
-- font for displaying help messages
hfsize = 20,
hface = freetype.newBuiltinFace("sans", 20),
hfhash = "sans20",
-- font for paging display
ffsize = 15,
fface = freetype.newBuiltinFace("sans", 15),
ffhash = "sans15",
-- Other Class vars:
-- spacing between lines
spacing = 25,
@ -32,6 +18,18 @@ HelpPage = {
page = 1
}
-- Other Class vars:
-- font for displaying keys
HelpPage.fsize = 20
HelpPage.face, HelpPage.fhash = Font:getFaceAndHash(HelpPage.fsize, "mono")
-- font for displaying help messages
HelpPage.hfsize = 20
HelpPage.hface, HelpPage.hfhash = Font:getFaceAndHash(HelpPage.hfsize, "sans")
-- font for paging display
HelpPage.ffsize = 15
HelpPage.fface, HelpPage.ffhash = Font:getFaceAndHash(HelpPage.ffsize, "sans")
function HelpPage:show(ypos, height,commands)
self.commands = {}
self.items = 0
@ -66,9 +64,9 @@ function HelpPage:show(ypos, height,commands)
if i <= self.items then
renderUtf8Text(fb.bb, max_x + 20, ypos + self.spacing*c, self.hface, self.hfhash, self.commands[i].help, true)
end
end
end
renderUtf8Text(fb.bb, 5, height - math.floor(self.ffsize * 0.4), self.fface, self.ffhash,
"Page "..self.page.." of "..math.ceil(self.items / perpage).." - click Back to close this page", true)
"Page "..self.page.." of "..math.ceil(self.items / perpage).." - click Back to close this page", true)
markerdirty = true
end
if pagedirty then

@ -144,7 +144,7 @@ static int waitForInput(lua_State *L) {
#endif
}
static const struct luaL_reg input_func[] = {
static const struct luaL_Reg input_func[] = {
{"open", openInputDevice},
{"closeAll", closeInputDevices},
{"waitForEvent", waitForInput},

@ -97,15 +97,15 @@ EVENT_VALUE_KEY_RELEASE = 0
-- modifiers
MOD_SHIFT = "Shift"
MOD_ALT = "Alt"
MOD_ALT = "Alt"
MOD_SHIFT_OR_ALT = "ShiftAlt"
MOD_ANY = "Any"
function get_modifier()
function getKeyModifier()
return Keys.altmode and MOD_ALT or Keys.shiftmode and MOD_SHIFT
end
function set_k3_keycodes()
function setK3Keycodes()
KEY_AA = 190
KEY_SYM = 126
KEY_HOME = 102
@ -120,7 +120,7 @@ function set_k3_keycodes()
KEY_FW_PRESS = 194
end
function set_emu_keycodes()
function setEmuKeycodes()
KEY_PGFWD = 117
KEY_PGBCK = 112
KEY_HOME = 110 -- home

@ -24,6 +24,7 @@
#include <lauxlib.h>
#include "blitbuffer.h"
#include "drawcontext.h"
#include "pdf.h"
#include "einkfb.h"
#include "input.h"
@ -48,6 +49,7 @@ int main(int argc, char **argv) {
luaL_openlibs(L);
luaopen_blitbuffer(L);
luaopen_drawcontext(L);
luaopen_einkfb(L);
luaopen_pdf(L);
luaopen_djvu(L);

205
pdf.c

@ -16,15 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fitz/fitz.h>
#include <pdf/mupdf.h>
#include "blitbuffer.h"
#include "drawcontext.h"
#include "pdf.h"
typedef struct PdfDocument {
pdf_document *xref;
fz_document *xref;
fz_context *context;
int pages;
} PdfDocument;
typedef struct PdfPage {
@ -32,47 +31,56 @@ typedef struct PdfPage {
#ifdef USE_DISPLAY_LIST
fz_display_list *list;
#endif
pdf_page *page;
fz_page *page;
PdfDocument *doc;
} PdfPage;
typedef struct DrawContext {
int rotate;
double zoom;
double gamma;
int offset_x;
int offset_y;
} DrawContext;
static int openDocument(lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *password = luaL_checkstring(L, 2);
char *filename = strdup(luaL_checkstring(L, 1));
int cachesize = luaL_optint(L, 2, 64 << 20); // 64 MB limit default
PdfDocument *doc = (PdfDocument*) lua_newuserdata(L, sizeof(PdfDocument));
luaL_getmetatable(L, "pdfdocument");
lua_setmetatable(L, -2);
doc->context = fz_new_context(NULL, NULL, 64 << 20); // 64MB limit
doc->context = fz_new_context(NULL, NULL, cachesize);
fz_try(doc->context) {
doc->xref = pdf_open_document(doc->context, filename);
doc->xref = fz_open_document(doc->context, filename);
}
fz_catch(doc->context) {
return luaL_error(L, "cannot open PDF file <%s>", filename);
free(filename);
return luaL_error(L, "cannot open PDF file");
}
if(pdf_needs_password(doc->xref)) {
if (!pdf_authenticate_password(doc->xref, password))
return luaL_error(L, "cannot authenticate");
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);
}
doc->pages = pdf_count_pages(doc->xref);
free(password);
return 1;
}
static int closeDocument(lua_State *L) {
PdfDocument *doc = (PdfDocument*) luaL_checkudata(L, 1, "pdfdocument");
if(doc->xref != NULL) {
pdf_close_document(doc->xref);
fz_close_document(doc->xref);
doc->xref = NULL;
}
if(doc->context != NULL) {
@ -84,7 +92,12 @@ static int closeDocument(lua_State *L) {
static int getNumberOfPages(lua_State *L) {
PdfDocument *doc = (PdfDocument*) luaL_checkudata(L, 1, "pdfdocument");
lua_pushinteger(L, doc->pages);
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;
}
@ -101,16 +114,17 @@ static int walkTableOfContent(lua_State *L, fz_outline* ol, int *count, int dept
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, "title");
lua_pushstring(L, ol->title);
lua_settable(L, -3);
lua_settable(L, -3);
lua_settable(L, -3);
(*count)++;
if (ol->down) {
walkTableOfContent(L, ol->down, count, depth);
@ -132,83 +146,13 @@ static int getTableOfContent(lua_State *L) {
int count = 1;
PdfDocument *doc = (PdfDocument*) luaL_checkudata(L, 1, "pdfdocument");
ol = pdf_load_outline(doc->xref);
ol = fz_load_outline(doc->xref);
lua_newtable(L);
walkTableOfContent(L, ol, &count, 0);
return 1;
}
static int newDrawContext(lua_State *L) {
int rotate = luaL_optint(L, 1, 0);
double zoom = luaL_optnumber(L, 2, (double) 1.0);
int offset_x = luaL_optint(L, 3, 0);
int offset_y = luaL_optint(L, 4, 0);
double gamma = luaL_optnumber(L, 5, (double) -1.0);
DrawContext *dc = (DrawContext*) lua_newuserdata(L, sizeof(DrawContext));
dc->rotate = rotate;
dc->zoom = zoom;
dc->offset_x = offset_x;
dc->offset_y = offset_y;
dc->gamma = gamma;
luaL_getmetatable(L, "drawcontext");
lua_setmetatable(L, -2);
return 1;
}
static int dcSetOffset(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->offset_x = luaL_checkint(L, 2);
dc->offset_y = luaL_checkint(L, 3);
return 0;
}
static int dcGetOffset(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushinteger(L, dc->offset_x);
lua_pushinteger(L, dc->offset_y);
return 2;
}
static int dcSetRotate(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->rotate = luaL_checkint(L, 2);
return 0;
}
static int dcSetZoom(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->zoom = luaL_checknumber(L, 2);
return 0;
}
static int dcGetRotate(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushinteger(L, dc->rotate);
return 1;
}
static int dcGetZoom(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushnumber(L, dc->zoom);
return 1;
}
static int dcSetGamma(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
dc->gamma = luaL_checknumber(L, 2);
return 0;
}
static int dcGetGamma(lua_State *L) {
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 1, "drawcontext");
lua_pushnumber(L, dc->gamma);
return 1;
}
static int openPage(lua_State *L) {
fz_device *dev;
@ -216,38 +160,38 @@ static int openPage(lua_State *L) {
int pageno = luaL_checkint(L, 2);
if(pageno < 1 || pageno > doc->pages) {
return luaL_error(L, "cannot open page #%d, out of range (1-%d)", pageno, doc->pages);
}
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));
PdfPage *page = (PdfPage*) lua_newuserdata(L, sizeof(PdfPage));
luaL_getmetatable(L, "pdfpage");
lua_setmetatable(L, -2);
luaL_getmetatable(L, "pdfpage");
lua_setmetatable(L, -2);
fz_try(doc->context) {
page->page = pdf_load_page(doc->xref, pageno - 1);
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);
}
page->doc = doc;
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");
ctm = fz_translate(0, -page->page->mediabox.y1);
ctm = fz_concat(ctm, fz_scale(dc->zoom, -dc->zoom));
ctm = fz_concat(ctm, fz_rotate(page->page->rotate));
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, page->page->mediabox);
bbox = fz_transform_rect(ctm, bounds);
lua_pushnumber(L, bbox.x1-bbox.x0);
lua_pushnumber(L, bbox.y1-bbox.y0);
@ -263,11 +207,10 @@ static int getUsedBBox(lua_State *L) {
/* returned BBox is in centi-point (n * 0.01 pt) */
ctm = fz_scale(100, 100);
ctm = fz_concat(ctm, fz_rotate(page->page->rotate));
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_run_page(page->doc->xref, page->page, dev, ctm, NULL);
}
fz_always(page->doc->context) {
fz_free_device(dev);
@ -287,7 +230,7 @@ static int getUsedBBox(lua_State *L) {
static int closePage(lua_State *L) {
PdfPage *page = (PdfPage*) luaL_checkudata(L, 1, "pdfpage");
if(page->page != NULL) {
pdf_free_page(page->doc->xref, page->page);
fz_free_page(page->doc->xref, page->page);
page->page = NULL;
}
return 0;
@ -311,7 +254,6 @@ static int drawPage(lua_State *L) {
fz_clear_pixmap_with_value(page->doc->context, pix, 0xff);
ctm = fz_scale(dc->zoom, dc->zoom);
ctm = fz_concat(ctm, fz_rotate(page->page->rotate));
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);
@ -319,13 +261,13 @@ static int drawPage(lua_State *L) {
fz_device *tdev;
fz_try(page->doc->context) {
tdev = fz_new_trace_device(page->doc->context);
pdf_run_page(page->doc->xref, page->page, tdev, ctm, NULL);
fz_run_page(page->doc->xref, page->page, tdev, ctm, NULL);
}
fz_always(page->doc->context) {
fz_free_device(tdev);
}
#endif
pdf_run_page(page->doc->xref, page->page, dev, ctm, NULL);
fz_run_page(page->doc->xref, page->page, dev, ctm, NULL);
fz_free_device(dev);
if(dc->gamma >= 0.0) {
@ -341,7 +283,7 @@ static int drawPage(lua_State *L) {
bbptr[x] = (((pmptr[x*2 + 1] & 0xF0) >> 4) | (pmptr[x*2] & 0xF0)) ^ 0xFF;
}
if(bb->w & 1) {
bbptr[x] = pmptr[x*2] & 0xF0;
bbptr[x] = (pmptr[x*2] & 0xF0) ^ 0xF0;
}
bbptr += bb->pitch;
pmptr += bb->w;
@ -352,13 +294,14 @@ static int drawPage(lua_State *L) {
return 0;
}
static const struct luaL_reg pdf_func[] = {
static const struct luaL_Reg pdf_func[] = {
{"openDocument", openDocument},
{"newDC", newDrawContext},
{NULL, NULL}
};
static const struct luaL_reg pdfdocument_meth[] = {
static const struct luaL_Reg pdfdocument_meth[] = {
{"needsPassword", needsPassword},
{"authenticatePassword", authenticatePassword},
{"openPage", openPage},
{"getPages", getNumberOfPages},
{"getTOC", getTableOfContent},
@ -367,7 +310,7 @@ static const struct luaL_reg pdfdocument_meth[] = {
{NULL, NULL}
};
static const struct luaL_reg pdfpage_meth[] = {
static const struct luaL_Reg pdfpage_meth[] = {
{"getSize", getPageSize},
{"getUsedBBox", getUsedBBox},
{"close", closePage},
@ -376,18 +319,6 @@ static const struct luaL_reg pdfpage_meth[] = {
{NULL, NULL}
};
static const struct luaL_reg drawcontext_meth[] = {
{"setRotate", dcSetRotate},
{"getRotate", dcGetRotate},
{"setZoom", dcSetZoom},
{"getZoom", dcGetZoom},
{"setOffset", dcSetOffset},
{"getOffset", dcGetOffset},
{"setGamma", dcSetGamma},
{"getGamma", dcGetGamma},
{NULL, NULL}
};
int luaopen_pdf(lua_State *L) {
luaL_newmetatable(L, "pdfdocument");
lua_pushstring(L, "__index");
@ -401,12 +332,6 @@ int luaopen_pdf(lua_State *L) {
lua_settable(L, -3);
luaL_register(L, NULL, pdfpage_meth);
lua_pop(L, 1);
luaL_newmetatable(L, "drawcontext");
lua_pushstring(L, "__index");
lua_pushvalue(L, -2);
lua_settable(L, -3);
luaL_register(L, NULL, drawcontext_meth);
lua_pop(L, 1);
luaL_register(L, "pdf", pdf_func);
return 1;
}

@ -1,18 +1,32 @@
require "unireader"
require "inputbox"
PDFReader = UniReader:new{
newDC = function()
print("pdf.newDC")
return pdf.newDC()
end,
}
function PDFReader:init()
self.nulldc = self.newDC();
end
PDFReader = UniReader:new{}
-- open a PDF file and its settings store
function PDFReader:open(filename, password)
self.doc = pdf.openDocument(filename, password or "")
return self:loadSettings(filename)
function PDFReader:open(filename)
-- muPDF manages its own cache, set second parameter
-- to the maximum size you want it to grow
local ok
ok, self.doc = pcall(pdf.openDocument, filename, 64*1024*1024)
if not ok then
return false, self.doc -- will contain error message
end
if self.doc:needsPassword() then
local password = InputBox:input(height-100, 100, "Pass:")
if not password or not self.doc:authenticatePassword(password) then
self.doc:close()
self.doc = nil
return false, "wrong or missing password"
end
-- password wrong or not entered
end
local ok, err = pcall(self.doc.getPages, self.doc)
if not ok then
-- for PDFs, they might trigger errors later when accessing page tree
self.doc:close()
self.doc = nil
return false, "damaged page tree"
end
return true
end

@ -36,22 +36,26 @@ longopts = {
}
function openFile(filename)
local file_type = string.lower(string.match(filename, ".+%.(.+)"))
local file_type = string.lower(string.match(filename, ".+%.([^.]+)"))
local reader = nil
if file_type == "djvu" then
if DJVUReader:open(filename) then
page_num = DJVUReader.settings:readsetting("last_page") or 1
DJVUReader:goto(tonumber(page_num))
reader_settings:savesetting("lastfile", filename)
return DJVUReader:inputloop()
end
elseif file_type == "pdf" then
if PDFReader:open(filename,"") then -- TODO: query for password
page_num = PDFReader.settings:readsetting("last_page") or 1
PDFReader:goto(tonumber(page_num))
reader = DJVUReader
elseif file_type == "pdf" or file_type == "xps" or file_type == "cbz" then
reader = PDFReader
end
if reader then
local ok, err = reader:open(filename)
if ok then
reader:loadSettings(filename)
page_num = reader.settings:readSetting("last_page") or 1
reader:goto(tonumber(page_num))
reader_settings:savesetting("lastfile", filename)
return PDFReader:inputloop()
return reader:inputLoop()
else
-- TODO: error handling
end
end
return true -- on failed attempts, we signal to keep running
end
function showusage()
@ -88,11 +92,11 @@ if optarg["d"] == "k3" then
input.open("/dev/input/event0")
input.open("/dev/input/event1")
input.open("/dev/input/event2")
set_k3_keycodes()
setK3Keycodes()
elseif optarg["d"] == "emu" then
input.open("")
-- SDL key codes
set_emu_keycodes()
setEmuKeycodes()
else
input.open("/dev/input/event0")
input.open("/dev/input/event1")
@ -103,7 +107,7 @@ else
if f then
print("Auto-detected Kindle 3")
input.open("/dev/input/event2")
set_k3_keycodes()
setK3Keycodes()
end
end
@ -119,7 +123,7 @@ origin_rotation_mode = Screen.cur_rotation_mode
-- set up reader's setting: font
reader_settings = DocSettings:open(".reader")
r_cfont = reader_settings:readsetting("cfont")
r_cfont = reader_settings:readSetting("cfont")
if r_cfont ~=nil then
Font.cfont = r_cfont
end
@ -131,7 +135,7 @@ PDFReader:init()
DJVUReader:init()
-- display directory or open file
local patharg = reader_settings:readsetting("lastfile")
local patharg = reader_settings:readSetting("lastfile")
if ARGV[optind] and lfs.attributes(ARGV[optind], "mode") == "directory" then
local running = true
FileChooser:setPath(ARGV[optind])

@ -2,7 +2,7 @@ glyphcache_max_memsize = 256*1024 -- 256kB glyphcache
glyphcache_current_memsize = 0
glyphcache = {}
glyphcache_max_age = 4096
function glyphcacheclaim(size)
function glyphCacheClaim(size)
if(size > glyphcache_max_memsize) then
error("too much memory claimed")
return false
@ -20,12 +20,12 @@ function glyphcacheclaim(size)
glyphcache_current_memsize = glyphcache_current_memsize + size
return true
end
function getglyph(face, facehash, charcode)
local hash = glyphcachehash(facehash, charcode)
function getGlyph(face, facehash, charcode)
local hash = glyphCacheHash(facehash, charcode)
if glyphcache[hash] == nil then
local glyph = face:renderGlyph(charcode)
local size = glyph.bb:getWidth() * glyph.bb:getHeight() / 2 + 32
glyphcacheclaim(size);
glyphCacheClaim(size);
glyphcache[hash] = {
age = glyphcache_max_age,
size = size,
@ -36,10 +36,10 @@ function getglyph(face, facehash, charcode)
end
return glyphcache[hash].g
end
function glyphcachehash(face, charcode)
function glyphCacheHash(face, charcode)
return face..'_'..charcode;
end
function clearglyphcache()
function clearGlyphCache()
glyphcache = {}
end
@ -84,7 +84,7 @@ function renderUtf8Text(buffer, x, y, face, facehash, text, kerning)
for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do
if pen_x < buffer:getWidth() then
local charcode = util.utf8charcode(uchar)
local glyph = getglyph(face, facehash, charcode)
local glyph = getGlyph(face, facehash, charcode)
if kerning and prevcharcode then
local kern = face:getKerning(prevcharcode, charcode)
pen_x = pen_x + kern

@ -195,7 +195,7 @@ function SelectMenu:choose(ypos, height)
prevItem()
elseif ev.code == KEY_FW_DOWN then
nextItem()
elseif ev.code == KEY_PGFWD then
elseif ev.code == KEY_PGFWD or ev.code == KEY_LPGFWD then
if self.page < (self.items / perpage) then
if self.current + self.page*perpage > self.items then
self.current = self.items - self.page*perpage
@ -206,7 +206,7 @@ function SelectMenu:choose(ypos, height)
self.current = self.items - (self.page-1)*perpage
markerdirty = true
end
elseif ev.code == KEY_PGBCK then
elseif ev.code == KEY_PGBCK or ev.code == KEY_LPGBCK then
if self.page > 1 then
self.page = self.page - 1
pagedirty = true

@ -9,7 +9,7 @@ function DocSettings:open(docfile)
return setmetatable(new, { __index = DocSettings})
end
function DocSettings:readsetting(key)
function DocSettings:readSetting(key)
return self.data[key]
end

@ -57,12 +57,10 @@ UniReader = {
-- the document's setting store:
settings = nil,
-- list of available commands:
commands = nil,
commands = 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":
nulldc = nil,
nulldc = DrawContext.new(),
-- tile cache configuration:
cache_max_memsize = 1024*1024*5, -- 5MB tile cache
@ -87,17 +85,15 @@ function UniReader:new(o)
return o
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()
print("empty initialization method!")
end
-- open a file and its settings store
@ -114,20 +110,20 @@ function UniReader:loadSettings(filename)
if self.doc ~= nil then
self.settings = DocSettings:open(filename)
local gamma = self.settings:readsetting("gamma")
local gamma = self.settings:readSetting("gamma")
if gamma then
self.globalgamma = gamma
end
local jumpstack = self.settings:readsetting("jumpstack")
local jumpstack = self.settings:readSetting("jumpstack")
self.jump_stack = jumpstack or {}
local bbox = self.settings:readsetting("bbox")
local bbox = self.settings:readSetting("bbox")
print("# bbox loaded "..dump(bbox))
self.bbox = bbox
self.globalzoom = self.settings:readsetting("globalzoom") or 1.0
self.globalzoommode = self.settings:readsetting("globalzoommode") or -1
self.globalzoom = self.settings:readSetting("globalzoom") or 1.0
self.globalzoommode = self.settings:readSetting("globalzoommode") or -1
return true
end
@ -135,26 +131,26 @@ function UniReader:loadSettings(filename)
end
function UniReader:initGlobalSettings(settings)
local pan_overlap_vertical = settings:readsetting("pan_overlap_vertical")
local pan_overlap_vertical = settings:readSetting("pan_overlap_vertical")
if pan_overlap_vertical then
self.pan_overlap_vertical = pan_overlap_vertical
end
-- initialize commands
self:add_all_commands()
self:addAllCommands()
local cache_max_memsize = settings:readsetting("cache_max_memsize")
local cache_max_memsize = settings:readSetting("cache_max_memsize")
if cache_max_memsize then
self.cache_max_memsize = cache_max_memsize
end
local cache_max_ttl = settings:readsetting("cache_max_ttl")
local cache_max_ttl = settings:readSetting("cache_max_ttl")
if cache_max_ttl then
self.cache_max_ttl = cache_max_ttl
end
end
-- guarantee that we have enough memory in cache
function UniReader:cacheclaim(size)
function UniReader:cacheClaim(size)
if(size > self.cache_max_memsize) then
-- we're not allowed to claim this much at all
error("too much memory claimed")
@ -177,7 +173,7 @@ function UniReader:cacheclaim(size)
return true
end
function UniReader:draworcache(no, preCache)
function UniReader:drawOrCache(no, preCache)
-- our general caching strategy is as follows:
-- #1 goal: we must render the needed area.
-- #2 goal: we render as much of the requested page as we can
@ -185,12 +181,16 @@ function UniReader:draworcache(no, preCache)
-- #4 goal: we render next page, too. (TODO)
-- ideally, this should be factored out and only be called when needed (TODO)
local page = self.doc:openPage(no)
local dc = self:setzoom(page)
local ok, page = pcall(self.doc.openPage, self.doc, no)
if not ok then
-- TODO: error handling
return nil
end
local dc = self:setZoom(page)
-- offset_x_in_page & offset_y_in_page is the offset within zoomed page
-- they are always positive.
-- you can see self.offset_x_& self.offset_y as the offset within
-- they are always positive.
-- you can see self.offset_x_& self.offset_y as the offset within
-- draw space, which includes the page. So it can be negative and positive.
local offset_x_in_page = -self.offset_x
local offset_y_in_page = -self.offset_y
@ -224,7 +224,7 @@ function UniReader:draworcache(no, preCache)
-- okay, we do not have it in cache yet.
-- so render now.
-- start off with the requested area
local tile = { x = offset_x_in_page, y = offset_y_in_page,
local tile = { x = offset_x_in_page, y = offset_y_in_page,
w = width, h = height }
-- can we cache the full page?
local max_cache = self.cache_max_memsize
@ -262,7 +262,7 @@ function UniReader:draworcache(no, preCache)
end
return nil
end
self:cacheclaim(tile.w * tile.h / 2);
self:cacheClaim(tile.w * tile.h / 2);
self.cache[pagehash] = {
x = tile.x,
y = tile.y,
@ -285,14 +285,14 @@ function UniReader:draworcache(no, preCache)
end
-- blank the cache
function UniReader:clearcache()
function UniReader:clearCache()
self.cache = {}
self.cache_current_memsize = 0
end
-- set viewer state according to zoom state
function UniReader:setzoom(page)
local dc = self.newDC()
function UniReader:setZoom(page)
local dc = DrawContext.new()
local pwidth, pheight = page:getSize(self.nulldc)
print("# page::getSize "..pwidth.."*"..pheight);
local x0, y0, x1, y1 = page:getUsedBBox()
@ -312,14 +312,14 @@ function UniReader:setzoom(page)
print("# ORIGINAL page::getUsedBBox "..x0.."*"..y0.." "..x1.."*"..y1);
local bbox = self.bbox[self.pageno] -- exact
local odd_even = self:odd_even(self.pageno)
local oddEven = self:oddEven(self.pageno)
if bbox ~= nil then
print("## bbox from "..self.pageno)
else
bbox = self.bbox[odd_even] -- odd/even
bbox = self.bbox[oddEven] -- odd/even
end
if bbox ~= nil then -- last used up to this page
print("## bbox from "..odd_even)
print("## bbox from "..oddEven)
else
for i = 0,self.pageno do
bbox = self.bbox[ self.pageno - i ]
@ -430,7 +430,7 @@ function UniReader:setzoom(page)
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)
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
@ -442,7 +442,10 @@ end
-- render and blit a page
function UniReader:show(no)
local pagehash, offset_x, offset_y = self:draworcache(no)
local pagehash, offset_x, offset_y = self:drawOrCache(no)
if not pagehash then
return
end
self.pagehash = pagehash
local bb = self.cache[pagehash].bb
local dest_x = 0
@ -451,15 +454,15 @@ function UniReader:show(no)
-- we can't fill the whole output width, center the content
dest_x = (width - (bb:getWidth() - offset_x)) / 2
end
if bb:getHeight() - offset_y < height and
if bb:getHeight() - offset_y < height and
self.globalzoommode ~= self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN then
-- we can't fill the whole output height and not in
-- we can't fill the whole output height and not in
-- ZOOM_FIT_TO_CONTENT_WIDTH_PAN mode, center the content
dest_y = (height - (bb:getHeight() - offset_y)) / 2
elseif self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN and
self.offset_y > 0 then
-- if we are in ZOOM_FIT_TO_CONTENT_WIDTH_PAN mode and turning to
-- the top of the page, we might leave an empty space between the
-- the top of the page, we might leave an empty space between the
-- page top and screen top.
dest_y = self.offset_y
end
@ -485,9 +488,9 @@ end
--[[
@ pageno is the page you want to add to jump_stack
--]]
function UniReader:add_jump(pageno, notes)
function UniReader:addJump(pageno, notes)
local jump_item = nil
local notes_to_add = notes
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)
@ -528,7 +531,7 @@ function UniReader:add_jump(pageno, notes)
end
end
function UniReader:del_jump(pageno)
function UniReader:delJump(pageno)
for _t,_v in ipairs(self.jump_stack) do
if _v.page == pageno then
table.remove(self.jump_stack, _t)
@ -544,7 +547,7 @@ function UniReader:goto(no)
-- for jump_stack, distinguish jump from normal page turn
if self.pageno and math.abs(self.pageno - no) > 1 then
self:add_jump(self.pageno)
self:addJump(self.pageno)
end
self.pageno = no
@ -555,7 +558,7 @@ function UniReader:goto(no)
if no < self.doc:getPages() then
if #self.bbox == 0 or not self.bbox.enabled then
-- pre-cache next page, but if we will modify bbox don't!
self:draworcache(no+1, true)
self:drawOrCache(no+1, true)
end
end
end
@ -591,7 +594,7 @@ function UniReader:prevView()
if self.globalzoommode == self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN then
if self.offset_y >= self.content_top then
-- hit content top, turn to previous page
-- set self.content_top with magic num to signal self:setzoom
-- set self.content_top with magic num to signal self:setZoom
self.content_top = -2012
pageno = pageno - 1
else
@ -612,14 +615,14 @@ function UniReader:prevView()
end
-- adjust global gamma setting
function UniReader:modify_gamma(factor)
print("modify_gamma, gamma="..self.globalgamma.." factor="..factor)
function UniReader:modifyGamma(factor)
print("modifyGamma, gamma="..self.globalgamma.." factor="..factor)
self.globalgamma = self.globalgamma * factor;
self:goto(self.pageno)
end
-- adjust zoom state and trigger re-rendering
function UniReader:setglobalzoommode(newzoommode)
function UniReader:setGlobalZoomMode(newzoommode)
if self.globalzoommode ~= newzoommode then
self.globalzoommode = newzoommode
self:goto(self.pageno)
@ -627,7 +630,7 @@ function UniReader:setglobalzoommode(newzoommode)
end
-- adjust zoom state and trigger re-rendering
function UniReader:setglobalzoom(zoom)
function UniReader:setGlobalZoom(zoom)
if self.globalzoom ~= zoom then
self.globalzoommode = self.ZOOM_BY_VALUE
self.globalzoom = zoom
@ -635,7 +638,7 @@ function UniReader:setglobalzoom(zoom)
end
end
function UniReader:setrotate(rotate)
function UniReader:setRotate(rotate)
self.globalrotate = rotate
self:goto(self.pageno)
end
@ -644,7 +647,7 @@ end
function UniReader:screenRotate(orien)
Screen:screenRotate(orien)
width, height = fb:getSize()
self:clearcache()
self:clearCache()
self:goto(self.pageno)
end
@ -666,7 +669,7 @@ function UniReader:getTOCTitleByPage(pageno)
if #self.toc == 0 then
return ""
end
local pre_entry = self.toc[1]
for _k,_v in ipairs(self.toc) do
if _v.page > pageno then
@ -684,15 +687,11 @@ function UniReader:showTOC()
end
local menu_items = {}
local filtered_toc = {}
local curr_page = -1
-- build menu items
for _k,_v in ipairs(self.toc) do
if(_v.page >= curr_page) then
table.insert(menu_items,
(" "):rep(_v.depth-1)..self:cleanUpTOCTitle(_v.title))
table.insert(filtered_toc,_v.page)
curr_page = _v.page
end
table.insert(menu_items,
(" "):rep(_v.depth-1)..self:cleanUpTOCTitle(_v.title))
table.insert(filtered_toc,_v.page)
end
toc_menu = SelectMenu:new{
menu_title = "Table of Contents",
@ -710,11 +709,11 @@ end
function UniReader:showJumpStack()
local menu_items = {}
for _k,_v in ipairs(self.jump_stack) do
table.insert(menu_items,
table.insert(menu_items,
_v.datetime.." -> Page ".._v.page.." ".._v.notes)
end
jump_menu = SelectMenu:new{
menu_title = "Jump Keeper (current page: "..self.pageno..")",
menu_title = "Jump Keeper (current page: "..self.pageno..")",
item_array = menu_items,
no_item_msg = "No jump history.",
}
@ -744,7 +743,7 @@ function UniReader:showMenu()
" "..cur_section, true)
ypos = ypos + 15
blitbuffer.progressBar(fb.bb, 10, ypos, width-20, 15,
blitbuffer.progressBar(fb.bb, 10, ypos, width-20, 15,
5, 4, load_percent, 8)
fb:refresh(1)
while 1 do
@ -758,8 +757,8 @@ function UniReader:showMenu()
end
end
function UniReader:odd_even(number)
print("## odd_even "..number)
function UniReader:oddEven(number)
print("## oddEven "..number)
if number % 2 == 1 then
return "odd"
else
@ -768,18 +767,18 @@ function UniReader:odd_even(number)
end
-- wait for input and handle it
function UniReader:inputloop()
function UniReader:inputLoop()
local keep_running = true
while 1 do
local ev = input.waitForEvent()
ev.code = adjustKeyEvents(ev)
if ev.type == EV_KEY and ev.value == EVENT_VALUE_KEY_PRESS then
local secs, usecs = util.gettime()
keydef = Keydef:new(ev.code, get_modifier())
keydef = Keydef:new(ev.code, getKeyModifier())
print("key pressed: "..tostring(keydef))
command = self.commands:get_by_keydef(keydef)
command = self.commands:getByKeydef(keydef)
if command ~= nil then
print("command to execute: "..tostring(command))
print("command to execute: "..tostring(command))
ret_code = command.func(self,keydef)
if ret_code == "break" then
break;
@ -787,7 +786,7 @@ function UniReader:inputloop()
else
print("command not found: "..tostring(command))
end
local nsecs, nusecs = util.gettime()
local dur = (nsecs - secs) * 1000000 + nusecs - usecs
print("E: T="..ev.type.." V="..ev.value.." C="..ev.code.." DUR="..dur)
@ -795,7 +794,7 @@ function UniReader:inputloop()
end
-- do clean up stuff
self:clearcache()
self:clearCache()
self.toc = nil
if self.doc ~= nil then
self.doc:close()
@ -814,7 +813,7 @@ function UniReader:inputloop()
end
-- command definitions
function UniReader:add_all_commands()
function UniReader:addAllCommands()
self.commands = Commands:new()
self.commands:add(KEY_PGFWD,nil,">",
"next page",
@ -829,22 +828,22 @@ function UniReader:add_all_commands()
self.commands:add(KEY_PGFWD,MOD_ALT,">",
"zoom in 10%",
function(unireader)
unireader:setglobalzoom(unireader.globalzoom+unireader.globalzoom_orig*0.1)
unireader:setGlobalZoom(unireader.globalzoom+unireader.globalzoom_orig*0.1)
end)
self.commands:add(KEY_PGBCK,MOD_ALT,"<",
"zoom out 10%",
function(unireader)
unireader:setglobalzoom(unireader.globalzoom-unireader.globalzoom_orig*0.1)
end)
unireader:setGlobalZoom(unireader.globalzoom-unireader.globalzoom_orig*0.1)
end)
self.commands:add(KEY_PGFWD,MOD_SHIFT,">",
"zoom in 20%",
function(unireader)
unireader:setglobalzoom(unireader.globalzoom+unireader.globalzoom_orig*0.2)
unireader:setGlobalZoom(unireader.globalzoom+unireader.globalzoom_orig*0.2)
end)
self.commands:add(KEY_PGBCK,MOD_SHIFT,"<",
"zoom out 20%",
function(unireader)
unireader:setglobalzoom(unireader.globalzoom-unireader.globalzoom_orig*0.2)
unireader:setGlobalZoom(unireader.globalzoom-unireader.globalzoom_orig*0.2)
end)
self.commands:add(KEY_BACK,nil,"back",
"back to last jump",
@ -852,7 +851,7 @@ function UniReader:add_all_commands()
if #unireader.jump_stack ~= 0 then
unireader:goto(unireader.jump_stack[1].page)
end
end)
end)
self.commands:add(KEY_BACK,MOD_ALT,"back",
"close document",
function(unireader)
@ -861,63 +860,63 @@ function UniReader:add_all_commands()
self.commands:add(KEY_VPLUS,nil,"vol+",
"increase gamma 25%",
function(unireader)
unireader:modify_gamma( 1.25 )
unireader:modifyGamma( 1.25 )
end)
self.commands:add(KEY_VMINUS,nil,"vol-",
"decrease gamma 25%",
function(unireader)
unireader:modify_gamma( 0.80 )
unireader:modifyGamma( 0.80 )
end)
--numeric key group
local numeric_keydefs = {}
for i=1,10 do numeric_keydefs[i]=Keydef:new(KEY_1+i-1,nil,tostring(i%10)) end
self.commands:add_group("[1..0]",numeric_keydefs,
self.commands:addGroup("[1..0]",numeric_keydefs,
"jump to <key>*10% of document",
function(unireader,keydef)
print('jump to page: '..math.max(math.floor(unireader.doc:getPages()*(keydef.keycode-KEY_1)/9),1)..'/'..unireader.doc:getPages())
unireader:goto(math.max(math.floor(unireader.doc:getPages()*(keydef.keycode-KEY_1)/9),1))
end)
end)
-- end numeric keys
self.commands:add(KEY_A,nil,"A",
"zoom to fit page",
function(unireader)
unireader:setglobalzoommode(unireader.ZOOM_FIT_TO_PAGE)
end)
unireader:setGlobalZoomMode(unireader.ZOOM_FIT_TO_PAGE)
end)
self.commands:add(KEY_A,MOD_SHIFT,"A",
"zoom to fit content",
function(unireader)
unireader:setglobalzoommode(unireader.ZOOM_FIT_TO_CONTENT)
end)
unireader:setGlobalZoomMode(unireader.ZOOM_FIT_TO_CONTENT)
end)
self.commands:add(KEY_S,nil,"S",
"zoom to fit page width",
function(unireader)
unireader:setglobalzoommode(unireader.ZOOM_FIT_TO_PAGE_WIDTH)
unireader:setGlobalZoomMode(unireader.ZOOM_FIT_TO_PAGE_WIDTH)
end)
self.commands:add(KEY_S,MOD_SHIFT,"S",
"zoom to fit content width",
function(unireader)
unireader:setglobalzoommode(unireader.ZOOM_FIT_TO_CONTENT_WIDTH)
end)
unireader:setGlobalZoomMode(unireader.ZOOM_FIT_TO_CONTENT_WIDTH)
end)
self.commands:add(KEY_D,nil,"D",
"zoom to fit page height",
function(unireader)
unireader:setglobalzoommode(unireader.ZOOM_FIT_TO_PAGE_HEIGHT)
unireader:setGlobalZoomMode(unireader.ZOOM_FIT_TO_PAGE_HEIGHT)
end)
self.commands:add(KEY_D,MOD_SHIFT,"D",
"zoom to fit content height",
function(unireader)
unireader:setglobalzoommode(unireader.ZOOM_FIT_TO_CONTENT_HEIGHT)
end)
unireader:setGlobalZoomMode(unireader.ZOOM_FIT_TO_CONTENT_HEIGHT)
end)
self.commands:add(KEY_F,nil,"F",
"zoom to fit margin 2-column mode",
function(unireader)
unireader:setglobalzoommode(unireader.ZOOM_FIT_TO_CONTENT_HALF_WIDTH_MARGIN)
unireader:setGlobalZoomMode(unireader.ZOOM_FIT_TO_CONTENT_HALF_WIDTH_MARGIN)
end)
self.commands:add(KEY_F,MOD_SHIFT,"F",
"zoom to fit content 2-column mode",
function(unireader)
unireader:setglobalzoommode(unireader.ZOOM_FIT_TO_CONTENT_HALF_WIDTH)
end)
unireader:setGlobalZoomMode(unireader.ZOOM_FIT_TO_CONTENT_HALF_WIDTH)
end)
self.commands:add(KEY_G,nil,"G",
"goto page",
function(unireader)
@ -931,13 +930,13 @@ function UniReader:add_all_commands()
end
end
unireader:goto(page)
end)
end)
self.commands:add(KEY_H,nil,"H",
"show help page",
function(unireader)
HelpPage:show(0,height,unireader.commands)
unireader:goto(unireader.pageno)
end)
end)
self.commands:add(KEY_T,nil,"T",
"show table of content",
function(unireader)
@ -951,28 +950,28 @@ function UniReader:add_all_commands()
self.commands:add(KEY_B,MOD_SHIFT,"B",
"add jump",
function(unireader)
unireader:add_jump(unireader.pageno)
end)
unireader:addJump(unireader.pageno)
end)
self.commands:add(KEY_J,nil,"J",
"rotate 10° clockwise",
function(unireader)
unireader:setrotate( unireader.globalrotate + 10 )
unireader:setRotate( unireader.globalrotate + 10 )
end)
self.commands:add(KEY_J,MOD_SHIFT,"J",
"rotate screen 90° clockwise",
function(unireader)
unireader:screenRotate("clockwise")
end)
end)
self.commands:add(KEY_K,nil,"K",
"rotate 10° counterclockwise",
function(unireader)
unireader:setrotate( unireader.globalrotate - 10 )
unireader:setRotate( unireader.globalrotate - 10 )
end)
self.commands:add(KEY_K,MOD_SHIFT,"K",
"rotate screen 90° counterclockwise",
function(unireader)
unireader:screenRotate("anticlockwise")
end)
end)
self.commands:add(KEY_HOME,MOD_SHIFT_OR_ALT,"Home",
"exit application",
function(unireader)
@ -990,10 +989,10 @@ function UniReader:add_all_commands()
bbox.pan_x = unireader.pan_x
bbox.pan_y = unireader.pan_y
unireader.bbox[unireader.pageno] = bbox
unireader.bbox[unireader:odd_even(unireader.pageno)] = bbox
unireader.bbox[unireader:oddEven(unireader.pageno)] = bbox
unireader.bbox.enabled = true
print("# bbox " .. unireader.pageno .. dump(unireader.bbox))
unireader.globalzoommode = unireader.ZOOM_FIT_TO_CONTENT -- use bbox
print("# bbox " .. unireader.pageno .. dump(unireader.bbox))
unireader.globalzoommode = unireader.ZOOM_FIT_TO_CONTENT -- use bbox
end)
self.commands:add(KEY_Z,MOD_SHIFT,"Z",
"reset crop",
@ -1011,11 +1010,11 @@ function UniReader:add_all_commands()
"open menu",
function(unireader)
unireader:showMenu()
unireader:goto(unireader.pageno)
end)
unireader:goto(unireader.pageno)
end)
-- panning
local panning_keys = {Keydef:new(KEY_FW_LEFT,MOD_ANY),Keydef:new(KEY_FW_RIGHT,MOD_ANY),Keydef:new(KEY_FW_UP,MOD_ANY),Keydef:new(KEY_FW_DOWN,MOD_ANY),Keydef:new(KEY_FW_PRESS,MOD_ANY)}
self.commands:add_group("[joypad]",panning_keys,
self.commands:addGroup("[joypad]",panning_keys,
"pan the active view; use Shift or Alt for smaller steps",
function(unireader,keydef)
if keydef.keycode ~= KEY_FW_PRESS then
@ -1100,11 +1099,10 @@ function UniReader:add_all_commands()
if old_offset_x ~= unireader.offset_x
or old_offset_y ~= unireader.offset_y then
unireader:goto(unireader.pageno)
end
end
end
end
end)
-- end panning
-- end panning
--print defined commands
--for k,v in pairs(self.commands.map) do print(v) end
--for k,v in pairs(self.commands.map) do print(v) end
end

@ -46,7 +46,7 @@ static int utf8charcode(lua_State *L) {
return 1;
}
static const struct luaL_reg util_func[] = {
static const struct luaL_Reg util_func[] = {
{"gettime", gettime},
{"utf8charcode", utf8charcode},
{NULL, NULL}

Loading…
Cancel
Save