PDF free zoom mode revisit

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)
pull/2082/head
chrox 8 years ago
parent 22d0dbfb4f
commit 5983050d79

@ -1 +1 @@
Subproject commit b1b0350697750565f1dae9be88dd59fc1ee79446 Subproject commit 6fa062f20cf2a7bafaaf4bd215c145c496643fc1

@ -136,9 +136,6 @@ DMINIBAR_HEIGHT = 7 -- Should be smaller than DMINIBAR_CONTAINER_HEI
DMINIBAR_CONTAINER_HEIGHT = 14 -- Larger means more padding at the bottom, at the risk of eating into the last line DMINIBAR_CONTAINER_HEIGHT = 14 -- Larger means more padding at the bottom, at the risk of eating into the last line
DMINIBAR_FONT_SIZE = 14 DMINIBAR_FONT_SIZE = 14
-- gesture detector defaults
DGESDETECT_DISABLE_DOUBLE_TAP = true
-- change this to any numerical value if you want to antomatically save settings when turning pages -- change this to any numerical value if you want to antomatically save settings when turning pages
DAUTO_SAVE_PAGING_COUNT = nil DAUTO_SAVE_PAGING_COUNT = nil

@ -267,30 +267,24 @@ function ReaderPaging:onToggleBookmarkFlipping()
end end
function ReaderPaging:enterFlippingMode() function ReaderPaging:enterFlippingMode()
self.ui:handleEvent(Event:new("EnterFlippingMode"))
self.orig_reflow_mode = self.view.document.configurable.text_wrap self.orig_reflow_mode = self.view.document.configurable.text_wrap
self.orig_scroll_mode = self.view.page_scroll self.orig_scroll_mode = self.view.page_scroll
self.orig_zoom_mode = self.view.zoom_mode self.orig_zoom_mode = self.view.zoom_mode
DEBUG("store zoom mode", self.orig_zoom_mode) DEBUG("store zoom mode", self.orig_zoom_mode)
self.DGESDETECT_DISABLE_DOUBLE_TAP = DGESDETECT_DISABLE_DOUBLE_TAP
self.view.document.configurable.text_wrap = 0 self.view.document.configurable.text_wrap = 0
self.view.page_scroll = self.flipping_scroll_mode self.view.page_scroll = self.flipping_scroll_mode
Input.disable_double_tap = false Input.disable_double_tap = false
DGESDETECT_DISABLE_DOUBLE_TAP = false self.ui:handleEvent(Event:new("EnterFlippingMode", self.flipping_zoom_mode))
self.ui:handleEvent(Event:new("SetZoomMode", self.flipping_zoom_mode))
end end
function ReaderPaging:exitFlippingMode() function ReaderPaging:exitFlippingMode()
self.ui:handleEvent(Event:new("ExitFlippingMode"))
self.view.document.configurable.text_wrap = self.orig_reflow_mode self.view.document.configurable.text_wrap = self.orig_reflow_mode
self.view.page_scroll = self.orig_scroll_mode self.view.page_scroll = self.orig_scroll_mode
DGESDETECT_DISABLE_DOUBLE_TAP = self.DGESDETECT_DISABLE_DOUBLE_TAP Input.disable_double_tap = true
Input.disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP
self.flipping_zoom_mode = self.view.zoom_mode self.flipping_zoom_mode = self.view.zoom_mode
self.flipping_scroll_mode = self.view.page_scroll self.flipping_scroll_mode = self.view.page_scroll
DEBUG("restore zoom mode", self.orig_zoom_mode) DEBUG("restore zoom mode", self.orig_zoom_mode)
self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode)) self.ui:handleEvent(Event:new("ExitFlippingMode", self.orig_zoom_mode))
end end
function ReaderPaging:updateOriginalPage(page) function ReaderPaging:updateOriginalPage(page)

