this should implement feature request of zoom mode for multi-columns page in #501 This PR depends on koreader/koreader-base#435 How to use? 1. Tap the top left corner of a PDF/Djvu page to get into the flipping mode 2. Double-tap on text block will zoom in to that column 3. Double-tap on any area will zoom out to an overview of the page 4. repeat step 2 to focus to another page block How does it work? 1. We first find the mask of text blocks in the page. (Pic 1) 2. Then we intersect page boxes with user tap to form a page block. (Pic 2) 3. Finally we zoom the page to the page block and center current view to that block. (Pic 3)
227 lines
7.1 KiB
Lua
227 lines
7.1 KiB
Lua
local Cache = require("cache")
|
|
local CacheItem = require("cacheitem")
|
|
local KoptOptions = require("ui/data/koptoptions")
|
|
local Document = require("document/document")
|
|
local DrawContext = require("ffi/drawcontext")
|
|
local DEBUG = require("dbg")
|
|
local util = require("util")
|
|
|
|
local PdfDocument = Document:new{
|
|
_document = false,
|
|
is_pdf = true,
|
|
dc_null = DrawContext.new(),
|
|
options = KoptOptions,
|
|
koptinterface = nil,
|
|
}
|
|
|
|
function PdfDocument:init()
|
|
local pdf = require("ffi/mupdf")
|
|
self.koptinterface = require("document/koptinterface")
|
|
self.configurable:loadDefaults(self.options)
|
|
local ok
|
|
ok, self._document = pcall(pdf.openDocument, self.file)
|
|
if not ok then
|
|
error(self._document) -- will contain error message
|
|
end
|
|
self.is_open = true
|
|
self.info.has_pages = true
|
|
self.info.configurable = true
|
|
if self._document:needsPassword() then
|
|
self.is_locked = true
|
|
else
|
|
self:_readMetadata()
|
|
end
|
|
-- TODO: handle this
|
|
-- if not (self.info.number_of_pages > 0) then
|
|
--error("No page found in PDF file")
|
|
-- end
|
|
end
|
|
|
|
function PdfDocument:unlock(password)
|
|
if not self._document:authenticatePassword(password) then
|
|
return false
|
|
end
|
|
self.is_locked = false
|
|
self:_readMetadata()
|
|
return true
|
|
end
|
|
|
|
function PdfDocument:getPageTextBoxes(pageno)
|
|
local page = self._document:openPage(pageno)
|
|
local text = page:getPageText()
|
|
page:close()
|
|
return text
|
|
end
|
|
|
|
function PdfDocument:getWordFromPosition(spos)
|
|
return self.koptinterface:getWordFromPosition(self, spos)
|
|
end
|
|
|
|
function PdfDocument:getTextFromPositions(spos0, spos1)
|
|
return self.koptinterface:getTextFromPositions(self, spos0, spos1)
|
|
end
|
|
|
|
function PdfDocument:getPageBoxesFromPositions(pageno, ppos0, ppos1)
|
|
return self.koptinterface:getPageBoxesFromPositions(self, pageno, ppos0, ppos1)
|
|
end
|
|
|
|
function PdfDocument:nativeToPageRectTransform(pageno, rect)
|
|
return self.koptinterface:nativeToPageRectTransform(self, pageno, rect)
|
|
end
|
|
|
|
function PdfDocument:getOCRWord(pageno, wbox)
|
|
return self.koptinterface:getOCRWord(self, pageno, wbox)
|
|
end
|
|
|
|
function PdfDocument:getOCRText(pageno, tboxes)
|
|
return self.koptinterface:getOCRText(self, pageno, tboxes)
|
|
end
|
|
|
|
function PdfDocument:getPageBlock(pageno, x, y)
|
|
return self.koptinterface:getPageBlock(self, pageno, x, y)
|
|
end
|
|
|
|
function PdfDocument:getUsedBBox(pageno)
|
|
local hash = "pgubbox|"..self.file.."|"..pageno
|
|
local cached = Cache:check(hash)
|
|
if cached then
|
|
return cached.ubbox
|
|
end
|
|
local page = self._document:openPage(pageno)
|
|
local used = {}
|
|
used.x0, used.y0, used.x1, used.y1 = page:getUsedBBox()
|
|
local pwidth, pheight = page:getSize(self.dc_null)
|
|
-- clamp to page BBox
|
|
if used.x0 < 0 then used.x0 = 0 end
|
|
if used.x1 > pwidth then used.x1 = pwidth end
|
|
if used.y0 < 0 then used.y0 = 0 end
|
|
if used.y1 > pheight then used.y1 = pheight end
|
|
--@TODO give size for cacheitem? 02.12 2012 (houqp)
|
|
Cache:insert(hash, CacheItem:new{
|
|
ubbox = used,
|
|
})
|
|
page:close()
|
|
return used
|
|
end
|
|
|
|
function PdfDocument:getPageLinks(pageno)
|
|
local hash = "pglinks|"..self.file.."|"..pageno
|
|
local cached = Cache:check(hash)
|
|
if cached then
|
|
return cached.links
|
|
end
|
|
local page = self._document:openPage(pageno)
|
|
local links = page:getPageLinks()
|
|
Cache:insert(hash, CacheItem:new{
|
|
links = links,
|
|
})
|
|
page:close()
|
|
return links
|
|
end
|
|
|
|
function PdfDocument:saveHighlight(pageno, item)
|
|
self.is_edited = true
|
|
local ffi = require("ffi")
|
|
-- will also need mupdf_h.lua to be evaluated once
|
|
-- but this is guaranteed at this point
|
|
local n = #item.pboxes
|
|
local quadpoints = ffi.new("fz_point[?]", 4*n)
|
|
for i=1, n do
|
|
quadpoints[4*i-4].x = item.pboxes[i].x
|
|
quadpoints[4*i-4].y = item.pboxes[i].y + item.pboxes[i].h
|
|
quadpoints[4*i-3].x = item.pboxes[i].x + item.pboxes[i].w
|
|
quadpoints[4*i-3].y = item.pboxes[i].y + item.pboxes[i].h
|
|
quadpoints[4*i-2].x = item.pboxes[i].x + item.pboxes[i].w
|
|
quadpoints[4*i-2].y = item.pboxes[i].y
|
|
quadpoints[4*i-1].x = item.pboxes[i].x
|
|
quadpoints[4*i-1].y = item.pboxes[i].y
|
|
end
|
|
local page = self._document:openPage(pageno)
|
|
local annot_type = ffi.C.FZ_ANNOT_HIGHLIGHT
|
|
if item.drawer == "lighten" then
|
|
annot_type = ffi.C.FZ_ANNOT_HIGHLIGHT
|
|
elseif item.drawer == "underscore" then
|
|
annot_type = ffi.C.FZ_ANNOT_UNDERLINE
|
|
elseif item.drawer == "strikeout" then
|
|
annot_type = ffi.C.FZ_ANNOT_STRIKEOUT
|
|
end
|
|
page:addMarkupAnnotation(quadpoints, 4*n, annot_type)
|
|
page:close()
|
|
end
|
|
|
|
function PdfDocument:writeDocument()
|
|
DEBUG("writing document to", self.file)
|
|
self._document:writeDocument(self.file)
|
|
end
|
|
|
|
function PdfDocument:close()
|
|
if self.is_edited then
|
|
self:writeDocument()
|
|
end
|
|
Document.close(self)
|
|
end
|
|
|
|
function PdfDocument:getProps()
|
|
local props = self._document:getMetadata()
|
|
if props.title == "" then
|
|
local startPos = util.lastIndexOf(self.file, "%/")
|
|
if startPos > 0 then
|
|
props.title = string.sub(self.file, startPos + 1, -5) --remove extension .pdf
|
|
else
|
|
props.title = string.sub(self.file, 0, -5)
|
|
end
|
|
end
|
|
props.authors = props.author
|
|
props.series = ""
|
|
props.language = ""
|
|
return props
|
|
end
|
|
|
|
function PdfDocument:getLinkFromPosition(pageno, pos)
|
|
return self.koptinterface:getLinkFromPosition(self, pageno, pos)
|
|
end
|
|
|
|
function PdfDocument:clipPagePNGFile(pos0, pos1, pboxes, drawer, filename)
|
|
return self.koptinterface:clipPagePNGFile(self, pos0, pos1, pboxes, drawer, filename)
|
|
end
|
|
|
|
function PdfDocument:clipPagePNGString(pos0, pos1, pboxes, drawer)
|
|
return self.koptinterface:clipPagePNGString(self, pos0, pos1, pboxes, drawer)
|
|
end
|
|
|
|
function PdfDocument:getPageBBox(pageno)
|
|
return self.koptinterface:getPageBBox(self, pageno)
|
|
end
|
|
|
|
function PdfDocument:getPageDimensions(pageno, zoom, rotation)
|
|
return self.koptinterface:getPageDimensions(self, pageno, zoom, rotation)
|
|
end
|
|
|
|
function PdfDocument:getCoverPageImage()
|
|
return self.koptinterface:getCoverPageImage(self)
|
|
end
|
|
|
|
function PdfDocument:findText(pattern, origin, reverse, caseInsensitive, page)
|
|
return self.koptinterface:findText(self, pattern, origin, reverse, caseInsensitive, page)
|
|
end
|
|
|
|
function PdfDocument:renderPage(pageno, rect, zoom, rotation, gamma, render_mode)
|
|
return self.koptinterface:renderPage(self, pageno, rect, zoom, rotation, gamma, render_mode)
|
|
end
|
|
|
|
function PdfDocument:hintPage(pageno, zoom, rotation, gamma, render_mode)
|
|
return self.koptinterface:hintPage(self, pageno, zoom, rotation, gamma, render_mode)
|
|
end
|
|
|
|
function PdfDocument:drawPage(target, x, y, rect, pageno, zoom, rotation, gamma, render_mode)
|
|
return self.koptinterface:drawPage(self, target, x, y, rect, pageno, zoom, rotation, gamma, render_mode)
|
|
end
|
|
|
|
function PdfDocument:register(registry)
|
|
registry:addProvider("pdf", "application/pdf", self)
|
|
registry:addProvider("cbz", "application/cbz", self)
|
|
registry:addProvider("zip", "application/zip", self)
|
|
registry:addProvider("xps", "application/xps", self)
|
|
end
|
|
|
|
return PdfDocument
|