mirror of
https://github.com/koreader/koreader
synced 2024-10-31 21:20:20 +00:00
077517be0b
to support custom font directory for EPUB documents Now Koreader could find fonts in the "fonts" directory in the USB root directory of kindle, Kobo and PocketBook devices, thus no need to copy fonts to "koreader/fonts" directory.
457 lines
14 KiB
Lua
457 lines
14 KiB
Lua
local CreOptions = require("ui/data/creoptions")
|
|
local Document = require("document/document")
|
|
local Configurable = require("configurable")
|
|
local Blitbuffer = require("ffi/blitbuffer")
|
|
local lfs = require("libs/libkoreader-lfs")
|
|
local Geom = require("ui/geometry")
|
|
local Device = require("device")
|
|
local Screen = require("device").screen
|
|
local Font = require("ui/font")
|
|
local DEBUG = require("dbg")
|
|
local ffi = require("ffi")
|
|
|
|
local CreDocument = Document:new{
|
|
-- this is defined in kpvcrlib/crengine/crengine/include/lvdocview.h
|
|
SCROLL_VIEW_MODE = 0,
|
|
PAGE_VIEW_MODE = 1,
|
|
|
|
_document = false,
|
|
_loaded = false,
|
|
engine_initilized = false,
|
|
|
|
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 "Droid Sans Fallback H",
|
|
default_css = "./data/cr3.css",
|
|
options = CreOptions,
|
|
}
|
|
|
|
-- 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
|
|
end
|
|
end
|
|
end
|
|
|
|
function CreDocument:cacheInit()
|
|
-- remove legacy cr3cache directory
|
|
if lfs.attributes("./cr3cache", "mode") == "directory" then
|
|
os.execute("rm -r ./cr3cache")
|
|
end
|
|
cre.initCache("./cache/cr3cache", 1024*1024*32)
|
|
end
|
|
|
|
function CreDocument:engineInit()
|
|
if not engine_initilized then
|
|
-- initialize cache
|
|
self:cacheInit()
|
|
|
|
-- initialize hyph dictionaries
|
|
cre.initHyphDict("./data/hyph/")
|
|
|
|
-- 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)
|
|
if not ok then
|
|
DEBUG(err)
|
|
end
|
|
end
|
|
end
|
|
|
|
engine_initilized = true
|
|
end
|
|
end
|
|
|
|
function CreDocument:init()
|
|
require "libs/libkoreader-cre"
|
|
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"
|
|
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
|
|
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
|
|
end
|
|
|
|
-- adjust font sizes according to screen dpi
|
|
self._document:adjustFontSizes(Screen:getDPI())
|
|
|
|
-- set fallback font face
|
|
self._document:setStringProperty("crengine.font.fallback.face", self.fallback_font)
|
|
|
|
self.is_open = true
|
|
self.info.has_pages = false
|
|
self:_readMetadata()
|
|
self.info.configurable = true
|
|
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()
|
|
-- 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()
|
|
if not self.info.has_pages then
|
|
self.info.doc_height = self._document:getFullHeight()
|
|
end
|
|
end
|
|
|
|
function CreDocument:close()
|
|
Document.close(self)
|
|
end
|
|
|
|
function CreDocument:getPageCount()
|
|
return self._document:getPages()
|
|
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")
|
|
local ok, image = pcall(Mupdf.renderImage, data, size)
|
|
if ok then
|
|
ffi.C.free(data)
|
|
return image
|
|
end
|
|
end
|
|
end
|
|
|
|
function CreDocument:getWordFromPosition(pos)
|
|
local word_box = self._document:getWordFromPosition(pos.x, pos.y)
|
|
DEBUG("CreDocument: get word box", word_box)
|
|
local text_range = self._document:getTextFromPositions(pos.x, pos.y, pos.x, pos.y)
|
|
DEBUG("CreDocument: get text range", text_range)
|
|
local wordbox = {
|
|
word = text_range.text == "" and word_box.word or text_range.text,
|
|
page = self._document:getCurrentPage(),
|
|
}
|
|
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,
|
|
}
|
|
end
|
|
return wordbox
|
|
end
|
|
|
|
function CreDocument:getTextFromPositions(pos0, pos1)
|
|
local text_range = self._document:getTextFromPositions(pos0.x, pos0.y, pos1.x, pos1.y)
|
|
DEBUG("CreDocument: get text range", text_range)
|
|
if text_range then
|
|
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)
|
|
local line_boxes = {}
|
|
if pos0 and pos1 then
|
|
local word_boxes = self._document:getWordBoxesFromPositions(pos0, pos1)
|
|
--DEBUG("word boxes", word_boxes)
|
|
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
|
|
--DEBUG("line boxes", line_boxes)
|
|
end
|
|
return line_boxes
|
|
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)
|
|
self._document:gotoPos(pos)
|
|
self:drawCurrentView(target, x, y, rect)
|
|
end
|
|
|
|
function CreDocument:drawCurrentViewByPage(target, x, y, rect, page)
|
|
self._document:gotoPage(page)
|
|
self:drawCurrentView(target, x, y, rect)
|
|
end
|
|
|
|
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)
|
|
DEBUG("CreDocument: goto xpointer", xpointer)
|
|
self._document:gotoXPointer(xpointer)
|
|
end
|
|
|
|
function CreDocument:getXPointer()
|
|
return self._document:getXPointer()
|
|
end
|
|
|
|
function CreDocument:getPosFromXPointer(xp)
|
|
return self._document:getPosFromXPointer(xp)
|
|
end
|
|
|
|
function CreDocument:getPageFromXPointer(xp)
|
|
return self._document:getPageFromXPointer(xp)
|
|
end
|
|
|
|
function CreDocument:getFontFace()
|
|
return self._document:getFontFace()
|
|
end
|
|
|
|
function CreDocument:getCurrentPos()
|
|
return self._document:getCurrentPos()
|
|
end
|
|
|
|
function CreDocument:getPageLinks()
|
|
return self._document:getPageLinks()
|
|
end
|
|
|
|
function CreDocument:getLinkFromPosition(pos)
|
|
return self._document:getLinkFromPosition(pos.x, pos.y)
|
|
end
|
|
|
|
function Document:gotoPos(pos)
|
|
DEBUG("CreDocument: goto position", pos)
|
|
self._document:gotoPos(pos)
|
|
end
|
|
|
|
function CreDocument:gotoPage(page)
|
|
DEBUG("CreDocument: goto page", page)
|
|
self._document:gotoPage(page)
|
|
end
|
|
|
|
function CreDocument:gotoLink(link)
|
|
DEBUG("CreDocument: goto link", link)
|
|
self._document:gotoLink(link)
|
|
end
|
|
|
|
function CreDocument:goBack()
|
|
DEBUG("CreDocument: go back")
|
|
self._document:goBack()
|
|
end
|
|
|
|
function CreDocument:goForward(link)
|
|
DEBUG("CreDocument: go forward")
|
|
self._document:goForward()
|
|
end
|
|
|
|
function CreDocument:getCurrentPage()
|
|
return self._document:getCurrentPage()
|
|
end
|
|
|
|
function CreDocument:setFontFace(new_font_face)
|
|
if new_font_face then
|
|
DEBUG("CreDocument: set font face", new_font_face)
|
|
self._document:setStringProperty("font.face.default", new_font_face)
|
|
end
|
|
end
|
|
|
|
function CreDocument:setHyphDictionary(new_hyph_dictionary)
|
|
if new_hyph_dictionary then
|
|
DEBUG("CreDocument: set hyphenation dictionary", new_hyph_dictionary)
|
|
self._document:setStringProperty("crengine.hyphenation.directory", new_hyph_dictionary)
|
|
end
|
|
end
|
|
|
|
function CreDocument:clearSelection()
|
|
DEBUG("clear selection")
|
|
self._document:clearSelection()
|
|
end
|
|
|
|
function CreDocument:getFontSize()
|
|
return self._document:getFontSize()
|
|
end
|
|
|
|
function CreDocument:setFontSize(new_font_size)
|
|
if new_font_size then
|
|
DEBUG("CreDocument: set font size", new_font_size)
|
|
self._document:setFontSize(new_font_size)
|
|
end
|
|
end
|
|
|
|
function CreDocument:setViewMode(new_mode)
|
|
if new_mode then
|
|
DEBUG("CreDocument: set view mode", new_mode)
|
|
if new_mode == "scroll" then
|
|
self._document:setViewMode(self.SCROLL_VIEW_MODE)
|
|
else
|
|
self._document:setViewMode(self.PAGE_VIEW_MODE)
|
|
end
|
|
end
|
|
end
|
|
|
|
function CreDocument:setViewDimen(dimen)
|
|
DEBUG("CreDocument: set view dimen", dimen)
|
|
self._document:setViewDimen(dimen.w, dimen.h)
|
|
end
|
|
|
|
function CreDocument:setHeaderFont(new_font)
|
|
if new_font then
|
|
DEBUG("CreDocument: set header font", new_font)
|
|
self._document:setHeaderFont(new_font)
|
|
end
|
|
end
|
|
|
|
function CreDocument:zoomFont(delta)
|
|
DEBUG("CreDocument: zoom font", delta)
|
|
self._document:zoomFont(delta)
|
|
end
|
|
|
|
function CreDocument:setInterlineSpacePercent(percent)
|
|
DEBUG("CreDocument: set interline space", percent)
|
|
self._document:setDefaultInterlineSpace(percent)
|
|
end
|
|
|
|
function CreDocument:toggleFontBolder(toggle)
|
|
DEBUG("CreDocument: toggle font bolder", toggle)
|
|
self._document:setIntProperty("font.face.weight.embolden", toggle)
|
|
end
|
|
|
|
function CreDocument:setGammaIndex(index)
|
|
DEBUG("CreDocument: set gamma index", index)
|
|
cre.setGammaIndex(index)
|
|
end
|
|
|
|
function CreDocument:setStyleSheet(new_css)
|
|
DEBUG("CreDocument: set style sheet", new_css)
|
|
self._document:setStyleSheet(new_css)
|
|
end
|
|
|
|
function CreDocument:setEmbeddedStyleSheet(toggle)
|
|
-- FIXME: occasional segmentation fault when switching embedded style sheet
|
|
DEBUG("CreDocument: set embedded style sheet", toggle)
|
|
self._document:setIntProperty("crengine.doc.embedded.styles.enabled", toggle)
|
|
end
|
|
|
|
function CreDocument:setPageMargins(left, top, right, bottom)
|
|
DEBUG("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)
|
|
end
|
|
|
|
function CreDocument:setFloatingPunctuation(enabled)
|
|
-- FIXME: occasional segmentation fault when toggling floating punctuation
|
|
DEBUG("CreDocument: set floating punctuation", enabled)
|
|
self._document:setIntProperty("crengine.style.floating.punctuation.enabled", enabled)
|
|
end
|
|
|
|
function CreDocument:getVisiblePageCount()
|
|
return self._document:getVisiblePageCount()
|
|
end
|
|
|
|
function CreDocument:setVisiblePageCount(new_count)
|
|
DEBUG("CreDocument: set visible page count", new_count)
|
|
self._document:setVisiblePageCount(new_count)
|
|
end
|
|
|
|
function CreDocument:setBatteryState(state)
|
|
DEBUG("CreDocument: set battery state", state)
|
|
self._document:setBatteryState(state)
|
|
end
|
|
|
|
function CreDocument:isXPointerInCurrentPage(xp)
|
|
DEBUG("CreDocument: check xpointer in current page", xp)
|
|
return self._document:isXPointerInCurrentPage(xp)
|
|
end
|
|
|
|
function CreDocument:setStatusLineProp(prop)
|
|
DEBUG("CreDocument: set status line property", prop)
|
|
self._document:setStringProperty("window.status.line", prop)
|
|
end
|
|
|
|
function CreDocument:findText(pattern, origin, reverse, caseInsensitive)
|
|
DEBUG("CreDocument: find text", pattern, origin, reverse, caseInsensitive)
|
|
return self._document:findText(pattern, origin, reverse, caseInsensitive and 1 or 0)
|
|
end
|
|
|
|
function CreDocument:register(registry)
|
|
registry:addProvider("txt", "application/txt", self)
|
|
registry:addProvider("txt.zip", "application/zip", self)
|
|
registry:addProvider("epub", "application/epub", self)
|
|
registry:addProvider("fb2", "application/fb2", self)
|
|
registry:addProvider("fb2.zip", "application/zip", self)
|
|
registry:addProvider("html", "application/html", self)
|
|
registry:addProvider("html.zip", "application/zip", self)
|
|
registry:addProvider("htm", "application/htm", self)
|
|
registry:addProvider("htm.zip", "application/zip", self)
|
|
registry:addProvider("rtf", "application/rtf", self)
|
|
registry:addProvider("mobi", "application/mobi", self)
|
|
registry:addProvider("prc", "application/prc", self)
|
|
registry:addProvider("azw", "application/azw", self)
|
|
registry:addProvider("chm", "application/chm", self)
|
|
registry:addProvider("pdb", "application/pdb", self)
|
|
registry:addProvider("doc", "application/doc", self)
|
|
registry:addProvider("tcr", "application/tcr", self)
|
|
end
|
|
|
|
return CreDocument
|