@ -113,7 +113,7 @@ function ReaderZooming:onReadSettings(config)
end end
function ReaderZooming:onSaveSettings() function ReaderZooming:onSaveSettings()
self.ui.doc_settings:saveSetting("zoom_mode", self.zoom_mode) self.ui.doc_settings:saveSetting("zoom_mode", self.orig_zoom_mode or self.zoom_mode)
end end
function ReaderZooming:onSpread(arg, ges) function ReaderZooming:onSpread(arg, ges)
@ -141,7 +141,6 @@ end
function ReaderZooming:onToggleFreeZoom(arg, ges) function ReaderZooming:onToggleFreeZoom(arg, ges)
if self.zoom_mode ~= "free" then if self.zoom_mode ~= "free" then
self.orig_zoom = self.zoom self.orig_zoom = self.zoom
self.orig_zoom_mode = self.zoom_mode
local xpos, ypos local xpos, ypos
self.zoom, xpos, ypos = self:getRegionalZoomCenter(self.current_page, ges.pos) self.zoom, xpos, ypos = self:getRegionalZoomCenter(self.current_page, ges.pos)
DEBUG("zoom center", self.zoom, xpos, ypos) DEBUG("zoom center", self.zoom, xpos, ypos)
@ -152,7 +151,7 @@ function ReaderZooming:onToggleFreeZoom(arg, ges)
end end
self.view:SetZoomCenter(xpos, ypos) self.view:SetZoomCenter(xpos, ypos)
else else
self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode or "page")) self.ui:handleEvent(Event:new("SetZoomMode", "page"))
end end
end end
@ -207,6 +206,20 @@ function ReaderZooming:onReZoom()
return true return true
end end
function ReaderZooming:onEnterFlippingMode(zoom_mode)
self.orig_zoom_mode = self.zoom_mode
if zoom_mode == "free" then
self.ui:handleEvent(Event:new("SetZoomMode", "page"))
else
self.ui:handleEvent(Event:new("SetZoomMode", zoom_mode))
end
end
function ReaderZooming:onExitFlippingMode(zoom_mode)
self.orig_zoom_mode = nil
self.ui:handleEvent(Event:new("SetZoomMode", zoom_mode))
end
function ReaderZooming:getZoom(pageno) function ReaderZooming:getZoom(pageno)
-- check if we're in bbox mode and work on bbox if that's the case -- check if we're in bbox mode and work on bbox if that's the case
local zoom = nil local zoom = nil
@ -254,22 +267,19 @@ end
function ReaderZooming:getRegionalZoomCenter(pageno, pos) function ReaderZooming:getRegionalZoomCenter(pageno, pos)
local p_pos = self.view:getSinglePagePosition(pos) local p_pos = self.view:getSinglePagePosition(pos)
local page_size = self.ui.document:getNativePageDimensions(pageno) local page_size = self.ui.document:getNativePageDimensions(pageno)
local pos_x = p_pos.x / page_size.w / p_pos.zoom local pos_x = p_pos.x / page_size.w
local pos_y = p_pos.y / page_size.h / p_pos.zoom local pos_y = p_pos.y / page_size.h
local regions = self.ui.document:getPageRegions(pageno) local block = self.ui.document:getPageBlock(pageno, pos_x, pos_y)
DEBUG("get page regions", regions)
local margin = self.ui.document.configurable.page_margin * Screen:getDPI() local margin = self.ui.document.configurable.page_margin * Screen:getDPI()
for i = 1, #regions do if block then
if regions[i].x0 <= pos_x and pos_x <= regions[i].x1 local zoom = self.dimen.w / page_size.w / (block.x1 - block.x0)
and regions[i].y0 <= pos_y and pos_y <= regions[i].y1 then zoom = zoom/(1 + 3*margin/zoom/page_size.w)
local zoom = 1/(regions[i].x1 - regions[i].x0) local xpos = (block.x0 + block.x1)/2 * zoom * page_size.w
zoom = zoom/(1 + 3*margin/zoom/page_size.w) local ypos = p_pos.y / p_pos.zoom * zoom
local xpos = (regions[i].x0 + regions[i].x1)/2 * zoom * page_size.w return zoom, xpos, ypos
local ypos = p_pos.y / p_pos.zoom * zoom
return zoom, xpos, ypos
end
end end
return 2 local zoom = 2*self.dimen.w / page_size.w
return zoom/(1 + 3*margin/zoom/page_size.w)
end end
function ReaderZooming:setZoom() function ReaderZooming:setZoom()

