2
0
mirror of https://github.com/koreader/koreader synced 2024-11-11 19:11:14 +00:00
koreader/frontend/document/credocument.lua

492 lines
16 KiB
Lua
Raw Normal View History

2014-08-14 11:49:42 +00:00
local Blitbuffer = require("ffi/blitbuffer")
local CreOptions = require("ui/data/creoptions")
local DataStorage = require("datastorage")
local Document = require("document/document")
local Font = require("ui/font")
local Geom = require("ui/geometry")
local Screen = require("device").screen
local ffi = require("ffi")
local lfs = require("libs/libkoreader-lfs")
local logger = require("logger")
2012-06-05 07:23:36 +00:00
-- engine can be initialized only once, on first document opened
local engine_initialized = false
2013-10-18 20:38:07 +00:00
local CreDocument = Document:new{
2014-03-13 13:52:43 +00:00
-- this is defined in kpvcrlib/crengine/crengine/include/lvdocview.h
SCROLL_VIEW_MODE = 0,
PAGE_VIEW_MODE = 1,
_document = false,
_loaded = false,
2014-03-13 13:52:43 +00:00
line_space_percent = 100,
default_font = G_reader_settings:readSetting("cre_font") or "Noto Serif",
header_font = G_reader_settings:readSetting("header_font") or "Noto Sans",
fallback_font = G_reader_settings:readSetting("fallback_font") or "Noto Sans CJK SC",
2014-03-13 13:52:43 +00:00
default_css = "./data/cr3.css",
options = CreOptions,
2012-06-05 07:23:36 +00:00
}
-- NuPogodi, 20.05.12: inspect the zipfile content
function CreDocument:zipContentExt(fname)
local std_out = io.popen("unzip ".."-qql \""..fname.."\"")
if std_out then
for line in std_out:lines() do
local size, ext = string.match(line, "%s+(%d+)%s+.+%.([^.]+)")
-- return the extention
if size and ext then return string.lower(ext) end
2014-03-13 13:52:43 +00:00
end
end
2012-06-05 07:23:36 +00:00
end
function CreDocument:cacheInit()
-- remove legacy cr3cache directory
if lfs.attributes("./cr3cache", "mode") == "directory" then
os.execute("rm -r ./cr3cache")
end
cre.initCache(DataStorage:getDataDir() .. "/cache/cr3cache", 1024*1024*32)
end
2012-06-12 06:52:35 +00:00
function CreDocument:engineInit()
if not engine_initialized then
require "libs/libkoreader-cre"
2014-03-13 13:52:43 +00:00
-- initialize cache
self:cacheInit()
-- initialize hyph dictionaries
2014-06-17 13:48:07 +00:00
cre.initHyphDict("./data/hyph/")
2013-03-13 02:43:15 +00:00
2014-03-13 13:52:43 +00:00
-- we need to initialize the CRE font list
local fonts = Font:getFontList()
for _k, _v in ipairs(fonts) do
if not _v:find("/urw/") then
local ok, err = pcall(cre.registerFont, _v)
2014-03-13 13:52:43 +00:00
if not ok then
logger.err("failed to register crengine font", err)
2014-03-13 13:52:43 +00:00
end
end
end
2012-06-12 06:52:35 +00:00
engine_initialized = true
2014-03-13 13:52:43 +00:00
end
2012-06-12 06:52:35 +00:00
end
2012-06-05 07:23:36 +00:00
2012-06-12 06:52:35 +00:00
function CreDocument:init()
2014-03-13 13:52:43 +00:00
self:engineInit()
self.configurable:loadDefaults(self.options)
local file_type = string.lower(string.match(self.file, ".+%.([^.]+)"))
if file_type == "zip" then
-- NuPogodi, 20.05.12: read the content of zip-file
-- and return extention of the 1st file
file_type = self:zipContentExt(self.file) or "unknown"
2014-03-13 13:52:43 +00:00
end
-- these two format use the same css file
if file_type == "html" then
file_type = "htm"
end
-- if native css-file doesn't exist, one needs to use default cr3.css
if not io.open("./data/"..file_type..".css") then
file_type = "cr3"
end
self.default_css = "./data/"..file_type..".css"
-- @TODO check the default view_mode to a global user configurable
-- variable 22.12 2012 (houqp)
local ok
2014-03-13 13:52:43 +00:00
ok, self._document = pcall(cre.newDocView,
Screen:getWidth(), Screen:getHeight(), self.PAGE_VIEW_MODE
)
if not ok then
error(self._document) -- will contain error message
2014-03-13 13:52:43 +00:00
end
2014-03-13 13:52:43 +00:00
-- adjust font sizes according to screen dpi
self._document:adjustFontSizes(Screen:getDPI())
2014-03-13 13:52:43 +00:00
-- set fallback font face
self._document:setStringProperty("crengine.font.fallback.face", self.fallback_font)
2014-03-13 13:52:43 +00:00
self.is_open = true
self.info.has_pages = false
self:_readMetadata()
self.info.configurable = true
2012-06-05 07:23:36 +00:00
end
function CreDocument:loadDocument()
if not self._loaded then
self._document:loadDocument(self.file)
self._loaded = true
end
end
function CreDocument:render()
-- load document before rendering
self:loadDocument()
2014-10-07 06:05:34 +00:00
-- set visible page count in landscape
if math.max(Screen:getWidth(), Screen:getHeight()) / Screen:getDPI()
< DCREREADER_TWO_PAGE_THRESHOLD then
self:setVisiblePageCount(1)
end
self._document:renderDocument()
2014-03-13 13:52:43 +00:00
if not self.info.has_pages then
self.info.doc_height = self._document:getFullHeight()
end
end
function CreDocument:close()
2014-03-13 13:52:43 +00:00
Document.close(self)
end
2013-10-17 15:53:29 +00:00
function CreDocument:getPageCount()
2014-03-13 13:52:43 +00:00
return self._document:getPages()
2013-10-17 15:53:29 +00:00
end
function CreDocument:getCoverPageImage()
-- don't need to render document in order to get cover image
self:loadDocument()
local data, size = self._document:getCoverPageImageData()
if data and size then
local Mupdf = require("ffi/mupdf")
2014-10-23 01:01:10 +00:00
local ok, image = pcall(Mupdf.renderImage, data, size)
ffi.C.free(data)
if ok then
return image
end
end
end
function CreDocument:getImageFromPosition(pos)
local data, size = self._document:getImageDataFromPosition(pos.x, pos.y)
if data and size then
logger.dbg("CreDocument: got image data from position", data, size)
local Mupdf = require("ffi/mupdf")
-- wrapped with pcall so we always free(data)
local ok, image = pcall(Mupdf.renderImage, data, size)
logger.dbg("Mupdf.renderImage", ok, image)
if not ok and string.find(image, "could not load image data: unknown image file format") then
-- in that case, mupdf seems to have already freed data (see mupdf/source/fitz/image.c:494),
-- as doing outselves ffi.C.free(data) would result in a crash with :
-- *** Error in `./luajit': double free or corruption (!prev): 0x0000000000e48a40 ***
logger.warn("Mupdf says 'unknown image file format', assuming mupdf has already freed image data")
else
ffi.C.free(data) -- need that explicite clean
end
2014-10-23 01:01:10 +00:00
if ok then
return image
end
end
end
2013-12-24 17:42:57 +00:00
function CreDocument:getWordFromPosition(pos)
2014-03-13 13:52:43 +00:00
local word_box = self._document:getWordFromPosition(pos.x, pos.y)
logger.dbg("CreDocument: get word box", word_box)
2014-03-13 13:52:43 +00:00
local text_range = self._document:getTextFromPositions(pos.x, pos.y, pos.x, pos.y)
logger.dbg("CreDocument: get text range", text_range)
local wordbox = {
word = text_range.text == "" and word_box.word or text_range.text,
page = self._document:getCurrentPage(),
}
2014-03-13 13:52:43 +00:00
if word_box.word then
wordbox.sbox = Geom:new{
x = word_box.x0, y = word_box.y0,
w = word_box.x1 - word_box.x0,
h = word_box.y1 - word_box.y0,
}
else
-- dummy word box
wordbox.sbox = Geom:new{
x = pos.x, y = pos.y,
w = 20, h = 20,
2014-03-13 13:52:43 +00:00
}
end
return wordbox
2013-12-24 17:42:57 +00:00
end
function CreDocument:getTextFromPositions(pos0, pos1)
2014-03-13 13:52:43 +00:00
local text_range = self._document:getTextFromPositions(pos0.x, pos0.y, pos1.x, pos1.y)
logger.dbg("CreDocument: get text range", text_range)
if text_range then
2016-02-15 09:33:48 +00:00
-- local line_boxes = self:getScreenBoxesFromPositions(text_range.pos0, text_range.pos1)
return {
text = text_range.text,
pos0 = text_range.pos0,
pos1 = text_range.pos1,
--sboxes = line_boxes, -- boxes on screen
}
end
end
function CreDocument:getScreenBoxesFromPositions(pos0, pos1)
2014-03-13 13:52:43 +00:00
local line_boxes = {}
if pos0 and pos1 then
local word_boxes = self._document:getWordBoxesFromPositions(pos0, pos1)
for i = 1, #word_boxes do
local line_box = word_boxes[i]
table.insert(line_boxes, Geom:new{
x = line_box.x0, y = line_box.y0,
w = line_box.x1 - line_box.x0,
h = line_box.y1 - line_box.y0,
})
end
end
return line_boxes
2013-12-24 17:42:57 +00:00
end
function CreDocument:drawCurrentView(target, x, y, rect, pos)
if self.buffer and (self.buffer.w ~= rect.w or self.buffer.h ~= rect.h) then
self.buffer:free()
self.buffer = nil
end
if not self.buffer then
self.buffer = Blitbuffer.new(rect.w, rect.h)
end
self._document:drawCurrentPage(self.buffer)
target:blitFrom(self.buffer, x, y, 0, 0, rect.w, rect.h)
end
function CreDocument:drawCurrentViewByPos(target, x, y, rect, pos)
2014-03-13 13:52:43 +00:00
self._document:gotoPos(pos)
self:drawCurrentView(target, x, y, rect)
end
function CreDocument:drawCurrentViewByPage(target, x, y, rect, page)
2014-03-13 13:52:43 +00:00
self._document:gotoPage(page)
self:drawCurrentView(target, x, y, rect)
end
2012-06-05 07:23:36 +00:00
function CreDocument:hintPage(pageno, zoom, rotation)
end
function CreDocument:drawPage(target, x, y, rect, pageno, zoom, rotation)
end
function CreDocument:renderPage(pageno, rect, zoom, rotation)
end
function CreDocument:gotoXPointer(xpointer)
logger.dbg("CreDocument: goto xpointer", xpointer)
2014-03-13 13:52:43 +00:00
self._document:gotoXPointer(xpointer)
end
function CreDocument:getXPointer()
2014-03-13 13:52:43 +00:00
return self._document:getXPointer()
end
function CreDocument:isXPointerInDocument(xp)
return self._document:isXPointerInDocument(xp)
end
function CreDocument:getPosFromXPointer(xp)
2014-03-13 13:52:43 +00:00
return self._document:getPosFromXPointer(xp)
end
function CreDocument:getPageFromXPointer(xp)
2014-03-13 13:52:43 +00:00
return self._document:getPageFromXPointer(xp)
end
function CreDocument:getFontFace()
2014-03-13 13:52:43 +00:00
return self._document:getFontFace()
end
function CreDocument:getCurrentPos()
2014-03-13 13:52:43 +00:00
return self._document:getCurrentPos()
end
2014-01-15 15:34:37 +00:00
function CreDocument:getPageLinks()
2014-03-13 13:52:43 +00:00
return self._document:getPageLinks()
2014-01-15 15:34:37 +00:00
end
2014-01-20 12:41:01 +00:00
function CreDocument:getLinkFromPosition(pos)
2014-03-13 13:52:43 +00:00
return self._document:getLinkFromPosition(pos.x, pos.y)
2014-01-20 12:41:01 +00:00
end
function Document:gotoPos(pos)
logger.dbg("CreDocument: goto position", pos)
2014-03-13 13:52:43 +00:00
self._document:gotoPos(pos)
end
function CreDocument:gotoPage(page)
logger.dbg("CreDocument: goto page", page)
2014-03-13 13:52:43 +00:00
self._document:gotoPage(page)
end
2014-01-15 15:34:37 +00:00
function CreDocument:gotoLink(link)
logger.dbg("CreDocument: goto link", link)
2014-03-13 13:52:43 +00:00
self._document:gotoLink(link)
2014-01-15 15:34:37 +00:00
end
function CreDocument:goBack()
logger.dbg("CreDocument: go back")
2014-03-13 13:52:43 +00:00
self._document:goBack()
2014-01-15 15:34:37 +00:00
end
function CreDocument:goForward(link)
logger.dbg("CreDocument: go forward")
2014-03-13 13:52:43 +00:00
self._document:goForward()
2014-01-15 15:34:37 +00:00
end
function CreDocument:getCurrentPage()
2014-03-13 13:52:43 +00:00
return self._document:getCurrentPage()
end
function CreDocument:setFontFace(new_font_face)
2014-03-13 13:52:43 +00:00
if new_font_face then
logger.dbg("CreDocument: set font face", new_font_face)
self._document:setStringProperty("font.face.default", new_font_face)
2014-03-13 13:52:43 +00:00
end
2012-06-12 09:42:02 +00:00
end
function CreDocument:setHyphDictionary(new_hyph_dictionary)
if new_hyph_dictionary then
logger.dbg("CreDocument: set hyphenation dictionary", new_hyph_dictionary)
self._document:setStringProperty("crengine.hyphenation.directory", new_hyph_dictionary)
end
end
function CreDocument:clearSelection()
logger.dbg("clear selection")
2014-03-13 13:52:43 +00:00
self._document:clearSelection()
end
function CreDocument:getFontSize()
2014-03-13 13:52:43 +00:00
return self._document:getFontSize()
end
2012-12-17 06:50:50 +00:00
function CreDocument:setFontSize(new_font_size)
2014-03-13 13:52:43 +00:00
if new_font_size then
logger.dbg("CreDocument: set font size", new_font_size)
2014-03-13 13:52:43 +00:00
self._document:setFontSize(new_font_size)
end
2012-12-17 06:50:50 +00:00
end
2013-01-07 11:53:35 +00:00
function CreDocument:setViewMode(new_mode)
2014-03-13 13:52:43 +00:00
if new_mode then
logger.dbg("CreDocument: set view mode", new_mode)
2014-03-13 13:52:43 +00:00
if new_mode == "scroll" then
self._document:setViewMode(self.SCROLL_VIEW_MODE)
else
self._document:setViewMode(self.PAGE_VIEW_MODE)
end
end
2013-01-07 11:53:35 +00:00
end
function CreDocument:setViewDimen(dimen)
logger.dbg("CreDocument: set view dimen", dimen)
self._document:setViewDimen(dimen.w, dimen.h)
end
2013-01-13 03:23:30 +00:00
function CreDocument:setHeaderFont(new_font)
2014-03-13 13:52:43 +00:00
if new_font then
logger.dbg("CreDocument: set header font", new_font)
2014-03-13 13:52:43 +00:00
self._document:setHeaderFont(new_font)
end
2013-01-13 03:23:30 +00:00
end
function CreDocument:zoomFont(delta)
logger.dbg("CreDocument: zoom font", delta)
2014-03-13 13:52:43 +00:00
self._document:zoomFont(delta)
end
function CreDocument:setInterlineSpacePercent(percent)
logger.dbg("CreDocument: set interline space", percent)
2014-03-13 13:52:43 +00:00
self._document:setDefaultInterlineSpace(percent)
end
function CreDocument:toggleFontBolder(toggle)
logger.dbg("CreDocument: toggle font bolder", toggle)
self._document:setIntProperty("font.face.weight.embolden", toggle)
2013-01-07 17:16:17 +00:00
end
function CreDocument:setGammaIndex(index)
logger.dbg("CreDocument: set gamma index", index)
2014-03-13 13:52:43 +00:00
cre.setGammaIndex(index)
end
function CreDocument:setStyleSheet(new_css)
logger.dbg("CreDocument: set style sheet", new_css)
2014-03-13 13:52:43 +00:00
self._document:setStyleSheet(new_css)
end
function CreDocument:setEmbeddedStyleSheet(toggle)
-- FIXME: occasional segmentation fault when switching embedded style sheet
logger.dbg("CreDocument: set embedded style sheet", toggle)
self._document:setIntProperty("crengine.doc.embedded.styles.enabled", toggle)
end
2013-06-28 09:12:04 +00:00
function CreDocument:setPageMargins(left, top, right, bottom)
logger.dbg("CreDocument: set page margins", left, top, right, bottom)
self._document:setIntProperty("crengine.page.margin.left", left)
self._document:setIntProperty("crengine.page.margin.top", top)
self._document:setIntProperty("crengine.page.margin.right", right)
self._document:setIntProperty("crengine.page.margin.bottom", bottom)
2013-06-28 09:12:04 +00:00
end
function CreDocument:setFloatingPunctuation(enabled)
-- FIXME: occasional segmentation fault when toggling floating punctuation
logger.dbg("CreDocument: set floating punctuation", enabled)
2014-03-13 13:52:43 +00:00
self._document:setIntProperty("crengine.style.floating.punctuation.enabled", enabled)
end
function CreDocument:setTxtPreFormatted(enabled)
logger.dbg("CreDocument: set txt preformatted", enabled)
self._document:setIntProperty("crengine.file.txt.preformatted", enabled)
end
function CreDocument:getVisiblePageCount()
return self._document:getVisiblePageCount()
end
function CreDocument:setVisiblePageCount(new_count)
logger.dbg("CreDocument: set visible page count", new_count)
2014-03-13 13:52:43 +00:00
self._document:setVisiblePageCount(new_count)
end
function CreDocument:setBatteryState(state)
logger.dbg("CreDocument: set battery state", state)
2014-03-13 13:52:43 +00:00
self._document:setBatteryState(state)
end
function CreDocument:isXPointerInCurrentPage(xp)
logger.dbg("CreDocument: check xpointer in current page", xp)
2014-03-13 13:52:43 +00:00
return self._document:isXPointerInCurrentPage(xp)
end
function CreDocument:setStatusLineProp(prop)
logger.dbg("CreDocument: set status line property", prop)
2014-03-13 13:52:43 +00:00
self._document:setStringProperty("window.status.line", prop)
end
2014-11-05 04:28:11 +00:00
function CreDocument:findText(pattern, origin, reverse, caseInsensitive)
logger.dbg("CreDocument: find text", pattern, origin, reverse, caseInsensitive)
2016-04-19 06:50:36 +00:00
return self._document:findText(
pattern, origin, reverse, caseInsensitive and 1 or 0)
2014-11-05 04:28:11 +00:00
end
2013-10-18 20:38:07 +00:00
function CreDocument:register(registry)
registry:addProvider("azw", "application/azw", self)
2014-03-13 13:52:43 +00:00
registry:addProvider("epub", "application/epub", self)
registry:addProvider("chm", "application/chm", self)
registry:addProvider("doc", "application/doc", self)
2014-03-13 13:52:43 +00:00
registry:addProvider("fb2", "application/fb2", self)
2014-11-04 18:11:29 +00:00
registry:addProvider("fb2.zip", "application/zip", self)
2014-03-13 13:52:43 +00:00
registry:addProvider("html", "application/html", self)
registry:addProvider("html.zip", "application/zip", self)
2014-03-13 13:52:43 +00:00
registry:addProvider("htm", "application/htm", self)
registry:addProvider("htm.zip", "application/zip", self)
registry:addProvider("log", "text/plain", self)
registry:addProvider("log.zip", "application/zip", self)
registry:addProvider("md", "text/plain", self)
registry:addProvider("md.zip", "application/zip", self)
2014-03-13 13:52:43 +00:00
registry:addProvider("mobi", "application/mobi", self)
registry:addProvider("pdb", "application/pdb", self)
registry:addProvider("prc", "application/prc", self)
2014-03-13 13:52:43 +00:00
registry:addProvider("tcr", "application/tcr", self)
registry:addProvider("txt", "text/plain", self)
registry:addProvider("txt.zip", "application/zip", self)
registry:addProvider("rtf", "application/rtf", self)
2013-10-18 20:38:07 +00:00
end
return CreDocument