add manual page crop for pdf/djvu documents

pull/2/merge
chrox 11 years ago
parent ddfe5ca3a9
commit a1aa41136c

@ -26,7 +26,7 @@ CreOptions = {
item_align_center = 1.0,
spacing = Screen:getWidth()*0.03,
item_font_size = {18, 20, 22, 24, 29, 33, 39, 44},
values = {18, 20, 22, 24, 29, 33, 39, 44},
args = {18, 20, 22, 24, 29, 33, 39, 44},
default_value = 1,
event = "SetFontSize",
},

@ -47,13 +47,16 @@ Document = {
number_of_pages = 0,
-- if not pageable, length of the document in pixels
doc_height = 0,
-- other metadata
title = "",
author = "",
date = ""
},
-- override bbox from orignal page's getUsedBBox
bbox = {},
-- flag to show whether the document was opened successfully
is_open = false,
error_message = nil,
@ -123,20 +126,56 @@ function Document:getPageDimensions(pageno, zoom, rotation)
return native_dimen
end
function Document:oddEven(number)
if number % 2 == 1 then
return "odd"
else
return "even"
end
end
function Document:getPageBBox(pageno)
local bbox = self.bbox[pageno] -- exact
local oddEven = self:oddEven(pageno)
if bbox ~= nil then
DEBUG("bbox from", pageno)
else
bbox = self.bbox[oddEven] -- odd/even
end
if bbox ~= nil then -- last used up to this page
DEBUG("bbox from", oddEven)
else
for i = 0,pageno do
bbox = self.bbox[ pageno - i ]
if bbox ~= nil then
DEBUG("bbox from", pageno - i)
break
end
end
end
if bbox == nil then -- fallback bbox
bbox = self:getUsedBBox(pageno)
DEBUG("bbox from ORIGINAL page")
end
DEBUG("final bbox", bbox)
return bbox
end
--[[
This method returns pagesize if bbox is corrupted
--]]
function Document:getUsedBBoxDimensions(pageno, zoom, rotation)
ubbox = self:getUsedBBox(pageno)
if ubbox.x0 < 0 or ubbox.y0 < 0 or ubbox.x1 < 0 or ubbox.y1 < 0 then
local bbox = self:getPageBBox(pageno)
local ubbox_dimen = nil
if bbox.x0 < 0 or bbox.y0 < 0 or bbox.x1 < 0 or bbox.y1 < 0 then
-- if document's bbox info is corrupted, we use the page size
ubbox_dimen = self:getPageDimensions(pageno, zoom, rotation)
else
ubbox_dimen = Geom:new{
x = ubbox.x0,
y = ubbox.y0,
w = ubbox.x1 - ubbox.x0,
h = ubbox.y1 - ubbox.y0,
x = bbox.x0,
y = bbox.y0,
w = bbox.x1 - bbox.x0,
h = bbox.y1 - bbox.y0,
}
if zoom ~= 1 then
ubbox_dimen:transformByScale(zoom)