@ -88,7 +88,7 @@ local Input = {
}, },
timer_callbacks = {}, timer_callbacks = {},
disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP, disable_double_tap = true,
-- keyboard state: -- keyboard state:
modifiers = { modifiers = {

@ -69,8 +69,8 @@ function DjvuDocument:getOCRText(pageno, tboxes)
return self.koptinterface:getOCRText(self, pageno, tboxes) return self.koptinterface:getOCRText(self, pageno, tboxes)
end end
function DjvuDocument:getPageRegions(pageno) function DjvuDocument:getPageBlock(pageno, x, y)
return self.koptinterface:getPageRegions(self, pageno) return self.koptinterface:getPageBlock(self, pageno, x, y)
end end
function DjvuDocument:getUsedBBox(pageno) function DjvuDocument:getUsedBBox(pageno)

@ -540,10 +540,11 @@ end
--[[ --[[
get page regions in native page via optical method, get page regions in native page via optical method,
--]] --]]
function KoptInterface:getPageRegions(doc, pageno) function KoptInterface:getPageBlock(doc, pageno, x, y)
local kctx = nil
local bbox = doc:getPageBBox(pageno) local bbox = doc:getPageBBox(pageno)
local context_hash = self:getContextHash(doc, pageno, bbox) local context_hash = self:getContextHash(doc, pageno, bbox)
local hash = "pageregions|"..context_hash local hash = "pageblocks|"..context_hash
local cached = Cache:check(hash) local cached = Cache:check(hash)
if not cached then if not cached then
local page_size = Document.getNativePageDimensions(doc, pageno) local page_size = Document.getNativePageDimensions(doc, pageno)
@ -553,17 +554,19 @@ function KoptInterface:getPageRegions(doc, pageno)
y1 = page_size.h, y1 = page_size.h,
} }
local kc = self:createContext(doc, pageno, bbox) local kc = self:createContext(doc, pageno, bbox)
kc:setZoom(1.0) local screen_size = Screen:getSize()
-- leptonica needs a source image of at least 300dpi
kc:setZoom(screen_size.w / page_size.w * 300 / self.screen_dpi)
local page = doc._document:openPage(pageno) local page = doc._document:openPage(pageno)
page:getPagePix(kc) page:getPagePix(kc)
local regions = kc:getPageRegions() kc:findPageBlocks()
Cache:insert(hash, CacheItem:new{ pageregions = regions }) Cache:insert(hash, CacheItem:new{ kctx = kc })
page:close() page:close()
kc:free() kctx = kc
return regions
else else
return cached.pageregions kctx = cached.kctx
end end
return kctx:getPageBlock(x, y)
end end
--[[ --[[

@ -77,8 +77,8 @@ function PdfDocument:getOCRText(pageno, tboxes)
return self.koptinterface:getOCRText(self, pageno, tboxes) return self.koptinterface:getOCRText(self, pageno, tboxes)
end end
function PdfDocument:getPageRegions(pageno) function PdfDocument:getPageBlock(pageno, x, y)
return self.koptinterface:getPageRegions(self, pageno) return self.koptinterface:getPageBlock(self, pageno, x, y)
end end
function PdfDocument:getUsedBBox(pageno) function PdfDocument:getUsedBBox(pageno)

@ -181,8 +181,6 @@ function UIManager:close(widget, refreshtype, refreshregion)
return return
end end
dbg("close widget", widget.id) dbg("close widget", widget.id)
-- TODO: Why do we the following?
Input.disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP
local dirty = false local dirty = false
for i = #self._window_stack, 1, -1 do for i = #self._window_stack, 1, -1 do
if self._window_stack[i].widget == widget then if self._window_stack[i].widget == widget then

Loading…
Cancel
Save