From 8e4516b824d614d21b2d854290193bffa824d621 Mon Sep 17 00:00:00 2001 From: chrox Date: Thu, 2 Jan 2014 11:08:06 +0800 Subject: [PATCH] add regional zoom mode in pdf/djvu page In regional zoom mode double tap will zoom to the tapped region(paragraph or column, etc., detected optically via libk2pdfopt). As the first demo, this feature is only turned on in flipping mode by tapping the top-left corner of the screen. Eventually we may incorporate this feature in "free" zoom mode. --- frontend/document/djvudocument.lua | 4 ++ frontend/document/koptinterface.lua | 29 ++++++++++++++ frontend/document/pdfdocument.lua | 4 ++ frontend/ui/geometry.lua | 29 ++++++++++++++ frontend/ui/reader/readerpaging.lua | 26 +++++++++++-- frontend/ui/reader/readerview.lua | 24 ++++++++++++ frontend/ui/reader/readerzooming.lua | 57 +++++++++++++++++++++++++--- koreader-base | 2 +- 8 files changed, 166 insertions(+), 9 deletions(-) diff --git a/frontend/document/djvudocument.lua b/frontend/document/djvudocument.lua index 31c80d87e..39e02e8de 100644 --- a/frontend/document/djvudocument.lua +++ b/frontend/document/djvudocument.lua @@ -80,6 +80,10 @@ function DjvuDocument:getOCRText(pageno, tboxes) return self.koptinterface:getOCRText(self, pageno, tboxes) end +function DjvuDocument:getPageRegions(pageno) + return self.koptinterface:getPageRegions(self, pageno) +end + function DjvuDocument:getUsedBBox(pageno) -- djvu does not support usedbbox, so fake it. local used = {} diff --git a/frontend/document/koptinterface.lua b/frontend/document/koptinterface.lua index c382d45e9..4b9fe676f 100644 --- a/frontend/document/koptinterface.lua +++ b/frontend/document/koptinterface.lua @@ -447,6 +447,35 @@ function KoptInterface:getNativeTextBoxesFromScratch(doc, pageno) end end +--[[ +get page regions in native page via optical method, +--]] +function KoptInterface:getPageRegions(doc, pageno) + local bbox = doc:getPageBBox(pageno) + local context_hash = self:getContextHash(doc, pageno, bbox) + local hash = "pageregions|"..context_hash + local cached = Cache:check(hash) + if not cached then + local page_size = Document.getNativePageDimensions(doc, pageno) + local bbox = { + x0 = 0, y0 = 0, + x1 = page_size.w, + y1 = page_size.h, + } + local kc = self:createContext(doc, pageno, bbox) + kc:setZoom(1.0) + local page = doc._document:openPage(pageno) + page:getPagePix(kc) + local regions = kc:getPageRegions() + Cache:insert(hash, CacheItem:new{ pageregions = regions }) + page:close() + kc:free() + return regions + else + return cached.pageregions + end +end + --[[ get word from OCR providing selected word box --]] diff --git a/frontend/document/pdfdocument.lua b/frontend/document/pdfdocument.lua index 3b081a502..2b489d215 100644 --- a/frontend/document/pdfdocument.lua +++ b/frontend/document/pdfdocument.lua @@ -70,6 +70,10 @@ function PdfDocument:getOCRText(pageno, tboxes) return self.koptinterface:getOCRText(self, pageno, tboxes) end +function PdfDocument:getPageRegions(pageno) + return self.koptinterface:getPageRegions(self, pageno) +end + function PdfDocument:getUsedBBox(pageno) local hash = "pgubbox|"..self.file.."|"..pageno local cached = Cache:check(hash) diff --git a/frontend/ui/geometry.lua b/frontend/ui/geometry.lua index a6fb49a6c..c7aeb433b 100644 --- a/frontend/ui/geometry.lua +++ b/frontend/ui/geometry.lua @@ -274,6 +274,35 @@ function Geom:offsetWithin(rect_b, dx, dy) end end +--[[ +center the current rectangle at position x and y of a given rectangle +]]-- +function Geom:centerWithin(rect_b, x, y) + -- check size constraints and shrink us when we're too big + if self.w > rect_b.w then + self.w = rect_b.w + end + if self.h > rect_b.h then + self.h = rect_b.h + end + -- place to center + self.x = x - self.w/2 + self.y = y - self.h/2 + -- check boundary + if self.x < rect_b.x then + self.x = rect_b.x + end + if self.y < rect_b.y then + self.y = rect_b.y + end + if self.x + self.w > rect_b.x + rect_b.w then + self.x = rect_b.x + rect_b.w - self.w + end + if self.y + self.h > rect_b.y + rect_b.h then + self.y = rect_b.y + rect_b.h - self.h + end +end + function Geom:shrinkInside(rect_b, dx, dy) self:offsetBy(dx, dy) return self:intersect(rect_b) diff --git a/frontend/ui/reader/readerpaging.lua b/frontend/ui/reader/readerpaging.lua index 487a2bd6a..302467fe7 100644 --- a/frontend/ui/reader/readerpaging.lua +++ b/frontend/ui/reader/readerpaging.lua @@ -209,16 +209,26 @@ function ReaderPaging:enterFlippingMode() self.orig_reflow_mode = self.view.document.configurable.text_wrap self.orig_footer_mode = self.view.footer_visible self.orig_scroll_mode = self.view.page_scroll + self.orig_zoom_mode = self.view.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.page_scroll = false self.view.footer_visible = true + Input.disable_double_tap = false + DGESDETECT_DISABLE_DOUBLE_TAP = false + self.ui:handleEvent(Event:new("SetZoomMode", "page")) end function ReaderPaging:exitFlippingMode() self.view.document.configurable.text_wrap = self.orig_reflow_mode self.view.page_scroll = self.orig_scroll_mode self.view.footer_visible = self.orig_footer_mode + DGESDETECT_DISABLE_DOUBLE_TAP = self.DGESDETECT_DISABLE_DOUBLE_TAP + Input.disable_double_tap = DGESDETECT_DISABLE_DOUBLE_TAP + DEBUG("restore zoom mode", self.orig_zoom_mode) + self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode)) end function ReaderPaging:updateOriginalPage(page) @@ -268,7 +278,11 @@ end function ReaderPaging:onPan(arg, ges) if self.flipping_mode then - self:flipping(self.flipping_page, ges) + if self.view.zoom_mode == "page" then + self:flipping(self.flipping_page, ges) + else + self.view:PanningStart(-ges.relative.x, -ges.relative.y) + end elseif ges.direction == "north" or ges.direction == "south" then self:onPanningRel(self.last_pan_relative_y - ges.relative.y) self.last_pan_relative_y = ges.relative.y @@ -278,7 +292,11 @@ end function ReaderPaging:onPanRelease(arg, ges) if self.flipping_mode then - self:updateFlippingPage(self.current_page) + if self.view.zoom_mode == "page" then + self:updateFlippingPage(self.current_page) + else + self.view:PanningStop() + end else self.last_pan_relative_y = 0 UIManager.full_refresh = true @@ -588,7 +606,9 @@ function ReaderPaging:onGotoPageRel(diff) local new_va = self.visible_area:copy() local x_pan_off, y_pan_off = 0, 0 - if self.zoom_mode:find("width") then + if self.zoom_mode == "free" then + -- do nothing in free zoom mode + elseif self.zoom_mode:find("width") then y_pan_off = self.visible_area.h * diff elseif self.zoom_mode:find("height") then x_pan_off = self.visible_area.w * diff diff --git a/frontend/ui/reader/readerview.lua b/frontend/ui/reader/readerview.lua index 1648171eb..8f6c0a517 100644 --- a/frontend/ui/reader/readerview.lua +++ b/frontend/ui/reader/readerview.lua @@ -439,6 +439,30 @@ function ReaderView:PanningUpdate(dx, dy) return true end +function ReaderView:PanningStart(x, y) + DEBUG("panning start", x, y) + if not self.panning_visible_area then + self.panning_visible_area = self.visible_area:copy() + end + self.visible_area = self.panning_visible_area:copy() + self.visible_area:offsetWithin(self.page_area, x, y) + self.ui:handleEvent(Event:new("ViewRecalculate", self.visible_area, self.page_area)) + UIManager:setDirty(self.dialog) +end + +function ReaderView:PanningStop() + self.panning_visible_area = nil +end + +function ReaderView:SetZoomCenter(x, y) + local old = self.visible_area:copy() + self.visible_area:centerWithin(self.page_area, x, y) + if self.visible_area ~= old then + self.ui:handleEvent(Event:new("ViewRecalculate", self.visible_area, self.page_area)) + UIManager:setDirty(self.dialog) + end +end + function ReaderView:onSetScreenMode(new_mode, rotation) if new_mode == "landscape" or new_mode == "portrait" then self.screen_mode = new_mode diff --git a/frontend/ui/reader/readerzooming.lua b/frontend/ui/reader/readerzooming.lua index a87643ba9..e00e31acb 100644 --- a/frontend/ui/reader/readerzooming.lua +++ b/frontend/ui/reader/readerzooming.lua @@ -84,6 +84,16 @@ function ReaderZooming:init() } } }, + ToggleFreeZoom = { + GestureRange:new{ + ges = "double_tap", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + }, } end self.ui.menu:registerToMainMenu(self) @@ -126,6 +136,24 @@ function ReaderZooming:onPinch(arg, ges) return true end +function ReaderZooming:onToggleFreeZoom(arg, ges) + if self.zoom_mode ~= "free" then + self.orig_zoom = self.zoom + self.orig_zoom_mode = self.zoom_mode + local xpos, ypos + self.zoom, xpos, ypos = self:getRegionalZoomCenter(self.current_page, ges.pos) + DEBUG("zoom center", self.zoom, xpos, ypos) + self.ui:handleEvent(Event:new("SetZoomMode", "free")) + if xpos == nil or ypos == nil then + xpos = ges.pos.x * self.zoom / self.orig_zoom + ypos = ges.pos.y * self.zoom / self.orig_zoom + end + self.view:SetZoomCenter(xpos, ypos) + else + self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode or "page")) + end +end + function ReaderZooming:onSetDimensions(dimensions) -- we were resized self.dimen = dimensions @@ -160,9 +188,9 @@ function ReaderZooming:onSetZoomMode(new_mode) self.view.zoom_mode = new_mode if self.zoom_mode ~= new_mode then DEBUG("setting zoom mode to", new_mode) + self.ui:handleEvent(Event:new("ZoomModeUpdate", new_mode)) self.zoom_mode = new_mode self:setZoom() - self.ui:handleEvent(Event:new("ZoomModeUpdate", new_mode)) end end @@ -212,15 +240,34 @@ function ReaderZooming:getZoom(pageno) zoom = zoom_w elseif self.zoom_mode == "contentheight" or self.zoom_mode == "pageheight" then zoom = zoom_h + elseif self.zoom_mode == "free" then + zoom = self.zoom end return zoom end -function ReaderZooming:setZoom() - -- nothing to do in free zoom mode - if self.zoom_mode == "free" then - return +function ReaderZooming:getRegionalZoomCenter(pageno, pos) + local p_pos = self.view:getSinglePagePosition(pos) + local page_size = self.ui.document:getNativePageDimensions(pageno) + local pos_x = p_pos.x / page_size.w / p_pos.zoom + local pos_y = p_pos.y / page_size.h / p_pos.zoom + local regions = self.ui.document:getPageRegions(pageno) + DEBUG("get page regions", regions) + local margin = self.ui.document.configurable.page_margin * Screen:getDPI() + for i = 1, #regions do + if regions[i].x0 <= pos_x and pos_x <= regions[i].x1 + and regions[i].y0 <= pos_y and pos_y <= regions[i].y1 then + local zoom = 1/(regions[i].x1 - regions[i].x0) + zoom = zoom/(1 + 3*margin/zoom/page_size.w) + local xpos = (regions[i].x0 + regions[i].x1)/2 * zoom * page_size.w + local ypos = p_pos.y / p_pos.zoom * zoom + return zoom, xpos, ypos + end end + return 2 +end + +function ReaderZooming:setZoom() if not self.dimen then self.dimen = self.ui.dimen end diff --git a/koreader-base b/koreader-base index 8b9989a5f..bf3ef2a6c 160000 --- a/koreader-base +++ b/koreader-base @@ -1 +1 @@ -Subproject commit 8b9989a5f91d1e708a285c3e660009232ad14a08 +Subproject commit bf3ef2a6c9002a665b8c224632eba744e6147fe0