@ -39,8 +39,11 @@ KoptOptions = {
name="trim_page",
name_text = "Page Crop",
toggle = {"auto", "manual"},
alternate = false,
values = {1, 0},
default_value = 1,
event = "PageCrop",
args = {"auto", "manual"},
}
}
},
@ -115,7 +118,15 @@ KoptOptions = {
toggle = {"On", "Off"},
values = {1, 0},
default_value = 0,
event = "RedrawCurrentPage",
events = {
{
event = "RedrawCurrentPage",
},
{
event = "SetZoomMode",
args = {"page", nil},
},
}
},
{
name="screen_rotation",
@ -185,7 +196,7 @@ function KoptInterface:getKOPTContext(doc, pageno)
kc:setDefectSize(doc.configurable.defect_size)
kc:setLineSpacing(doc.configurable.line_spacing)
kc:setWordSpacing(doc.configurable.word_spacing)
local bbox = doc:getUsedBBox(pageno)
local bbox = doc:getPageBBox(pageno)
kc:setBBox(bbox.x0, bbox.y0, bbox.x1, bbox.y1)
return kc
end

@ -88,12 +88,22 @@ function OptionTextItem:onTapSelect()
self[1].color = 15
local option_value = nil
local option_arg = nil
if type(self.values) == "table" then
if self.values then
self.values = self.values or {}
option_value = self.values[self.current_item]
self.config:onConfigChoice(self.name, option_value, self.event)
elseif type(self.args) == "table" then
self.config:onConfigChoice(self.name, option_value)
end
if self.event then
self.args = self.args or {}
option_arg = self.args[self.current_item]
self.config:onConfigChoice(self.name, option_arg, self.event)
self.config:onConfigEvent(self.event, option_arg)
end
if self.events then
for i=0,#self.events do
self.events[i].args = self.events[i].args or {}
option_arg = self.events[i].args[self.current_item]
self.config:onConfigEvent(self.events[i].event, option_arg)
end
end
UIManager.repaint_all = true
return true
@ -234,7 +244,7 @@ function ToggleSwitch:setPosition(position)
end
function ToggleSwitch:togglePosition(position)
if self.n_pos == 2 then
if self.n_pos == 2 and self.alternate ~= false then
self.position = (self.position+1)%self.n_pos
self.position = self.position == 0 and self.n_pos or self.position
else
@ -248,12 +258,22 @@ function ToggleSwitch:onTapSelect(position)
self:togglePosition(position)
local option_value = nil
local option_arg = nil
if type(self.values) == "table" then
if self.values then
self.values = self.values or {}
option_value = self.values[self.position]
self.config:onConfigChoice(self.name, option_value, self.event)
elseif type(self.args) == "table" then
self.config:onConfigChoice(self.name, option_value)
end
if self.event then
self.args = self.args or {}
option_arg = self.args[self.position]
self.config:onConfigChoice(self.name, option_arg, self.event)
self.config:onConfigEvent(self.event, option_arg)
end
if self.events then
for i=1,#self.events do
self.events[i].args = self.events[i].args or {}
option_arg = self.events[i].args[self.position]
self.config:onConfigEvent(self.events[i].event, option_arg)
end
end
UIManager.repaint_all = true
end
@ -319,6 +339,7 @@ function ConfigOption:init()
if self.options[c].name then
if self.options[c].values then
local val = self.config.configurable[self.options[c].name]
DEBUG("val", val)
local min_diff = math.abs(val - self.options[c].values[1])
local diff = nil
for index, val_ in pairs(self.options[c].values) do
@ -384,9 +405,11 @@ function ConfigOption:init()
local switch = ToggleSwitch:new{
name = self.options[c].name,
toggle = self.options[c].toggle,
alternate = self.options[c].alternate,
values = self.options[c].values,
args = self.options[c].args,
event = self.options[c].event,
events = self.options[c].events,
config = self.config,
}
local position = current_item
@ -552,7 +575,18 @@ function ConfigDialog:onShowConfigPanel(index)
return true
end
function ConfigDialog:onCloseMenu()
function ConfigDialog:onConfigChoice(option_name, option_value)
DEBUG("config option value", option_name, option_value)
self.configurable[option_name] = option_value
end
function ConfigDialog:onConfigEvent(option_event, option_arg)
DEBUG("config option event", option_event, option_arg)
self.ui:handleEvent(Event:new(option_event, option_arg))
end
function ConfigDialog:closeDialog()
DEBUG("closing config dialog")
UIManager:close(self)
if self.close_callback then
self.close_callback()
@ -562,7 +596,7 @@ end
function ConfigDialog:onTapCloseMenu(arg, ges_ev)
if ges_ev.pos:notIntersectWith(self.dialog_dimen) then
self:onCloseMenu()
self:closeDialog()
return true
end
end

@ -259,8 +259,12 @@ function Geom:offsetWithin(rect_b, dx, dy)
end
end
--[[
return the Euclidean distance between two geoms
]]--
function Geom:distance(geom)
return math.sqrt(math.pow(self.x - geom.x, 2) + math.pow(self.y - geom.y, 2))
end
--[[
Simple math helper function

@ -72,31 +72,14 @@ function ReaderConfig:init()
end
function ReaderConfig:onShowConfigMenu()
local config_dialog = ConfigDialog:new{
self.config_dialog = ConfigDialog:new{
dimen = self.dimen:copy(),
ui = self.ui,
configurable = self.configurable,
config_options = self.options,
}
function config_dialog:onConfigChoice(option_name, option_value, event)
self.configurable[option_name] = option_value
if event then
self.ui:handleEvent(Event:new(event, option_value))
end
end
local dialog_container = CenterContainer:new{
config_dialog,
dimen = self.dimen:copy(),
}
config_dialog.close_callback = function ()
UIManager:close(menu_container)
end
self.dialog_container = dialog_container
UIManager:show(config_dialog)
UIManager:show(self.config_dialog)
return true
end
@ -111,6 +94,10 @@ function ReaderConfig:onSetDimensions(dimen)
self:init()
end
function ReaderConfig:onCloseConfig()
self.config_dialog:closeDialog()
end
function ReaderConfig:onReadSettings(config)
self.configurable:loadSettings(config, self.options.prefix..'_')
end

@ -0,0 +1,198 @@
--[[
BBoxWidget shows a bbox for page cropping
]]
BBoxWidget = InputContainer:new{
page_bbox = nil,
screen_bbox = nil,
linesize = 2,
}
function BBoxWidget:getSize()
return Geom:new{
x = 0, y = 0,
w = Screen:getWidth(),
h = Screen:getHeight()
}
end
function BBoxWidget:paintTo(bb, x, y)
self.screen_bbox = self.screen_bbox or self:page_to_screen()
local bbox = self.screen_bbox
-- upper_left
bb:invertRect(bbox.x0 + self.linesize, bbox.y0, bbox.x1 - bbox.x0, self.linesize)
bb:invertRect(bbox.x0, bbox.y0, self.linesize, bbox.y1 - bbox.y0 + self.linesize)
-- bottom_right
bb:invertRect(bbox.x0 + self.linesize, bbox.y1, bbox.x1 - bbox.x0 - self.linesize, self.linesize)
bb:invertRect(bbox.x1, bbox.y0 + self.linesize, self.linesize, bbox.y1 - bbox.y0)
end
-- transform page bbox to screen bbox
function BBoxWidget:page_to_screen()
local bbox = {}
local scale = self.crop.zoom
local screen_offset = self.crop.screen_offset
DEBUG("screen offset in page_to_screen", screen_offset)
bbox.x0 = self.page_bbox.x0 * scale + screen_offset.x
bbox.y0 = self.page_bbox.y0 * scale + screen_offset.y
bbox.x1 = self.page_bbox.x1 * scale + screen_offset.x
bbox.y1 = self.page_bbox.y1 * scale + screen_offset.y
return bbox
end
-- transform screen bbox to page bbox
function BBoxWidget:screen_to_page()
local bbox = {}
local scale = self.crop.zoom
local screen_offset = self.crop.screen_offset
DEBUG("screen offset in screen_to_page", screen_offset)
bbox.x0 = self.screen_bbox.x0 / scale - screen_offset.x
bbox.y0 = self.screen_bbox.y0 / scale - screen_offset.y
bbox.x1 = self.screen_bbox.x1 / scale - screen_offset.x
bbox.y1 = self.screen_bbox.y1 / scale - screen_offset.y
return bbox
end
function BBoxWidget:oddEven(number)
if number % 2 == 1 then
return "odd"
else
return "even"
end
end
function BBoxWidget:init()
if Device:isTouchDevice() then
self.ges_events = {
AdjustCrop = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = 0, y = 0,
w = Screen:getWidth(),
h = Screen:getHeight()
}
}
},
ConfirmCrop = {
GestureRange:new{
ges = "double_tap",
range = Geom:new{
x = 0, y = 0,
w = Screen:getWidth(),
h = Screen:getHeight()
}
}
},
CancelCrop = {
GestureRange:new{
ges = "hold_release",
range = Geom:new{
x = 0, y = 0,
w = Screen:getWidth(),
h = Screen:getHeight()
}
}
},
}
end
end
function BBoxWidget:onGesture(ev)
for name, gsseq in pairs(self.ges_events) do
for _, gs_range in ipairs(gsseq) do
--DEBUG("gs_range", gs_range)
if gs_range:match(ev) then
local eventname = gsseq.event or name
return self:handleEvent(Event:new(eventname, ev.pos))
end
end
end
end
function BBoxWidget:onAdjustCrop(pos)
DEBUG("adjusting crop bbox with pos", pos)
local bbox = self.screen_bbox
local upper_left = Geom:new{ x = bbox.x0, y = bbox.y0}
local bottom_right = Geom:new{ x = bbox.x1, y = bbox.y1}
if upper_left:distance(pos) < bottom_right:distance(pos) then
upper_left.x = pos.x
upper_left.y = pos.y
else
bottom_right.x = pos.x
bottom_right.y = pos.y
end
self.screen_bbox = {
x0 = upper_left.x,
y0 = upper_left.y,
x1 = bottom_right.x,
y1 = bottom_right.y
}
UIManager.repaint_all = true
end
function BBoxWidget:onConfirmCrop()
self.page_bbox = self:screen_to_page()
--DEBUG("new bbox", self.page_bbox)
UIManager:close(self)
self.ui:handleEvent(Event:new("BBoxUpdate"), self.page_bbox)
self.document.bbox[self.pageno] = self.page_bbox
self.document.bbox[self:oddEven(self.pageno)] = self.page_bbox
self.ui:handleEvent(Event:new("SetZoomMode", self.orig_zoom_mode))
self.document.configurable.text_wrap = self.orig_reflow_mode
UIManager.repaint_all = true
end
ReaderCropping = InputContainer:new{}
function ReaderCropping:onPageCrop(mode)
if mode == "auto" then return end
local orig_reflow_mode = self.document.configurable.text_wrap
self.document.configurable.text_wrap = 0
self.ui:handleEvent(Event:new("CloseConfig"))
self.ui:handleEvent(Event:new("SetZoomMode", "page"))
local ubbox = self.document:getPageBBox(self.current_page)
--DEBUG("used page bbox", ubbox)
self.crop_bbox = BBoxWidget:new{
page_bbox = ubbox,
ui = self.ui,
crop = self,
document = self.document,
pageno = self.current_page,
orig_zoom_mode = self.orig_zoom_mode,
orig_reflow_mode = orig_reflow_mode,
}
UIManager:show(self.crop_bbox)
return true
end
function ReaderCropping:onPageUpdate(page_no)
--DEBUG("page updated to", page_no)
self.current_page = page_no
end
function ReaderCropping:onZoomUpdate(zoom)
--DEBUG("zoom updated to", zoom)
self.zoom = zoom
end
function ReaderCropping:onScreenOffsetUpdate(screen_offset)
--DEBUG("offset updated to", screen_offset)
self.screen_offset = screen_offset
end
function ReaderCropping:onSetZoomMode(mode)
if self.orig_zoom_mode == nil then
--DEBUG("backup zoom mode", mode)
self.orig_zoom_mode = mode
end
end
function ReaderCropping:onReadSettings(config)
local bbox = config:readSetting("bbox")
self.document.bbox = bbox
DEBUG("read document bbox", self.document.bbox)
end
function ReaderCropping:onCloseDocument()
self.ui.doc_settings:saveSetting("bbox", self.document.bbox)
end

@ -38,7 +38,7 @@ function ReaderView:paintTo(bb, x, y)
bb:paintRect(x, y, inner_offset.x, self.ui.dimen.h, self.outer_page_color)
bb:paintRect(x + self.ui.dimen.w - inner_offset.x - 1, y, inner_offset.x + 1, self.ui.dimen.h, self.outer_page_color)
end
self.ui:handleEvent(Event:new("ScreenOffsetUpdate", inner_offset))
-- draw content
if self.ui.document.info.has_pages then
self.ui.document:drawPage(

@ -61,7 +61,7 @@ function ReaderZooming:onReadSettings(config)
if not zoom_mode then
zoom_mode = self.DEFAULT_ZOOM_MODE
end
self:onSetZoomMode(zoom_mode)
self.ui:handleEvent(Event:new("SetZoomMode", zoom_mode))
end
function ReaderZooming:onCloseDocument()
@ -99,7 +99,6 @@ function ReaderZooming:onSetZoomMode(new_mode)
self:setZoom()
self.ui:handleEvent(Event:new("ZoomModeUpdate", new_mode))
end
return true
end
function ReaderZooming:onPageUpdate(new_page_no)
@ -148,7 +147,7 @@ function ReaderZooming:setZoom()
elseif self.zoom_mode == "contentheight" or self.zoom_mode == "pageheight" then
self.zoom = zoom_h
end
self.view:onZoomUpdate(self.zoom)
self.ui:handleEvent(Event:new("ZoomUpdate", self.zoom))
end
function ReaderZooming:genSetZoomModeCallBack(mode)

@ -11,6 +11,7 @@ require "ui/reader/readerfont"
require "ui/reader/readertypeset"
require "ui/reader/readermenu"
require "ui/reader/readerconfig"
require "ui/reader/readercropping"
--[[
This is an abstraction for a reader interface
@ -112,6 +113,14 @@ function ReaderUI:init()
ui = self
}
table.insert(self, panner)
-- cropping controller
local cropper = ReaderCropping:new{
dialog = self.dialog,
view = self[1],
ui = self,
document = self.document,
}
table.insert(self, cropper)
else
-- make sure we load document first before calling any callback
table.insert(self.postInitCallback, function()

Loading…
Cancel
Save