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