mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Highlights: edit boundaries in pdf (#13364)
This commit is contained in:
@@ -1028,8 +1028,25 @@ function ReaderHighlight:getHighlightVisibleBoxes(index)
|
||||
end
|
||||
|
||||
function ReaderHighlight:updateHighlight(index, side, direction, move_by_char)
|
||||
if move_by_char and self.ui.paging then return end
|
||||
local highlight = self.ui.annotation.annotations[index]
|
||||
local highlight_before = util.tableDeepCopy(highlight)
|
||||
local is_updated
|
||||
if self.ui.rolling then
|
||||
is_updated = self:updateHighlightRolling(highlight, side, direction, move_by_char)
|
||||
else
|
||||
is_updated = self:updateHighlightPaging(highlight, side, direction)
|
||||
end
|
||||
if is_updated then
|
||||
highlight.text = util.cleanupSelectedText(highlight.text)
|
||||
self:writePdfAnnotation("delete", highlight_before)
|
||||
self:writePdfAnnotation("save", highlight)
|
||||
self.ui:handleEvent(Event:new("AnnotationsModified", { highlight, highlight_before }))
|
||||
UIManager:setDirty(self.dialog, "ui")
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderHighlight:updateHighlightRolling(highlight, side, direction, move_by_char)
|
||||
local highlight_beginning = highlight.pos0
|
||||
local highlight_end = highlight.pos1
|
||||
if side == 0 then -- we move pos0
|
||||
@@ -1040,22 +1057,20 @@ function ReaderHighlight:updateHighlight(index, side, direction, move_by_char)
|
||||
else
|
||||
updated_highlight_beginning = self.ui.document:getNextVisibleWordStart(highlight_beginning)
|
||||
end
|
||||
else -- move highlight to the left
|
||||
else -- move highlight to the left
|
||||
if move_by_char then
|
||||
updated_highlight_beginning = self.ui.document:getPrevVisibleChar(highlight_beginning)
|
||||
else
|
||||
updated_highlight_beginning = self.ui.document:getPrevVisibleWordStart(highlight_beginning)
|
||||
end
|
||||
end
|
||||
if updated_highlight_beginning then
|
||||
local order = self.ui.document:compareXPointers(updated_highlight_beginning, highlight_end)
|
||||
if order and order > 0 then -- only if beginning did not go past end
|
||||
highlight.pos0 = updated_highlight_beginning
|
||||
highlight.page = updated_highlight_beginning
|
||||
highlight.chapter = self.ui.toc:getTocTitleByPage(updated_highlight_beginning)
|
||||
highlight.pageno = self.document:getPageFromXPointer(updated_highlight_beginning)
|
||||
end
|
||||
end
|
||||
if updated_highlight_beginning == nil then return end
|
||||
local order = self.ui.document:compareXPointers(updated_highlight_beginning, highlight_end)
|
||||
if order == nil or order <= 0 then return end -- only if beginning did not go past end
|
||||
highlight.pos0 = updated_highlight_beginning
|
||||
highlight.page = updated_highlight_beginning
|
||||
highlight.chapter = self.ui.toc:getTocTitleByPage(updated_highlight_beginning)
|
||||
highlight.pageno = self.document:getPageFromXPointer(updated_highlight_beginning)
|
||||
else -- we move pos1
|
||||
local updated_highlight_end
|
||||
if direction == 1 then -- move highlight to the right
|
||||
@@ -1071,19 +1086,15 @@ function ReaderHighlight:updateHighlight(index, side, direction, move_by_char)
|
||||
updated_highlight_end = self.ui.document:getPrevVisibleWordEnd(highlight_end)
|
||||
end
|
||||
end
|
||||
if updated_highlight_end then
|
||||
local order = self.ui.document:compareXPointers(highlight_beginning, updated_highlight_end)
|
||||
if order and order > 0 then -- only if end did not go back past beginning
|
||||
highlight.pos1 = updated_highlight_end
|
||||
end
|
||||
end
|
||||
if updated_highlight_end == nil then return end
|
||||
local order = self.ui.document:compareXPointers(highlight_beginning, updated_highlight_end)
|
||||
if order == nil or order <= 0 then return end -- only if end did not go back past beginning
|
||||
highlight.pos1 = updated_highlight_end
|
||||
end
|
||||
|
||||
local new_beginning = highlight.pos0
|
||||
local new_end = highlight.pos1
|
||||
local new_text = self.ui.document:getTextFromXPointers(new_beginning, new_end)
|
||||
highlight.text = util.cleanupSelectedText(new_text)
|
||||
self.ui:handleEvent(Event:new("AnnotationsModified", { highlight, highlight_before }))
|
||||
highlight.text = self.ui.document:getTextFromXPointers(new_beginning, new_end)
|
||||
if side == 0 then
|
||||
-- Ensure we show the page with the new beginning of highlight
|
||||
if not self.ui.document:isXPointerInCurrentPage(new_beginning) then
|
||||
@@ -1104,7 +1115,147 @@ function ReaderHighlight:updateHighlight(index, side, direction, move_by_char)
|
||||
end
|
||||
end
|
||||
end
|
||||
UIManager:setDirty(self.dialog, "ui")
|
||||
return true
|
||||
end
|
||||
|
||||
function ReaderHighlight:updateHighlightPaging(highlight, side, direction)
|
||||
local page = self.ui.paging.current_page
|
||||
local pboxes
|
||||
if highlight.ext then -- multipage highlight, don't move invisible boundaries
|
||||
if (page ~= highlight.pos0.page and page ~= highlight.pos1.page ) or -- middle pages
|
||||
(page == highlight.pos0.page and side == 1) or -- first page, tried to move end
|
||||
(page == highlight.pos1.page and side == 0) then -- last page, tried to move start
|
||||
return
|
||||
end
|
||||
pboxes = highlight.ext[page].pboxes
|
||||
else
|
||||
pboxes = highlight.pboxes
|
||||
end
|
||||
local page_boxes = self.document:getTextBoxes(page)
|
||||
|
||||
-- find page boxes indices of the highlight start and end pboxes
|
||||
-- pboxes { x, y, h, w }; page_boxes { x0, y0, x1, y1, word }
|
||||
local start_i, start_j, end_i, end_j
|
||||
local function is_equal(a, b)
|
||||
return math.abs(a - b) < 0.001
|
||||
end
|
||||
local start_box = pboxes[1]
|
||||
local end_box = pboxes[#pboxes]
|
||||
for i, line in ipairs(page_boxes) do
|
||||
for j, box in ipairs(line) do
|
||||
if not start_i and is_equal(start_box.x, box.x0) and is_equal(start_box.y, box.y0) then
|
||||
start_i, start_j = i, j
|
||||
end
|
||||
if not end_i and is_equal(end_box.x + end_box.w, box.x1) and is_equal(end_box.y, box.y0) then
|
||||
end_i, end_j = i, j
|
||||
end
|
||||
if start_i and end_i then break end
|
||||
end
|
||||
if start_i and end_i then break end
|
||||
end
|
||||
if not (start_i and end_i) then return end
|
||||
|
||||
-- move
|
||||
local new_start_i, new_start_j, new_end_i, new_end_j
|
||||
if side == 0 then -- we move pos0
|
||||
new_end_i, new_end_j = end_i, end_j
|
||||
if direction == 1 then -- move highlight to the right
|
||||
if start_i == end_i and start_j == end_j then return end -- don't move start behind end
|
||||
if start_j == #page_boxes[start_i] then -- last box of the line
|
||||
new_start_i = start_i + 1
|
||||
new_start_j = 1
|
||||
table.remove(pboxes, 1)
|
||||
else
|
||||
new_start_i = start_i
|
||||
new_start_j = start_j + 1
|
||||
pboxes[1].x = page_boxes[new_start_i][new_start_j].x0
|
||||
local last_box_j = new_start_i == new_end_i and new_end_j or #page_boxes[new_start_i]
|
||||
local last_box = page_boxes[new_start_i][last_box_j] -- last highlighted box of the line
|
||||
pboxes[1].w = last_box.x1 - pboxes[1].x
|
||||
end
|
||||
local removed_word = page_boxes[start_i][start_j].word
|
||||
if removed_word then
|
||||
highlight.text = highlight.text:sub(#removed_word + 2) -- remove first word and space after it
|
||||
end
|
||||
else -- move highlight to the left
|
||||
local new_box
|
||||
if start_j == 1 then -- first box of the line
|
||||
if start_i == 1 then return end -- first line of the page, don't move to the previous page
|
||||
new_start_i = start_i - 1
|
||||
new_start_j = #page_boxes[new_start_i]
|
||||
new_box = page_boxes[new_start_i][new_start_j]
|
||||
table.insert(pboxes, 1, { x = new_box.x0, y = new_box.y0, w = new_box.x1 - new_box.x0, h = new_box.y1 - new_box.y0 })
|
||||
else
|
||||
new_start_i = start_i
|
||||
new_start_j = start_j - 1
|
||||
new_box = page_boxes[new_start_i][new_start_j]
|
||||
pboxes[1].x = new_box.x0
|
||||
local last_box_j = new_start_i == new_end_i and new_end_j or #page_boxes[new_start_i]
|
||||
local last_box = page_boxes[new_start_i][last_box_j] -- last highlighted box of the line
|
||||
pboxes[1].w = last_box.x1 - pboxes[1].x
|
||||
end
|
||||
if new_box.word then
|
||||
highlight.text = new_box.word .. " " .. highlight.text
|
||||
end
|
||||
end
|
||||
else -- we move pos1
|
||||
new_start_i, new_start_j = start_i, start_j
|
||||
if direction == 1 then -- move highlight to the right
|
||||
local new_box
|
||||
if end_j == #page_boxes[end_i] then -- last box of the line
|
||||
if end_i == #page_boxes then return end -- last line of the page, don't move to the next page
|
||||
new_end_i = end_i + 1
|
||||
new_end_j = 1
|
||||
new_box = page_boxes[new_end_i][new_end_j]
|
||||
table.insert(pboxes, { x = new_box.x0, y = new_box.y0, w = new_box.x1 - new_box.x0, h = new_box.y1 - new_box.y0 })
|
||||
else
|
||||
new_end_i = end_i
|
||||
new_end_j = end_j + 1
|
||||
new_box = page_boxes[new_end_i][new_end_j]
|
||||
pboxes[#pboxes].w = new_box.x1 - pboxes[#pboxes].x
|
||||
end
|
||||
if new_box.word then
|
||||
highlight.text = highlight.text .. " " .. new_box.word
|
||||
end
|
||||
else -- move highlight to the left
|
||||
if start_i == end_i and start_j == end_j then return end -- don't move end before start
|
||||
if end_j == 1 then -- first box of the line
|
||||
new_end_i = end_i - 1
|
||||
new_end_j = #page_boxes[new_end_i]
|
||||
table.remove(pboxes)
|
||||
else
|
||||
new_end_i = end_i
|
||||
new_end_j = end_j - 1
|
||||
local last_box = page_boxes[new_end_i][new_end_j] -- last highlighted box of the line
|
||||
pboxes[#pboxes].w = last_box.x1 - pboxes[#pboxes].x
|
||||
end
|
||||
local removed_word = page_boxes[end_i][end_j].word
|
||||
if removed_word then
|
||||
highlight.text = highlight.text:sub(1, -(#removed_word + 2)) -- remove last word and space before it
|
||||
end
|
||||
end
|
||||
end
|
||||
start_box, end_box = page_boxes[new_start_i][new_start_j], page_boxes[new_end_i][new_end_j]
|
||||
if highlight.ext then -- multipage highlight
|
||||
if side == 0 then -- we move pos0
|
||||
highlight.pos0.x = (start_box.x0 + start_box.x1) / 2
|
||||
highlight.pos0.y = (start_box.y0 + start_box.y1) / 2
|
||||
highlight.ext[page].pos0.x = highlight.pos0.x
|
||||
highlight.ext[page].pos0.y = highlight.pos0.y
|
||||
else
|
||||
highlight.pos1.x = (end_box.x0 + end_box.x1) / 2
|
||||
highlight.pos1.y = (end_box.y0 + end_box.y1) / 2
|
||||
highlight.ext[page].pos1.x = highlight.pos1.x
|
||||
highlight.ext[page].pos1.y = highlight.pos1.y
|
||||
end
|
||||
else
|
||||
-- pos0 and pos1 may be not in order, reassign all
|
||||
highlight.pos0.x = (start_box.x0 + start_box.x1) / 2
|
||||
highlight.pos0.y = (start_box.y0 + start_box.y1) / 2
|
||||
highlight.pos1.x = (end_box.x0 + end_box.x1) / 2
|
||||
highlight.pos1.y = (end_box.y0 + end_box.y1) / 2
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function ReaderHighlight:showChooseHighlightDialog(highlights)
|
||||
@@ -1197,6 +1348,13 @@ end
|
||||
|
||||
function ReaderHighlight:showHighlightDialog(index)
|
||||
local item = self.ui.annotation.annotations[index]
|
||||
local change_boundaries_enabled = not item.text_edited
|
||||
local start_prev, start_next, end_prev, end_next = "◁▒▒", "▷▒▒", "▒▒◁", "▒▒▷"
|
||||
if BD.mirroredUILayout() then
|
||||
-- BiDi will mirror the arrows, and this just works
|
||||
start_prev, start_next = start_next, start_prev
|
||||
end_prev, end_next = end_next, end_prev
|
||||
end
|
||||
local edit_highlight_dialog
|
||||
local buttons = {
|
||||
{
|
||||
@@ -1245,23 +1403,10 @@ function ReaderHighlight:showHighlightDialog(index)
|
||||
end,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if self.ui.rolling then
|
||||
local enabled = not item.text_edited
|
||||
local start_prev = "◁▒▒"
|
||||
local start_next = "▷▒▒"
|
||||
local end_prev = "▒▒◁"
|
||||
local end_next = "▒▒▷"
|
||||
if BD.mirroredUILayout() then
|
||||
-- BiDi will mirror the arrows, and this just works
|
||||
start_prev, start_next = start_next, start_prev
|
||||
end_prev, end_next = end_next, end_prev
|
||||
end
|
||||
table.insert(buttons, {
|
||||
{
|
||||
{
|
||||
text = start_prev,
|
||||
enabled = enabled,
|
||||
enabled = change_boundaries_enabled,
|
||||
callback = function()
|
||||
self:updateHighlight(index, 0, -1, false)
|
||||
end,
|
||||
@@ -1271,7 +1416,7 @@ function ReaderHighlight:showHighlightDialog(index)
|
||||
},
|
||||
{
|
||||
text = start_next,
|
||||
enabled = enabled,
|
||||
enabled = change_boundaries_enabled,
|
||||
callback = function()
|
||||
self:updateHighlight(index, 0, 1, false)
|
||||
end,
|
||||
@@ -1281,7 +1426,7 @@ function ReaderHighlight:showHighlightDialog(index)
|
||||
},
|
||||
{
|
||||
text = end_prev,
|
||||
enabled = enabled,
|
||||
enabled = change_boundaries_enabled,
|
||||
callback = function()
|
||||
self:updateHighlight(index, 1, -1, false)
|
||||
end,
|
||||
@@ -1291,16 +1436,16 @@ function ReaderHighlight:showHighlightDialog(index)
|
||||
},
|
||||
{
|
||||
text = end_next,
|
||||
enabled = enabled,
|
||||
enabled = change_boundaries_enabled,
|
||||
callback = function()
|
||||
self:updateHighlight(index, 1, 1, false)
|
||||
end,
|
||||
hold_callback = function()
|
||||
self:updateHighlight(index, 1, 1, true)
|
||||
end,
|
||||
}
|
||||
})
|
||||
end
|
||||
},
|
||||
},
|
||||
}
|
||||
edit_highlight_dialog = ButtonDialog:new{
|
||||
name = "edit_highlight_dialog", -- for unit tests
|
||||
buttons = buttons,
|
||||
@@ -1522,10 +1667,23 @@ function ReaderHighlight:onHold(arg, ges)
|
||||
-- use text selections throughout readerhighlight in order to allow the
|
||||
-- highlight to be corrected by language-specific plugins more easily.
|
||||
self.is_word_selection = true
|
||||
local pos = word.pos
|
||||
self.selected_text = {
|
||||
text = word.word or "",
|
||||
pos0 = word.pos0 or word.pos,
|
||||
pos1 = word.pos1 or word.pos,
|
||||
pos0 = word.pos0 or {
|
||||
page = pos.page,
|
||||
rotation = pos.rotation,
|
||||
x = pos.x,
|
||||
y = pos.y,
|
||||
zoom = pos.zoom,
|
||||
},
|
||||
pos1 = word.pos1 or {
|
||||
page = pos.page,
|
||||
rotation = pos.rotation,
|
||||
x = pos.x,
|
||||
y = pos.y,
|
||||
zoom = pos.zoom,
|
||||
},
|
||||
sboxes = word.sbox and { word.sbox },
|
||||
pboxes = word.pbox and { word.pbox },
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user