Highlights: edit boundaries in pdf (#13364)

This commit is contained in:
hius07
2025-03-09 07:44:43 +02:00
committed by GitHub
parent 70d13be32a
commit a06be7636a

View File

@@ -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 },
}