Annotations, part 1 (#11563)

New format to handle annotations (page bookmarks, highlights, notes) and store them in the book metadata files.
reviewable/pr11748/r1
hius07 1 month ago committed by GitHub
parent 6b0d97bf22
commit d82815952e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -73,11 +73,18 @@ end
-- Purge doc settings except kept -- Purge doc settings except kept
function filemanagerutil.resetDocumentSettings(file) function filemanagerutil.resetDocumentSettings(file)
local settings_to_keep = { local settings_to_keep = {
annotations = true,
annotations_paging = true,
annotations_rolling = true,
bookmarks = true, bookmarks = true,
bookmarks_paging = true,
bookmarks_rolling = true,
bookmarks_sorted_20220106 = true, bookmarks_sorted_20220106 = true,
bookmarks_version = true, bookmarks_version = true,
cre_dom_version = true, cre_dom_version = true,
highlight = true, highlight = true,
highlight_paging = true,
highlight_rolling = true,
highlights_imported = true, highlights_imported = true,
last_page = true, last_page = true,
last_xpointer = true, last_xpointer = true,

@ -0,0 +1,425 @@
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local logger = require("logger")
local _ = require("gettext")
local T = require("ffi/util").template
local ReaderAnnotation = WidgetContainer:extend{
annotations = nil, -- array sorted by annotation position order, ascending
}
-- build, read, save
function ReaderAnnotation:buildAnnotation(bm, highlights, init)
-- bm: associated single bookmark ; highlights: tables with all highlights
local note = bm.text
if note == "" then
note = nil
end
local chapter = bm.chapter
local hl, pageno = self:getHighlightByDatetime(highlights, bm.datetime)
if init then
if note and self.ui.bookmark:isBookmarkAutoText(bm) then
note = nil
end
if chapter == nil then
chapter = self.ui.toc:getTocTitleByPage(bm.page)
end
pageno = self.ui.paging and bm.page or self.document:getPageFromXPointer(bm.page)
end
if not hl then -- page bookmark or orphaned bookmark
hl = {}
if bm.highlighted then -- orphaned bookmark
hl.drawer = self.view.highlight.saved_drawer
hl.color = self.view.highlight.saved_color
if self.ui.paging then
if bm.pos0.page == bm.pos1.page then
hl.pboxes = self.document:getPageBoxesFromPositions(bm.page, bm.pos0, bm.pos1)
else -- multi-page highlight, restore the first box only
hl.pboxes = self.document:getPageBoxesFromPositions(bm.page, bm.pos0, bm.pos0)
end
end
end
end
if self.ui.paging then
-- old single-page reflow highlights do not have page in position
if not bm.pos0.page then
bm.pos0.page = bm.page
bm.pos1.page = bm.page
end
end
return { -- annotation
datetime = bm.datetime, -- creation time, not changeable
drawer = hl.drawer, -- highlight drawer
color = hl.color, -- highlight color
text = bm.notes, -- highlighted text, editable
text_edited = hl.edited, -- true if highlighted text has been edited
note = note, -- user's note, editable
chapter = chapter, -- book chapter title
pageno = pageno, -- book page number
page = bm.page, -- highlight location, xPointer or number (pdf)
pos0 = bm.pos0, -- highlight start position, xPointer (== page) or table (pdf)
pos1 = bm.pos1, -- highlight end position, xPointer or table (pdf)
pboxes = hl.pboxes, -- pdf pboxes, used only and changeable by addMarkupAnnotation
ext = hl.ext, -- pdf multi-page highlight
}
end
function ReaderAnnotation:getHighlightByDatetime(highlights, datetime)
for pageno, page_highlights in pairs(highlights) do
for _, highlight in ipairs(page_highlights) do
if highlight.datetime == datetime then
return highlight, pageno
end
end
end
end
function ReaderAnnotation:getAnnotationsFromBookmarksHighlights(bookmarks, highlights, init)
local annotations = {}
for i = #bookmarks, 1, -1 do
table.insert(annotations, self:buildAnnotation(bookmarks[i], highlights, init))
end
if init then
self:sortItems(annotations)
end
return annotations
end
function ReaderAnnotation:onReadSettings(config)
local annotations = config:readSetting("annotations")
if annotations then
-- KOHighlights may set this key when it has merged annotations from different sources:
-- we want to make sure they are updated and sorted
local needs_update = config:isTrue("annotations_externally_modified")
local needs_sort -- if incompatible annotations were built of old highlights/bookmarks
-- Annotation formats in crengine and mupdf are incompatible.
local has_annotations = #annotations > 0
local annotations_type = has_annotations and type(annotations[1].page)
if self.ui.rolling and annotations_type ~= "string" then -- incompatible format loaded, or empty
if has_annotations then -- backup incompatible format if not empty
config:saveSetting("annotations_paging", annotations)
end
-- load compatible format
annotations = config:readSetting("annotations_rolling") or {}
config:delSetting("annotations_rolling")
needs_sort = true
elseif self.ui.paging and annotations_type ~= "number" then
if has_annotations then
config:saveSetting("annotations_rolling", annotations)
end
annotations = config:readSetting("annotations_paging") or {}
config:delSetting("annotations_paging")
needs_sort = true
end
self.annotations = annotations
if needs_update or needs_sort then
if self.ui.rolling then
self.ui:registerPostInitCallback(function()
self:updatedAnnotations(needs_update, needs_sort)
end)
else
self:updatedAnnotations(needs_update, needs_sort)
end
config:delSetting("annotations_externally_modified")
end
else -- first run
if self.ui.rolling then
self.ui:registerPostInitCallback(function()
self:migrateToAnnotations(config)
end)
else
self:migrateToAnnotations(config)
end
end
end
function ReaderAnnotation:migrateToAnnotations(config)
local bookmarks = config:readSetting("bookmarks") or {}
local highlights = config:readSetting("highlight") or {}
if config:hasNot("highlights_imported") then
-- before 2014, saved highlights were not added to bookmarks when they were created.
for page, hls in pairs(highlights) do
for _, hl in ipairs(hls) do
local hl_page = self.ui.paging and page or hl.pos0
-- highlights saved by some old versions don't have pos0 field
-- we just ignore those highlights
if hl_page then
local item = {
datetime = hl.datetime,
highlighted = true,
notes = hl.text,
page = hl_page,
pos0 = hl.pos0,
pos1 = hl.pos1,
}
if self.ui.paging then
item.pos0.page = page
item.pos1.page = page
end
table.insert(bookmarks, item)
end
end
end
end
-- Bookmarks/highlights formats in crengine and mupdf are incompatible.
local has_bookmarks = #bookmarks > 0
local bookmarks_type = has_bookmarks and type(bookmarks[1].page)
if self.ui.rolling then
if bookmarks_type == "string" then -- compatible format loaded, check for incompatible old backup
if config:has("bookmarks_paging") then -- save incompatible old backup
local bookmarks_paging = config:readSetting("bookmarks_paging")
local highlights_paging = config:readSetting("highlight_paging")
local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks_paging, highlights_paging)
config:saveSetting("annotations_paging", annotations)
config:delSetting("bookmarks_paging")
config:delSetting("highlight_paging")
end
else -- incompatible format loaded, or empty
if has_bookmarks then -- save incompatible format if not empty
local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights)
config:saveSetting("annotations_paging", annotations)
end
-- load compatible format
bookmarks = config:readSetting("bookmarks_rolling") or {}
highlights = config:readSetting("highlight_rolling") or {}
config:delSetting("bookmarks_rolling")
config:delSetting("highlight_rolling")
end
else -- self.ui.paging
if bookmarks_type == "number" then
if config:has("bookmarks_rolling") then
local bookmarks_rolling = config:readSetting("bookmarks_rolling")
local highlights_rolling = config:readSetting("highlight_rolling")
local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks_rolling, highlights_rolling)
config:saveSetting("annotations_rolling", annotations)
config:delSetting("bookmarks_rolling")
config:delSetting("highlight_rolling")
end
else
if has_bookmarks then
local annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights)
config:saveSetting("annotations_rolling", annotations)
end
bookmarks = config:readSetting("bookmarks_paging") or {}
highlights = config:readSetting("highlight_paging") or {}
config:delSetting("bookmarks_paging")
config:delSetting("highlight_paging")
end
end
self.annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights, true)
end
function ReaderAnnotation:onDocumentRerendered()
self.needs_update = true
end
function ReaderAnnotation:onCloseDocument()
self:updatePageNumbers()
end
function ReaderAnnotation:onSaveSettings()
self:updatePageNumbers()
self.ui.doc_settings:saveSetting("annotations", self.annotations)
end
-- items handling
function ReaderAnnotation:updatePageNumbers()
if self.needs_update and self.ui.rolling then -- triggered by ReaderRolling on document layout change
for _, item in ipairs(self.annotations) do
item.pageno = self.document:getPageFromXPointer(item.page)
end
end
self.needs_update = nil
end
function ReaderAnnotation:sortItems(items)
if #items > 1 then
local sort_func = self.ui.rolling and function(a, b) return self:isItemInPositionOrderRolling(a, b) end
or function(a, b) return self:isItemInPositionOrderPaging(a, b) end
table.sort(items, sort_func)
end
end
function ReaderAnnotation:updatedAnnotations(needs_update, needs_sort)
if needs_update then
self.needs_update = true
self:updatePageNumbers()
needs_sort = true
end
if needs_sort then
self:sortItems(self.annotations)
end
end
function ReaderAnnotation:updateItemByXPointer(item)
-- called by ReaderRolling:checkXPointersAndProposeDOMVersionUpgrade()
local chapter = self.ui.toc:getTocTitleByPage(item.page)
if chapter == "" then
chapter = nil
end
if not item.drawer then -- page bookmark
item.text = chapter and T(_("in %1"), chapter) or nil
end
item.chapter = chapter
item.pageno = self.document:getPageFromXPointer(item.page)
end
function ReaderAnnotation:isItemInPositionOrderRolling(a, b)
local a_page = self.document:getPageFromXPointer(a.page)
local b_page = self.document:getPageFromXPointer(b.page)
if a_page == b_page then -- both items in the same page
if a.drawer and b.drawer then -- both items are highlights, compare positions
local compare_xp = self.document:compareXPointers(a.page, b.page)
if compare_xp then
if compare_xp == 0 then -- both highlights with the same start, compare ends
compare_xp = self.document:compareXPointers(a.pos1, b.pos1)
if compare_xp then
return compare_xp > 0
end
logger.warn("Invalid xpointer in highlight:", a.pos1, b.pos1)
return true
end
return compare_xp > 0
end
-- if compare_xp is nil, some xpointer is invalid and "a" will be sorted first to page 1
logger.warn("Invalid xpointer in highlight:", a.page, b.page)
return true
end
return not a.drawer -- have page bookmarks before highlights
end
return a_page < b_page
end
function ReaderAnnotation:isItemInPositionOrderPaging(a, b)
if a.page == b.page then -- both items in the same page
if a.drawer and b.drawer then -- both items are highlights, compare positions
local is_reflow = self.document.configurable.text_wrap -- save reflow mode
self.document.configurable.text_wrap = 0 -- native positions
-- sort start and end positions of each highlight
local a_start, a_end, b_start, b_end, result
if self.document:comparePositions(a.pos0, a.pos1) > 0 then
a_start, a_end = a.pos0, a.pos1
else
a_start, a_end = a.pos1, a.pos0
end
if self.document:comparePositions(b.pos0, b.pos1) > 0 then
b_start, b_end = b.pos0, b.pos1
else
b_start, b_end = b.pos1, b.pos0
end
-- compare start positions
local compare_pos = self.document:comparePositions(a_start, b_start)
if compare_pos == 0 then -- both highlights with the same start, compare ends
result = self.document:comparePositions(a_end, b_end) > 0
else
result = compare_pos > 0
end
self.document.configurable.text_wrap = is_reflow -- restore reflow mode
return result
end
return not a.drawer -- have page bookmarks before highlights
end
return a.page < b.page
end
function ReaderAnnotation:getItemIndex(item, no_binary)
local doesMatch
if item.datetime then
doesMatch = function(a, b)
return a.datetime == b.datetime
end
else
if self.ui.rolling then
doesMatch = function(a, b)
if a.text ~= b.text or a.pos0 ~= b.pos0 or a.pos1 ~= b.pos1 then
return false
end
return true
end
else
doesMatch = function(a, b)
if a.text ~= b.text or a.pos0.page ~= b.pos0.page
or a.pos0.x ~= b.pos0.x or a.pos1.x ~= b.pos1.x
or a.pos0.y ~= b.pos0.y or a.pos1.y ~= b.pos1.y then
return false
end
return true
end
end
end
if not no_binary then
local isInOrder = self.ui.rolling and self.isItemInPositionOrderRolling or self.isItemInPositionOrderPaging
local _start, _end, _middle = 1, #self.annotations
while _start <= _end do
_middle = bit.rshift(_start + _end, 1)
local v = self.annotations[_middle]
if doesMatch(item, v) then
return _middle
elseif isInOrder(self, item, v) then
_end = _middle - 1
else
_start = _middle + 1
end
end
end
for i, v in ipairs(self.annotations) do
if doesMatch(item, v) then
return i
end
end
end
function ReaderAnnotation:getInsertionIndex(item)
local isInOrder = self.ui.rolling and self.isItemInPositionOrderRolling or self.isItemInPositionOrderPaging
local _start, _end, _middle, direction = 1, #self.annotations, 1, 0
while _start <= _end do
_middle = bit.rshift(_start + _end, 1)
if isInOrder(self, item, self.annotations[_middle]) then
_end, direction = _middle - 1, 0
else
_start, direction = _middle + 1, 1
end
end
return _middle + direction
end
function ReaderAnnotation:addItem(item)
item.datetime = os.date("%Y-%m-%d %H:%M:%S")
item.pageno = self.ui.paging and item.page or self.document:getPageFromXPointer(item.page)
local index = self:getInsertionIndex(item)
table.insert(self.annotations, index, item)
return index
end
-- info
function ReaderAnnotation:hasAnnotations()
return #self.annotations > 0
end
function ReaderAnnotation:getNumberOfAnnotations()
return #self.annotations
end
function ReaderAnnotation:getNumberOfHighlightsAndNotes() -- for Statistics plugin
local highlights = 0
local notes = 0
for _, item in ipairs(self.annotations) do
if item.drawer then
if item.note then
notes = notes + 1
else
highlights = highlights + 1
end
end
end
return highlights, notes
end
return ReaderAnnotation

File diff suppressed because it is too large Load Diff

@ -219,7 +219,7 @@ local footerTextGeneratorMap = {
bookmark_count = function(footer) bookmark_count = function(footer)
local symbol_type = footer.settings.item_prefix local symbol_type = footer.settings.item_prefix
local prefix = symbol_prefix[symbol_type].bookmark_count local prefix = symbol_prefix[symbol_type].bookmark_count
local bookmark_count = footer.ui.bookmark:getNumberOfBookmarks() local bookmark_count = footer.ui.annotation:getNumberOfAnnotations()
if footer.settings.all_at_once and footer.settings.hide_empty_generators and bookmark_count == 0 then if footer.settings.all_at_once and footer.settings.hide_empty_generators and bookmark_count == 0 then
return "" return ""
end end

@ -7,6 +7,7 @@ local Geom = require("ui/geometry")
local InfoMessage = require("ui/widget/infomessage") local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer") local InputContainer = require("ui/widget/container/inputcontainer")
local Notification = require("ui/widget/notification") local Notification = require("ui/widget/notification")
local RadioButtonWidget = require("ui/widget/radiobuttonwidget")
local TextViewer = require("ui/widget/textviewer") local TextViewer = require("ui/widget/textviewer")
local Translator = require("ui/translator") local Translator = require("ui/translator")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
@ -73,11 +74,11 @@ function ReaderHighlight:init()
["02_highlight"] = function(this) ["02_highlight"] = function(this)
return { return {
text = _("Highlight"), text = _("Highlight"),
enabled = this.hold_pos ~= nil,
callback = function() callback = function()
this:saveHighlight(true) this:saveHighlight(true)
this:onClose() this:onClose()
end, end,
enabled = this.hold_pos ~= nil,
} }
end, end,
["03_copy"] = function(this) ["03_copy"] = function(this)
@ -96,11 +97,11 @@ function ReaderHighlight:init()
["04_add_note"] = function(this) ["04_add_note"] = function(this)
return { return {
text = _("Add note"), text = _("Add note"),
enabled = this.hold_pos ~= nil,
callback = function() callback = function()
this:addNote() this:addNote()
this:onClose() this:onClose()
end, end,
enabled = this.hold_pos ~= nil,
} }
end, end,
-- then information lookup functions, putting on the left those that -- then information lookup functions, putting on the left those that
@ -127,11 +128,11 @@ function ReaderHighlight:init()
end, end,
} }
end, end,
["07_translate"] = function(this, page, index) ["07_translate"] = function(this, index)
return { return {
text = _("Translate"), text = _("Translate"),
callback = function() callback = function()
this:translate(this.selected_text, page, index) this:translate(index)
-- We don't call this:onClose(), so one can still see -- We don't call this:onClose(), so one can still see
-- the highlighted text when moving the translated -- the highlighted text when moving the translated
-- text window, and also if NetworkMgr:promptWifiOn() -- text window, and also if NetworkMgr:promptWifiOn()
@ -429,7 +430,7 @@ function ReaderHighlight:addToMainMenu(menu_items)
self.view.highlight.lighten_factor = spin.value self.view.highlight.lighten_factor = spin.value
UIManager:setDirty(self.dialog, "ui") UIManager:setDirty(self.dialog, "ui")
if touchmenu_instance then touchmenu_instance:updateItems() end if touchmenu_instance then touchmenu_instance:updateItems() end
end end,
} }
UIManager:show(spin_widget) UIManager:show(spin_widget)
end, end,
@ -455,7 +456,7 @@ function ReaderHighlight:addToMainMenu(menu_items)
}, },
}) })
end end
UIManager:show(require("ui/widget/radiobuttonwidget"):new{ UIManager:show(RadioButtonWidget:new{
title_text = _("Note marker"), title_text = _("Note marker"),
width_factor = 0.5, width_factor = 0.5,
keep_shown_on_apply = true, keep_shown_on_apply = true,
@ -475,7 +476,7 @@ function ReaderHighlight:addToMainMenu(menu_items)
}) })
end, end,
}) })
if self.document.info.has_pages then if self.ui.paging then
menu_items.panel_zoom_options = { menu_items.panel_zoom_options = {
text = _("Panel zoom (manga/comic)"), text = _("Panel zoom (manga/comic)"),
sub_item_table = self:genPanelZoomMenu(), sub_item_table = self:genPanelZoomMenu(),
@ -563,7 +564,7 @@ function ReaderHighlight:addToMainMenu(menu_items)
callback = function(spin) callback = function(spin)
G_reader_settings:saveSetting("highlight_long_hold_threshold_s", spin.value) G_reader_settings:saveSetting("highlight_long_hold_threshold_s", spin.value)
if touchmenu_instance then touchmenu_instance:updateItems() end if touchmenu_instance then touchmenu_instance:updateItems() end
end end,
} }
UIManager:show(items) UIManager:show(items)
end, end,
@ -671,6 +672,7 @@ function ReaderHighlight:clear(clear_id)
end end
self.is_word_selection = false self.is_word_selection = false
self.selected_text_start_xpointer = nil self.selected_text_start_xpointer = nil
self.gest_pos = nil
if self.hold_pos then if self.hold_pos then
self.hold_pos = nil self.hold_pos = nil
self.selected_text = nil self.selected_text = nil
@ -693,8 +695,8 @@ function ReaderHighlight:onTapSelectModeIcon()
cancel_text = _("Close"), cancel_text = _("Close"),
ok_callback = function() ok_callback = function()
self.select_mode = false self.select_mode = false
self:deleteHighlight(self.highlight_page, self.highlight_idx) self:deleteHighlight(self.highlight_idx)
end end,
}) })
return true return true
end end
@ -706,7 +708,7 @@ function ReaderHighlight:onTap(_, ges)
-- ReaderHighlight:clear can only return true if self.hold_pos was set anyway. -- ReaderHighlight:clear can only return true if self.hold_pos was set anyway.
local cleared = self.hold_pos and self:clear() local cleared = self.hold_pos and self:clear()
-- We only care about potential taps on existing highlights, not on taps that closed a highlight menu. -- We only care about potential taps on existing highlights, not on taps that closed a highlight menu.
if not cleared and ges then if not cleared and ges and self.ui.annotation:hasAnnotations() then
if self.ui.paging then if self.ui.paging then
return self:onTapPageSavedHighlight(ges) return self:onTapPageSavedHighlight(ges)
else else
@ -720,31 +722,24 @@ function ReaderHighlight:onTapPageSavedHighlight(ges)
local pos = self.view:screenToPageTransform(ges.pos) local pos = self.view:screenToPageTransform(ges.pos)
local highlights_tapped = {} local highlights_tapped = {}
for _, page in ipairs(pages) do for _, page in ipairs(pages) do
local items = self.view:getPageSavedHighlights(page) local items = self:getPageSavedHighlights(page)
if items then for i, item in ipairs(items) do
for i, item in ipairs(items) do local boxes = self.ui.document:getPageBoxesFromPositions(page, item.pos0, item.pos1)
local boxes = self.ui.document:getPageBoxesFromPositions(page, item.pos0, item.pos1) if boxes then
if boxes then for __, box in ipairs(boxes) do
for _, box in ipairs(boxes) do if inside_box(pos, box) then
if inside_box(pos, box) then logger.dbg("Tap on highlight")
logger.dbg("Tap on highlight") local hl_i = item.parent or i -- parent exists in multi-page highlight only
local hl_page, hl_i if self.select_mode then
if item.parent then -- multi-page highlight if hl_i == self.highlight_idx then
hl_page, hl_i = unpack(item.parent) -- tap on the first fragment: abort select mode, clear highlight
else self.select_mode = false
hl_page, hl_i = page, i self:deleteHighlight(hl_i)
end return true
if self.select_mode then
if hl_page == self.highlight_page and hl_i == self.highlight_idx then
-- tap on the first fragment: abort select mode, clear highlight
self.select_mode = false
self:deleteHighlight(hl_page, hl_i)
return true
end
else
table.insert(highlights_tapped, {hl_page, hl_i})
break
end end
else
table.insert(highlights_tapped, hl_i)
break
end end
end end
end end
@ -761,7 +756,6 @@ function ReaderHighlight:onTapXPointerSavedHighlight(ges)
-- showing menu...). We might want to cache these boxes per page (and -- showing menu...). We might want to cache these boxes per page (and
-- clear that cache when page layout change or highlights are added -- clear that cache when page layout change or highlights are added
-- or removed). -- or removed).
local cur_view_top, cur_view_bottom
local pos = self.view:screenToPageTransform(ges.pos) local pos = self.view:screenToPageTransform(ges.pos)
-- NOTE: By now, pos.page is set, but if a highlight spans across multiple pages, -- NOTE: By now, pos.page is set, but if a highlight spans across multiple pages,
-- it's stored under the hash of its *starting* point, -- it's stored under the hash of its *starting* point,
@ -770,46 +764,39 @@ function ReaderHighlight:onTapXPointerSavedHighlight(ges)
-- because pos.page isn't super accurate in continuous mode -- because pos.page isn't super accurate in continuous mode
-- (it's the page number for what's it the topleft corner of the screen, -- (it's the page number for what's it the topleft corner of the screen,
-- i.e., often a bit earlier)... -- i.e., often a bit earlier)...
-- Even in page mode, it's safer to use pos and ui.dimen.h
-- than pages' xpointers pos, even if ui.dimen.h is a bit
-- larger than pages' heights
local cur_view_top = self.document:getCurrentPos()
local cur_view_bottom
if self.view.view_mode == "page" and self.document:getVisiblePageCount() > 1 then
cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h
else
cur_view_bottom = cur_view_top + self.ui.dimen.h
end
local highlights_tapped = {} local highlights_tapped = {}
for page, items in pairs(self.view.highlight.saved) do for hl_i, item in ipairs(self.ui.annotation.annotations) do
if items then if item.drawer then
for i = 1, #items do -- document:getScreenBoxesFromPositions() is expensive, so we
local item = items[i] -- first check this item is on current page
local pos0, pos1 = item.pos0, item.pos1 local start_pos = self.document:getPosFromXPointer(item.pos0)
-- document:getScreenBoxesFromPositions() is expensive, so we local end_pos = self.document:getPosFromXPointer(item.pos1)
-- first check this item is on current page if start_pos <= cur_view_bottom and end_pos >= cur_view_top then
if not cur_view_top then local boxes = self.ui.document:getScreenBoxesFromPositions(item.pos0, item.pos1, true) -- get_segments=true
-- Even in page mode, it's safer to use pos and ui.dimen.h if boxes then
-- than pages' xpointers pos, even if ui.dimen.h is a bit for _, box in ipairs(boxes) do
-- larger than pages' heights if inside_box(pos, box) then
cur_view_top = self.ui.document:getCurrentPos() logger.dbg("Tap on highlight")
if self.view.view_mode == "page" and self.ui.document:getVisiblePageCount() > 1 then if self.select_mode then
cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h if hl_i == self.highlight_idx then
else -- tap on the first fragment: abort select mode, clear highlight
cur_view_bottom = cur_view_top + self.ui.dimen.h self.select_mode = false
end self:deleteHighlight(hl_i)
end return true
local spos0 = self.ui.document:getPosFromXPointer(pos0)
local spos1 = self.ui.document:getPosFromXPointer(pos1)
local start_pos = math.min(spos0, spos1)
local end_pos = math.max(spos0, spos1)
if start_pos <= cur_view_bottom and end_pos >= cur_view_top then
local boxes = self.ui.document:getScreenBoxesFromPositions(pos0, pos1, true) -- get_segments=true
if boxes then
for index, box in pairs(boxes) do
if inside_box(pos, box) then
logger.dbg("Tap on highlight")
if self.select_mode then
if page == self.highlight_page and i == self.highlight_idx then
-- tap on the first fragment: abort select mode, clear highlight
self.select_mode = false
self:deleteHighlight(page, i)
return true
end
else
table.insert(highlights_tapped, {page, i})
break
end end
else
table.insert(highlights_tapped, hl_i)
break
end end
end end
end end
@ -822,10 +809,9 @@ function ReaderHighlight:onTapXPointerSavedHighlight(ges)
end end
end end
function ReaderHighlight:updateHighlight(page, index, side, direction, move_by_char) function ReaderHighlight:updateHighlight(index, side, direction, move_by_char)
if self.ui.paging then return end local highlight = self.ui.annotation.annotations[index]
local highlight = self.view.highlight.saved[page][index] local highlight_before = util.tableDeepCopy(highlight)
local highlight_time = highlight.datetime
local highlight_beginning = highlight.pos0 local highlight_beginning = highlight.pos0
local highlight_end = highlight.pos1 local highlight_end = highlight.pos1
if side == 0 then -- we move pos0 if side == 0 then -- we move pos0
@ -846,7 +832,10 @@ function ReaderHighlight:updateHighlight(page, index, side, direction, move_by_c
if updated_highlight_beginning then if updated_highlight_beginning then
local order = self.ui.document:compareXPointers(updated_highlight_beginning, highlight_end) 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 if order and order > 0 then -- only if beginning did not go past end
self.view.highlight.saved[page][index].pos0 = updated_highlight_beginning 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
end end
else -- we move pos1 else -- we move pos1
@ -867,23 +856,16 @@ function ReaderHighlight:updateHighlight(page, index, side, direction, move_by_c
if updated_highlight_end then if updated_highlight_end then
local order = self.ui.document:compareXPointers(highlight_beginning, updated_highlight_end) 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 if order and order > 0 then -- only if end did not go back past beginning
self.view.highlight.saved[page][index].pos1 = updated_highlight_end highlight.pos1 = updated_highlight_end
end end
end end
end end
local new_beginning = self.view.highlight.saved[page][index].pos0 local new_beginning = highlight.pos0
local new_end = self.view.highlight.saved[page][index].pos1 local new_end = highlight.pos1
local new_text = self.ui.document:getTextFromXPointers(new_beginning, new_end) local new_text = self.ui.document:getTextFromXPointers(new_beginning, new_end)
local new_chapter = self.ui.toc:getTocTitleByPage(new_beginning) highlight.text = cleanupSelectedText(new_text)
self.view.highlight.saved[page][index].text = cleanupSelectedText(new_text) self.ui:handleEvent(Event:new("AnnotationsModified", { highlight, highlight_before }))
self.view.highlight.saved[page][index].chapter = new_chapter
local new_highlight = self.view.highlight.saved[page][index]
self.ui.bookmark:updateBookmark({
page = highlight_beginning,
datetime = highlight_time,
updated_highlight = new_highlight
})
if side == 0 then if side == 0 then
-- Ensure we show the page with the new beginning of highlight -- Ensure we show the page with the new beginning of highlight
if not self.ui.document:isXPointerInCurrentPage(new_beginning) then if not self.ui.document:isXPointerInCurrentPage(new_beginning) then
@ -909,20 +891,15 @@ end
function ReaderHighlight:showChooseHighlightDialog(highlights) function ReaderHighlight:showChooseHighlightDialog(highlights)
if #highlights == 1 then if #highlights == 1 then
local page, index = unpack(highlights[1]) self:showHighlightNoteOrDialog(highlights[1])
local item = self.view.highlight.saved[page][index]
local bookmark_note = self.ui.bookmark:getBookmarkNote({datetime = item.datetime})
self:showHighlightNoteOrDialog(page, index, bookmark_note)
else -- overlapped highlights else -- overlapped highlights
local dialog local dialog
local buttons = {} local buttons = {}
for i, v in ipairs(highlights) do for i, index in ipairs(highlights) do
local page, index = unpack(v) local item = self.ui.annotation.annotations[index]
local item = self.view.highlight.saved[page][index]
local bookmark_note = self.ui.bookmark:getBookmarkNote({datetime = item.datetime})
buttons[i] = {{ buttons[i] = {{
text = (bookmark_note and self.ui.bookmark.display_prefix["note"] text = (item.note and self.ui.bookmark.display_prefix["note"]
or self.ui.bookmark.display_prefix["highlight"]) .. item.text, or self.ui.bookmark.display_prefix["highlight"]) .. item.text,
align = "left", align = "left",
avoid_text_truncation = false, avoid_text_truncation = false,
font_face = "smallinfofont", font_face = "smallinfofont",
@ -930,7 +907,7 @@ function ReaderHighlight:showChooseHighlightDialog(highlights)
font_bold = false, font_bold = false,
callback = function() callback = function()
UIManager:close(dialog) UIManager:close(dialog)
self:showHighlightNoteOrDialog(page, index, bookmark_note) self:showHighlightNoteOrDialog(index)
end, end,
}} }}
end end
@ -942,7 +919,8 @@ function ReaderHighlight:showChooseHighlightDialog(highlights)
return true return true
end end
function ReaderHighlight:showHighlightNoteOrDialog(page, index, bookmark_note) function ReaderHighlight:showHighlightNoteOrDialog(index)
local bookmark_note = self.ui.annotation.annotations[index].note
if bookmark_note then if bookmark_note then
local textviewer local textviewer
textviewer = TextViewer:new{ textviewer = TextViewer:new{
@ -957,14 +935,14 @@ function ReaderHighlight:showHighlightNoteOrDialog(page, index, bookmark_note)
text = _("Delete highlight"), text = _("Delete highlight"),
callback = function() callback = function()
UIManager:close(textviewer) UIManager:close(textviewer)
self:deleteHighlight(page, index) self:deleteHighlight(index)
end, end,
}, },
{ {
text = _("Edit highlight"), text = _("Edit highlight"),
callback = function() callback = function()
UIManager:close(textviewer) UIManager:close(textviewer)
self:onShowHighlightDialog(page, index, false) self:onShowHighlightDialog(index)
end, end,
}, },
}, },
@ -972,17 +950,18 @@ function ReaderHighlight:showHighlightNoteOrDialog(page, index, bookmark_note)
} }
UIManager:show(textviewer) UIManager:show(textviewer)
else else
self:onShowHighlightDialog(page, index, true) self:onShowHighlightDialog(index)
end end
end end
function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text) function ReaderHighlight:onShowHighlightDialog(index)
local item = self.ui.annotation.annotations[index]
local buttons = { local buttons = {
{ {
{ {
text = _("Delete"), text = _("Delete"),
callback = function() callback = function()
self:deleteHighlight(page, index) self:deleteHighlight(index)
UIManager:close(self.edit_highlight_dialog) UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil self.edit_highlight_dialog = nil
end, end,
@ -990,15 +969,15 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
{ {
text = C_("Highlight", "Style"), text = C_("Highlight", "Style"),
callback = function() callback = function()
self:editHighlightStyle(page, index) self:editHighlightStyle(index)
UIManager:close(self.edit_highlight_dialog) UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil self.edit_highlight_dialog = nil
end, end,
}, },
{ {
text = is_auto_text and _("Add note") or _("Edit note"), text = item.note and _("Edit note") or _("Add note"),
callback = function() callback = function()
self:editHighlight(page, index) self:editHighlight(index)
UIManager:close(self.edit_highlight_dialog) UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil self.edit_highlight_dialog = nil
end, end,
@ -1006,8 +985,8 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
{ {
text = "", text = "",
callback = function() callback = function()
self.selected_text = self.view.highlight.saved[page][index] self.selected_text = util.tableDeepCopy(item)
self:onShowHighlightMenu(page, index) self:onShowHighlightMenu(index)
UIManager:close(self.edit_highlight_dialog) UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil self.edit_highlight_dialog = nil
end, end,
@ -1016,7 +995,7 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
} }
if self.ui.rolling then if self.ui.rolling then
local enabled = not self.view.highlight.saved[page][index].edited local enabled = not item.text_edited
local start_prev = "◁▒▒" local start_prev = "◁▒▒"
local start_next = "▷▒▒" local start_next = "▷▒▒"
local end_prev = "▒▒◁" local end_prev = "▒▒◁"
@ -1031,10 +1010,10 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
text = start_prev, text = start_prev,
enabled = enabled, enabled = enabled,
callback = function() callback = function()
self:updateHighlight(page, index, 0, -1, false) self:updateHighlight(index, 0, -1, false)
end, end,
hold_callback = function() hold_callback = function()
self:updateHighlight(page, index, 0, -1, true) self:updateHighlight(index, 0, -1, true)
return true return true
end end
}, },
@ -1042,10 +1021,10 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
text = start_next, text = start_next,
enabled = enabled, enabled = enabled,
callback = function() callback = function()
self:updateHighlight(page, index, 0, 1, false) self:updateHighlight(index, 0, 1, false)
end, end,
hold_callback = function() hold_callback = function()
self:updateHighlight(page, index, 0, 1, true) self:updateHighlight(index, 0, 1, true)
return true return true
end end
}, },
@ -1053,26 +1032,26 @@ function ReaderHighlight:onShowHighlightDialog(page, index, is_auto_text)
text = end_prev, text = end_prev,
enabled = enabled, enabled = enabled,
callback = function() callback = function()
self:updateHighlight(page, index, 1, -1, false) self:updateHighlight(index, 1, -1, false)
end, end,
hold_callback = function() hold_callback = function()
self:updateHighlight(page, index, 1, -1, true) self:updateHighlight(index, 1, -1, true)
end end
}, },
{ {
text = end_next, text = end_next,
enabled = enabled, enabled = enabled,
callback = function() callback = function()
self:updateHighlight(page, index, 1, 1, false) self:updateHighlight(index, 1, 1, false)
end, end,
hold_callback = function() hold_callback = function()
self:updateHighlight(page, index, 1, 1, true) self:updateHighlight(index, 1, 1, true)
end end
} }
}) })
end end
self.edit_highlight_dialog = ButtonDialog:new{ self.edit_highlight_dialog = ButtonDialog:new{
buttons = buttons buttons = buttons,
} }
UIManager:show(self.edit_highlight_dialog) UIManager:show(self.edit_highlight_dialog)
return true return true
@ -1090,7 +1069,7 @@ function ReaderHighlight:removeFromHighlightDialog(idx)
return button return button
end end
function ReaderHighlight:onShowHighlightMenu(page, index) function ReaderHighlight:onShowHighlightMenu(index)
if not self.selected_text then if not self.selected_text then
return return
end end
@ -1099,7 +1078,7 @@ function ReaderHighlight:onShowHighlightMenu(page, index)
local columns = 2 local columns = 2
for idx, fn_button in ffiUtil.orderedPairs(self._highlight_buttons) do for idx, fn_button in ffiUtil.orderedPairs(self._highlight_buttons) do
local button = fn_button(self, page, index) local button = fn_button(self, index)
if not button.show_in_highlight_dialog_func or button.show_in_highlight_dialog_func() then if not button.show_in_highlight_dialog_func or button.show_in_highlight_dialog_func() then
if #highlight_buttons[#highlight_buttons] >= columns then if #highlight_buttons[#highlight_buttons] >= columns then
table.insert(highlight_buttons, {}) table.insert(highlight_buttons, {})
@ -1198,12 +1177,12 @@ function ReaderHighlight:_resetHoldTimer(clear)
end end
function ReaderHighlight:onTogglePanelZoomSetting(arg, ges) function ReaderHighlight:onTogglePanelZoomSetting(arg, ges)
if not self.document.info.has_pages then return end if self.ui.rolling then return end
self.panel_zoom_enabled = not self.panel_zoom_enabled self.panel_zoom_enabled = not self.panel_zoom_enabled
end end
function ReaderHighlight:onToggleFallbackTextSelection(arg, ges) function ReaderHighlight:onToggleFallbackTextSelection(arg, ges)
if not self.document.info.has_pages then return end if self.ui.rolling then return end
self.panel_zoom_fallback_to_text_selection = not self.panel_zoom_fallback_to_text_selection self.panel_zoom_fallback_to_text_selection = not self.panel_zoom_fallback_to_text_selection
end end
@ -1229,7 +1208,7 @@ function ReaderHighlight:onPanelZoom(arg, ges)
end end
function ReaderHighlight:onHold(arg, ges) function ReaderHighlight:onHold(arg, ges)
if self.document.info.has_pages and self.panel_zoom_enabled then if self.ui.paging and self.panel_zoom_enabled then
local res = self:onPanelZoom(arg, ges) local res = self:onPanelZoom(arg, ges)
if res or not self.panel_zoom_fallback_to_text_selection then if res or not self.panel_zoom_fallback_to_text_selection then
return res return res
@ -1548,20 +1527,20 @@ function ReaderHighlight:viewSelectionHTML(debug_view, no_css_files_buttons)
end end
end end
function ReaderHighlight:translate(selected_text, page, index) function ReaderHighlight:translate(index)
if self.ui.rolling then if self.ui.rolling then
-- Extend the selected text to include any punctuation at start or end, -- Extend the selected text to include any punctuation at start or end,
-- which may give a better translation with the added context. -- which may give a better translation with the added context.
local extended_text = self.ui.document:extendXPointersToSentenceSegment(selected_text.pos0, selected_text.pos1) local extended_text = self.ui.document:extendXPointersToSentenceSegment(self.selected_text.pos0, self.selected_text.pos1)
if extended_text then if extended_text then
selected_text = extended_text self.selected_text = extended_text
end end
end end
if #selected_text.text > 0 then if #self.selected_text.text > 0 then
self:onTranslateText(selected_text.text, page, index) self:onTranslateText(self.selected_text.text, index)
-- or we will do OCR -- or we will do OCR
elseif self.hold_pos then elseif self.hold_pos then
local text = self.ui.document:getOCRText(self.hold_pos.page, selected_text) local text = self.ui.document:getOCRText(self.hold_pos.page, self.selected_text)
logger.dbg("OCRed text:", text) logger.dbg("OCRed text:", text)
if text and text ~= "" then if text and text ~= "" then
self:onTranslateText(text) self:onTranslateText(text)
@ -1572,14 +1551,9 @@ function ReaderHighlight:translate(selected_text, page, index)
end end
end end
end end
dbg:guard(ReaderHighlight, "translate",
function(self, selected_text)
assert(selected_text ~= nil,
"translate must not be called with nil selected_text!")
end)
function ReaderHighlight:onTranslateText(text, page, index) function ReaderHighlight:onTranslateText(text, index)
Translator:showTranslation(text, true, nil, nil, true, page, index) Translator:showTranslation(text, true, nil, nil, true, index)
end end
function ReaderHighlight:onTranslateCurrentPage() function ReaderHighlight:onTranslateCurrentPage()
@ -1661,7 +1635,7 @@ function ReaderHighlight:onHoldRelease()
self:addNote() self:addNote()
self:onClose() self:onClose()
elseif default_highlight_action == "translate" then elseif default_highlight_action == "translate" then
self:translate(self.selected_text) self:translate()
elseif default_highlight_action == "wikipedia" then elseif default_highlight_action == "wikipedia" then
self:lookupWikipedia() self:lookupWikipedia()
self:onClose() self:onClose()
@ -1751,137 +1725,45 @@ function ReaderHighlight:highlightFromHoldPos()
end end
end end
function ReaderHighlight:onHighlight() function ReaderHighlight:saveHighlight(extend_to_sentence)
self:saveHighlight() logger.dbg("save highlight")
end
function ReaderHighlight:onUnhighlight(bookmark_item)
local page
local sel_text
local sel_pos0
local datetime
local idx
if bookmark_item then -- called from Bookmarks menu onHold
page = bookmark_item.page
sel_text = bookmark_item.notes
sel_pos0 = bookmark_item.pos0
datetime = bookmark_item.datetime
else -- called from DictQuickLookup Unhighlight button
--- @fixme: is this self.hold_pos access safe?
page = self.hold_pos.page
sel_text = cleanupSelectedText(self.selected_text.text)
sel_pos0 = self.selected_text.pos0
end
if self.ui.paging then -- We can safely use page
-- As we may have changed spaces and hyphens handling in the extracted
-- text over the years, check text identities with them removed
local sel_text_cleaned = sel_text:gsub("[ -]", ""):gsub("\u{00AD}", "")
for index = 1, #self.view.highlight.saved[page] do
local highlight = self.view.highlight.saved[page][index]
-- pos0 are tables and can't be compared directly, except when from
-- DictQuickLookup where these are the same object.
-- If bookmark_item provided, just check datetime
if ( (datetime == nil and highlight.pos0 == sel_pos0) or
(datetime ~= nil and highlight.datetime == datetime) ) then
if highlight.text:gsub("[ -]", ""):gsub("\u{00AD}", "") == sel_text_cleaned then
idx = index
break
end
end
end
else -- page is a xpointer
-- The original page could be found in bookmark_item.text, but
-- no more if it has been renamed: we need to loop through all
-- highlights on all page slots
for p, highlights in pairs(self.view.highlight.saved) do
for index = 1, #highlights do
local highlight = highlights[index]
-- pos0 are strings and can be compared directly
if highlight.text == sel_text and (
(datetime == nil and highlight.pos0 == sel_pos0) or
(datetime ~= nil and highlight.datetime == datetime)) then
page = p -- this is the original page slot
idx = index
break
end
end
if idx then
break
end
end
end
if idx then
logger.dbg("found highlight to delete on page", page, idx)
self:deleteHighlight(page, idx, bookmark_item)
return true
end
end
function ReaderHighlight:getHighlightByDatetime(datetime)
for page, highlights in pairs(self.view.highlight.saved) do
for _, highlight in ipairs(highlights) do
if highlight.datetime == datetime then
return highlight
end
end
end
end
function ReaderHighlight:getHighlightBookmarkItem()
if self.hold_pos and not self.selected_text then if self.hold_pos and not self.selected_text then
self:highlightFromHoldPos() self:highlightFromHoldPos()
end end
if self.selected_text and self.selected_text.pos0 and self.selected_text.pos1 then if self.selected_text and self.selected_text.pos0 and self.selected_text.pos1 then
return { local pg_or_xp
page = self.ui.paging and self.selected_text.pos0.page or self.selected_text.pos0, if self.ui.rolling then
pos0 = self.selected_text.pos0, if extend_to_sentence then
pos1 = self.selected_text.pos1, local extended_text = self.ui.document:extendXPointersToSentenceSegment(self.selected_text.pos0, self.selected_text.pos1)
notes = cleanupSelectedText(self.selected_text.text), if extended_text then
highlighted = true, self.selected_text = extended_text
} end
end
end
function ReaderHighlight:saveHighlight(extend_to_sentence)
self.ui:handleEvent(Event:new("AddHighlight"))
logger.dbg("save highlight")
if self.selected_text and self.selected_text.pos0 and self.selected_text.pos1 then
if extend_to_sentence and self.ui.rolling then
local extended_text = self.ui.document:extendXPointersToSentenceSegment(self.selected_text.pos0, self.selected_text.pos1)
if extended_text then
self.selected_text = extended_text
end end
pg_or_xp = self.selected_text.pos0
else
pg_or_xp = self.selected_text.pos0.page
end end
local page = self.ui.paging and self.selected_text.pos0.page or self.ui.document:getPageFromXPointer(self.selected_text.pos0) local item = {
if not self.view.highlight.saved[page] then page = self.ui.paging and self.selected_text.pos0.page or self.selected_text.pos0,
self.view.highlight.saved[page] = {}
end
local datetime = os.date("%Y-%m-%d %H:%M:%S")
local pg_or_xp = self.ui.paging and page or self.selected_text.pos0
local chapter_name = self.ui.toc:getTocTitleByPage(pg_or_xp)
local hl_item = {
datetime = datetime,
text = cleanupSelectedText(self.selected_text.text),
pos0 = self.selected_text.pos0, pos0 = self.selected_text.pos0,
pos1 = self.selected_text.pos1, pos1 = self.selected_text.pos1,
pboxes = self.selected_text.pboxes, text = cleanupSelectedText(self.selected_text.text),
ext = self.selected_text.ext,
drawer = self.view.highlight.saved_drawer, drawer = self.view.highlight.saved_drawer,
chapter = chapter_name, chapter = self.ui.toc:getTocTitleByPage(pg_or_xp),
} }
table.insert(self.view.highlight.saved[page], hl_item) if self.ui.paging then
local bookmark_item = self:getHighlightBookmarkItem() item.pboxes = self.selected_text.pboxes
if bookmark_item then item.ext = self.selected_text.ext
bookmark_item.datetime = datetime self:writePdfAnnotation("save", item)
bookmark_item.chapter = chapter_name
self.ui.bookmark:addBookmark(bookmark_item)
end end
self:writePdfAnnotation("save", page, hl_item) local index = self.ui.annotation:addItem(item)
return page, #self.view.highlight.saved[page] self.view.footer:onUpdateFooter(self.view.footer_visible)
self.ui:handleEvent(Event:new("AnnotationsModified", { item, nb_highlights_added = 1 }))
return index
end end
end end
function ReaderHighlight:writePdfAnnotation(action, page, item, content) function ReaderHighlight:writePdfAnnotation(action, item, content)
if self.ui.rolling or G_reader_settings:readSetting("save_document") == "disable" then if self.ui.rolling or G_reader_settings:readSetting("save_document") == "disable" then
return return
end end
@ -1897,24 +1779,10 @@ function ReaderHighlight:writePdfAnnotation(action, page, item, content)
end end
local can_write local can_write
if item.pos0.page == item.pos1.page then -- single-page highlight if item.pos0.page == item.pos1.page then -- single-page highlight
local item_ can_write = doAction(action, item.pos0.page, item, content)
if item.pboxes then
item_ = item
else -- called from bookmarks to write bookmark note to annotation
for _, hl in ipairs(self.view.highlight.saved[page]) do
if hl.datetime == item.datetime then
item_ = {pboxes = hl.pboxes}
break
end
end
end
can_write = doAction(action, page, item_, content)
else -- multi-page highlight else -- multi-page highlight
local is_reflow = self.ui.document.configurable.text_wrap
for hl_page = item.pos0.page, item.pos1.page do for hl_page = item.pos0.page, item.pos1.page do
self.ui.document.configurable.text_wrap = 0
local hl_part = self:getSavedExtendedHighlightPage(item, hl_page) local hl_part = self:getSavedExtendedHighlightPage(item, hl_page)
self.ui.document.configurable.text_wrap = is_reflow
can_write = doAction(action, hl_page, hl_part, content) can_write = doAction(action, hl_page, hl_part, content)
if can_write == false then break end if can_write == false then break end
if action == "save" then -- update pboxes from quadpoints if action == "save" then -- update pboxes from quadpoints
@ -1934,14 +1802,6 @@ If you wish your highlights to be saved in the document, just move it to a writa
end end
end end
function ReaderHighlight:addNote(text)
local page, index = self:saveHighlight(true)
if text then self:clear() end
self:editHighlight(page, index, true, text)
UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil
end
function ReaderHighlight:lookupWikipedia() function ReaderHighlight:lookupWikipedia()
if self.selected_text then if self.selected_text then
self.ui:handleEvent(Event:new("LookupWikipedia", cleanupSelectedText(self.selected_text.text))) self.ui:handleEvent(Event:new("LookupWikipedia", cleanupSelectedText(self.selected_text.text)))
@ -1970,59 +1830,48 @@ function ReaderHighlight:onHighlightDictLookup()
end end
end end
function ReaderHighlight:deleteHighlight(page, i, bookmark_item) function ReaderHighlight:deleteHighlight(index)
logger.dbg("delete highlight", page, i) logger.dbg("delete highlight", index)
-- The per-page table is a pure array local item = self.ui.annotation.annotations[index]
local removed = table.remove(self.view.highlight.saved[page], i) self:writePdfAnnotation("delete", item)
-- But the main, outer table is a hash, so clear the table for this page if there are no longer any highlights on it self.ui.bookmark:removeItemByIndex(index)
if #self.view.highlight.saved[page] == 0 then
self.view.highlight.saved[page] = nil
end
if bookmark_item then
self.ui.bookmark:removeBookmark(bookmark_item)
else
self.ui.bookmark:removeBookmark({
page = self.ui.paging and page or removed.pos0,
datetime = removed.datetime,
})
end
self:writePdfAnnotation("delete", page, removed)
UIManager:setDirty(self.dialog, "ui") UIManager:setDirty(self.dialog, "ui")
end end
function ReaderHighlight:editHighlight(page, i, is_new_note, text) function ReaderHighlight:addNote(text)
local item = self.view.highlight.saved[page][i] local index = self:saveHighlight(true)
self.ui.bookmark:setBookmarkNote({ if text then -- called from Translator to save translation to note
page = self.ui.paging and page or item.pos0, self:clear()
datetime = item.datetime, end
}, true, is_new_note, text) self:editHighlight(index, true, text)
UIManager:close(self.edit_highlight_dialog)
self.edit_highlight_dialog = nil
end
function ReaderHighlight:editHighlight(index, is_new_note, text)
self.ui.bookmark:setBookmarkNote(index, is_new_note, text)
end end
function ReaderHighlight:editHighlightStyle(page, i) function ReaderHighlight:editHighlightStyle(index)
local item = self.view.highlight.saved[page][i] local item = self.ui.annotation.annotations[index]
local apply_drawer = function(drawer) local apply_drawer = function(drawer)
self:writePdfAnnotation("delete", page, item) self:writePdfAnnotation("delete", item)
item.drawer = drawer item.drawer = drawer
if self.ui.paging then if self.ui.paging then
self:writePdfAnnotation("save", page, item) self:writePdfAnnotation("save", item)
local bm_note = self.ui.bookmark:getBookmarkNote(item) if item.note then
if bm_note then self:writePdfAnnotation("content", item, item.note)
self:writePdfAnnotation("content", page, item, bm_note)
end end
end end
UIManager:setDirty(self.dialog, "ui") UIManager:setDirty(self.dialog, "ui")
self.ui:handleEvent(Event:new("BookmarkUpdated", self.ui:handleEvent(Event:new("AnnotationsModified", { item }))
self.ui.bookmark:getBookmarkForHighlight({
page = self.ui.paging and page or item.pos0,
datetime = item.datetime,
})))
end end
self:showHighlightStyleDialog(apply_drawer, item.drawer) self:showHighlightStyleDialog(apply_drawer, item.drawer)
end end
function ReaderHighlight:showHighlightStyleDialog(caller_callback, item_drawer) function ReaderHighlight:showHighlightStyleDialog(caller_callback, item_drawer)
local default_drawer, keep_shown_on_apply local default_drawer, keep_shown_on_apply
if item_drawer then -- called from editHighlightStyle if item_drawer then -- called from ReaderHighlight:editHighlightStyle()
default_drawer = self.view.highlight.saved_drawer or default_drawer = self.view.highlight.saved_drawer or
G_reader_settings:readSetting("highlight_drawing_style", "lighten") G_reader_settings:readSetting("highlight_drawing_style", "lighten")
keep_shown_on_apply = true keep_shown_on_apply = true
@ -2037,7 +1886,6 @@ function ReaderHighlight:showHighlightStyleDialog(caller_callback, item_drawer)
}, },
}) })
end end
local RadioButtonWidget = require("ui/widget/radiobuttonwidget")
UIManager:show(RadioButtonWidget:new{ UIManager:show(RadioButtonWidget:new{
title_text = _("Highlight style"), title_text = _("Highlight style"),
width_factor = 0.5, width_factor = 0.5,
@ -2051,14 +1899,14 @@ function ReaderHighlight:showHighlightStyleDialog(caller_callback, item_drawer)
end end
function ReaderHighlight:startSelection() function ReaderHighlight:startSelection()
self.highlight_page, self.highlight_idx = self:saveHighlight() self.highlight_idx = self:saveHighlight()
self.select_mode = true self.select_mode = true
end end
function ReaderHighlight:extendSelection() function ReaderHighlight:extendSelection()
-- item1 - starting fragment (saved), item2 - ending fragment (currently selected) -- item1 - starting fragment (saved), item2 - ending fragment (currently selected)
-- new extended highlight includes item1, item2 and the text between them -- new extended highlight includes item1, item2 and the text between them
local item1 = self.view.highlight.saved[self.highlight_page][self.highlight_idx] local item1 = self.ui.annotation.annotations[self.highlight_idx]
local item2_pos0, item2_pos1 = self.selected_text.pos0, self.selected_text.pos1 local item2_pos0, item2_pos1 = self.selected_text.pos0, self.selected_text.pos1
-- getting starting and ending positions, text and pboxes of extended highlight -- getting starting and ending positions, text and pboxes of extended highlight
local new_pos0, new_pos1, new_text, new_pboxes, ext local new_pos0, new_pos1, new_text, new_pboxes, ext
@ -2106,7 +1954,7 @@ function ReaderHighlight:extendSelection()
-- true to draw -- true to draw
new_text = self.ui.document:getTextFromXPointers(new_pos0, new_pos1, true) new_text = self.ui.document:getTextFromXPointers(new_pos0, new_pos1, true)
end end
self:deleteHighlight(self.highlight_page, self.highlight_idx) -- starting fragment self:deleteHighlight(self.highlight_idx) -- starting fragment
self.selected_text = { self.selected_text = {
text = new_text, text = new_text,
pos0 = new_pos0, pos0 = new_pos0,
@ -2126,7 +1974,10 @@ function ReaderHighlight:getExtendedHighlightPage(pos0, pos1, cur_page)
local page_boxes = self.ui.document:getTextBoxes(page) local page_boxes = self.ui.document:getTextBoxes(page)
if page == pos0.page then if page == pos0.page then
-- first page (from the start of highlight to the end of the page) -- first page (from the start of highlight to the end of the page)
item.pos0 = pos0 item.pos0 = {
x = pos0.x,
y = pos0.y,
}
item.pos1 = { item.pos1 = {
x = page_boxes[#page_boxes][#page_boxes[#page_boxes]].x1, x = page_boxes[#page_boxes][#page_boxes[#page_boxes]].x1,
y = page_boxes[#page_boxes][#page_boxes[#page_boxes]].y1, y = page_boxes[#page_boxes][#page_boxes[#page_boxes]].y1,
@ -2147,7 +1998,10 @@ function ReaderHighlight:getExtendedHighlightPage(pos0, pos1, cur_page)
x = page_boxes[1][1].x0, x = page_boxes[1][1].x0,
y = page_boxes[1][1].y0, y = page_boxes[1][1].y0,
} }
item.pos1 = pos1 item.pos1 = {
x = pos1.x,
y = pos1.y,
}
end end
item.pos0.page = page item.pos0.page = page
item.pos1.page = page item.pos1.page = page
@ -2159,44 +2013,53 @@ function ReaderHighlight:getExtendedHighlightPage(pos0, pos1, cur_page)
return item return item
end end
-- Returns one page of saved multi-page highlight -- Returns the list of highlights in page.
-- The list includes full single-page highlights and parts of multi-page highlights.
-- (For pdf documents only) -- (For pdf documents only)
function ReaderHighlight:getSavedExtendedHighlightPage(hl_or_bm, page, index) function ReaderHighlight:getPageSavedHighlights(page)
local highlight local highlights = {}
if hl_or_bm.ext then for index, highlight in ipairs(self.ui.annotation.annotations) do
highlight = hl_or_bm if highlight.drawer and highlight.pos0.page <= page and page <= highlight.pos1.page then
else -- called from bookmark, need to find the corresponding highlight if highlight.ext then -- multi-page highlight
for _, hl in ipairs(self.view.highlight.saved[hl_or_bm.page]) do local item = self:getSavedExtendedHighlightPage(highlight, page, index)
if hl.datetime == hl_or_bm.datetime then table.insert(highlights, item)
highlight = hl else
break table.insert(highlights, highlight)
end end
end end
end end
local item = {} return highlights
item.datetime = highlight.datetime end
item.drawer = highlight.drawer
item.pos0 = highlight.ext[page].pos0 -- Returns one page of saved multi-page highlight
item.pos0.zoom = highlight.pos0.zoom -- (For pdf documents only)
function ReaderHighlight:getSavedExtendedHighlightPage(highlight, page, index)
local item = {
datetime = highlight.datetime,
drawer = highlight.drawer,
text = highlight.text,
page = highlight.page,
pos0 = highlight.ext[page].pos0,
pos1 = highlight.ext[page].pos1,
pboxes = highlight.ext[page].pboxes,
parent = index,
}
item.pos0.zoom = highlight.pos0.zoom
item.pos0.rotation = highlight.pos0.rotation item.pos0.rotation = highlight.pos0.rotation
item.pos1 = highlight.ext[page].pos1 item.pos1.zoom = highlight.pos0.zoom
item.pos1.zoom = highlight.pos0.zoom
item.pos1.rotation = highlight.pos0.rotation item.pos1.rotation = highlight.pos0.rotation
item.pboxes = highlight.ext[page].pboxes
item.parent = {highlight.pos0.page, index}
return item return item
end end
function ReaderHighlight:onReadSettings(config) function ReaderHighlight:onReadSettings(config)
self.view.highlight.saved_drawer = config:readSetting("highlight_drawer") self.view.highlight.saved_drawer = config:readSetting("highlight_drawer")
or G_reader_settings:readSetting("highlight_drawing_style") or self.view.highlight.saved_drawer or G_reader_settings:readSetting("highlight_drawing_style") or self.view.highlight.saved_drawer
self.view.highlight.disabled = G_reader_settings:has("default_highlight_action") self.view.highlight.disabled = G_reader_settings:readSetting("default_highlight_action") == "nothing"
and G_reader_settings:readSetting("default_highlight_action") == "nothing"
self.allow_corner_scroll = G_reader_settings:nilOrTrue("highlight_corner_scroll") self.allow_corner_scroll = G_reader_settings:nilOrTrue("highlight_corner_scroll")
-- panel zoom settings isn't supported in EPUB -- panel zoom settings isn't supported in EPUB
if self.document.info.has_pages then if self.ui.paging then
local ext = util.getFileNameSuffix(self.ui.document.file) local ext = util.getFileNameSuffix(self.ui.document.file)
G_reader_settings:initializeExtSettings("panel_zoom_enabled", {cbz = true, cbt = true}) G_reader_settings:initializeExtSettings("panel_zoom_enabled", {cbz = true, cbt = true})
G_reader_settings:initializeExtSettings("panel_zoom_fallback_to_text_selection", {pdf = true}) G_reader_settings:initializeExtSettings("panel_zoom_fallback_to_text_selection", {pdf = true})
@ -2231,6 +2094,8 @@ function ReaderHighlight:onClose()
self:clear() self:clear()
end end
-- dpad/keys support
function ReaderHighlight:onHighlightPress() function ReaderHighlight:onHighlightPress()
if self._current_indicator_pos then if self._current_indicator_pos then
if not self._start_indicator_highlight then if not self._start_indicator_highlight then

@ -3,7 +3,7 @@ ReaderLink is an abstraction for document-specific link interfaces.
]] ]]
local BD = require("ui/bidi") local BD = require("ui/bidi")
local ButtonDialogTitle = require("ui/widget/buttondialogtitle") local ButtonDialog = require("ui/widget/buttondialog")
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device") local Device = require("device")
local DocumentRegistry = require("document/documentregistry") local DocumentRegistry = require("document/documentregistry")
@ -118,7 +118,7 @@ function ReaderLink:init()
end) end)
if G_reader_settings:isTrue("opening_page_location_stack") then if G_reader_settings:isTrue("opening_page_location_stack") then
-- Add location at book opening to stack -- Add location at book opening to stack
self.ui:registerPostReadyCallback(function() self.ui:registerPostReaderReadyCallback(function()
self:addCurrentLocationToStack() self:addCurrentLocationToStack()
end) end)
end end
@ -858,7 +858,7 @@ end
function ReaderLink:onGoToExternalLink(link_url) function ReaderLink:onGoToExternalLink(link_url)
local buttons, title = self:getButtonsForExternalLinkDialog(link_url) local buttons, title = self:getButtonsForExternalLinkDialog(link_url)
self.external_link_dialog = ButtonDialogTitle:new{ self.external_link_dialog = ButtonDialog:new{
title = title, title = title,
buttons = buttons, buttons = buttons,
} }

@ -100,7 +100,7 @@ function ReaderRolling:init()
self.valid_cache_rendering_hash = self.ui.document:getDocumentRenderingHash(false) self.valid_cache_rendering_hash = self.ui.document:getDocumentRenderingHash(false)
end end
end) end)
table.insert(self.ui.postReaderCallback, function() table.insert(self.ui.postReaderReadyCallback, function()
self:updatePos() self:updatePos()
-- Disable crengine internal history, with required redraw -- Disable crengine internal history, with required redraw
self.ui.document:enableInternalHistory(false) self.ui.document:enableInternalHistory(false)
@ -234,7 +234,7 @@ function ReaderRolling:onReadSettings(config)
-- And check if we can migrate to a newest DOM version after -- And check if we can migrate to a newest DOM version after
-- the book is loaded (unless the user told us not to). -- the book is loaded (unless the user told us not to).
if config:nilOrFalse("cre_keep_old_dom_version") then if config:nilOrFalse("cre_keep_old_dom_version") then
self.ui:registerPostReadyCallback(function() self.ui:registerPostReaderReadyCallback(function()
self:checkXPointersAndProposeDOMVersionUpgrade() self:checkXPointersAndProposeDOMVersionUpgrade()
end) end)
end end
@ -1029,9 +1029,9 @@ function ReaderRolling:onUpdatePos(force)
if self.batched_update_count > 0 then if self.batched_update_count > 0 then
return return
end end
if self.ui.postReaderCallback ~= nil then -- ReaderUI:init() not yet done if self.ui.postReaderReadyCallback ~= nil then -- ReaderUI:init() not yet done
-- Don't schedule any updatePos as long as ReaderUI:init() is -- Don't schedule any updatePos as long as ReaderUI:init() is
-- not finished (one will be called in the ui.postReaderCallback -- not finished (one will be called in the ui.postReaderReadyCallback
-- we have set above) to avoid multiple refreshes. -- we have set above) to avoid multiple refreshes.
return true return true
end end
@ -1129,7 +1129,7 @@ function ReaderRolling:onRedrawCurrentView()
end end
function ReaderRolling:onSetDimensions(dimen) function ReaderRolling:onSetDimensions(dimen)
if self.ui.postReaderCallback ~= nil then if self.ui.postReaderReadyCallback ~= nil then
-- ReaderUI:init() not yet done: just set document dimensions -- ReaderUI:init() not yet done: just set document dimensions
self.ui.document:setViewDimen(Screen:getSize()) self.ui.document:setViewDimen(Screen:getSize())
-- (what's done in the following else is done elsewhere by -- (what's done in the following else is done elsewhere by
@ -1445,25 +1445,12 @@ function ReaderRolling:checkXPointersAndProposeDOMVersionUpgrade()
local applyFuncToXPointersSlots = function(func) local applyFuncToXPointersSlots = function(func)
-- Last position -- Last position
func(self, "xpointer", "last position in book") func(self, "xpointer", "last position in book")
-- Bookmarks -- Annotations
if self.ui.bookmark and self.ui.bookmark.bookmarks and #self.ui.bookmark.bookmarks > 0 then if self.ui.annotation and self.ui.annotation.annotations and #self.ui.annotation.annotations > 0 then
local slots = { "page", "pos0", "pos1" } local slots = { "page", "pos0", "pos1" }
for _, bookmark in ipairs(self.ui.bookmark.bookmarks) do for _, item in ipairs(self.ui.annotation.annotations) do
for _, slot in ipairs(slots) do for _, slot in ipairs(slots) do
func(bookmark, slot, bookmark.notes or "bookmark") func(item, slot, item.text or "annotation")
end
end
end
-- Highlights
if self.view.highlight and self.view.highlight.saved then
local slots = { "pos0", "pos1" }
for page, items in pairs(self.view.highlight.saved) do
if items and #items > 0 then
for _, highlight in ipairs(items) do
for _, slot in ipairs(slots) do
func(highlight, slot, highlight.text or "highlight")
end
end
end end
end end
end end
@ -1509,6 +1496,9 @@ function ReaderRolling:checkXPointersAndProposeDOMVersionUpgrade()
local new_xp = normalized_xpointers[xp] local new_xp = normalized_xpointers[xp]
if new_xp then if new_xp then
obj[slot] = new_xp obj[slot] = new_xp
if slot == "page" then
self.ui.annotation:updateItemByXPointer(obj)
end
else else
-- Let lost/not-found XPointer be. There is a small chance that -- Let lost/not-found XPointer be. There is a small chance that
-- it will be found (it it was made before the boxing code moved -- it will be found (it it was made before the boxing code moved

@ -186,11 +186,11 @@ function ReaderThumbnail:removeFromCache(hash_subs, remove_only_non_matching)
return nb_removed, size_removed return nb_removed, size_removed
end end
function ReaderThumbnail:resetCachedPagesForBookmarks(...) function ReaderThumbnail:resetCachedPagesForBookmarks(annotations)
-- Multiple bookmarks may be provided -- Multiple bookmarks may be provided
local start_page, end_page local start_page, end_page
for i = 1, select("#", ...) do for i = 1, #annotations do
local bm = select(i, ...) local bm = annotations[i]
if self.ui.rolling then if self.ui.rolling then
-- Look at all properties that may be xpointers -- Look at all properties that may be xpointers
for _, k in ipairs({"page", "pos0", "pos1"}) do for _, k in ipairs({"page", "pos0", "pos1"}) do
@ -537,9 +537,6 @@ end
ReaderThumbnail.onDocumentRerendered = ReaderThumbnail.resetCache ReaderThumbnail.onDocumentRerendered = ReaderThumbnail.resetCache
ReaderThumbnail.onDocumentPartiallyRerendered = ReaderThumbnail.resetCache ReaderThumbnail.onDocumentPartiallyRerendered = ReaderThumbnail.resetCache
-- Emitted When adding/removing/updating bookmarks and highlights -- Emitted When adding/removing/updating bookmarks and highlights
ReaderThumbnail.onBookmarkAdded = ReaderThumbnail.resetCachedPagesForBookmarks ReaderThumbnail.onAnnotationsModified = ReaderThumbnail.resetCachedPagesForBookmarks
ReaderThumbnail.onBookmarkRemoved = ReaderThumbnail.resetCachedPagesForBookmarks
ReaderThumbnail.onBookmarkUpdated = ReaderThumbnail.resetCachedPagesForBookmarks
ReaderThumbnail.onBookmarkEdited = ReaderThumbnail.resetCachedPagesForBookmarks
return ReaderThumbnail return ReaderThumbnail

@ -95,7 +95,6 @@ function ReaderView:init()
temp_drawer = "invert", temp_drawer = "invert",
temp = {}, temp = {},
saved_drawer = "lighten", saved_drawer = "lighten",
saved = {},
indicator = nil, -- geom: non-touch highlight position indicator: {x = 50, y=50} indicator = nil, -- geom: non-touch highlight position indicator: {x = 50, y=50}
} }
self.page_states = {} self.page_states = {}
@ -522,6 +521,7 @@ function ReaderView:drawTempHighlight(bb, x, y)
end end
function ReaderView:drawSavedHighlight(bb, x, y) function ReaderView:drawSavedHighlight(bb, x, y)
if #self.ui.annotation.annotations == 0 then return end
if self.ui.paging then if self.ui.paging then
self:drawPageSavedHighlight(bb, x, y) self:drawPageSavedHighlight(bb, x, y)
else else
@ -529,45 +529,18 @@ function ReaderView:drawSavedHighlight(bb, x, y)
end end
end end
-- Returns the list of highlights in page.
-- The list includes full single-page highlights and parts of multi-page highlights.
function ReaderView:getPageSavedHighlights(page)
local highlights = {}
local is_reflow = self.document.configurable.text_wrap
self.document.configurable.text_wrap = 0
for page_num, page_highlights in pairs(self.highlight.saved) do
for i, highlight in ipairs(page_highlights) do
-- old single-page reflow highlights do not have page in position
local pos0_page = highlight.pos0.page or page_num
local pos1_page = highlight.pos1.page or page_num
if pos0_page <= page and page <= pos1_page then
if pos0_page == pos1_page then -- single-page highlight
table.insert(highlights, highlight)
else -- multi-page highlight
local item = self.ui.highlight:getSavedExtendedHighlightPage(highlight, page, i)
table.insert(highlights, item)
end
end
end
end
self.document.configurable.text_wrap = is_reflow
return highlights
end
function ReaderView:drawPageSavedHighlight(bb, x, y) function ReaderView:drawPageSavedHighlight(bb, x, y)
local pages = self:getCurrentPageList() local pages = self:getCurrentPageList()
for _, page in ipairs(pages) do for _, page in ipairs(pages) do
local items = self:getPageSavedHighlights(page) local items = self.ui.highlight:getPageSavedHighlights(page)
for _, item in ipairs(items) do for _, item in ipairs(items) do
local boxes = self.document:getPageBoxesFromPositions(page, item.pos0, item.pos1) local boxes = self.document:getPageBoxesFromPositions(page, item.pos0, item.pos1)
if boxes then if boxes then
local drawer = item.drawer or self.highlight.saved_drawer local draw_note_mark = item.note and self.highlight.note_mark
local draw_note_mark = self.highlight.note_mark and
self.ui.bookmark:getBookmarkNote({datetime = item.datetime})
for _, box in ipairs(boxes) do for _, box in ipairs(boxes) do
local rect = self:pageToScreenTransform(page, box) local rect = self:pageToScreenTransform(page, box)
if rect then if rect then
self:drawHighlightRect(bb, x, y, rect, drawer, draw_note_mark) self:drawHighlightRect(bb, x, y, rect, item.drawer, draw_note_mark)
if draw_note_mark and self.highlight.note_mark == "sidemark" then if draw_note_mark and self.highlight.note_mark == "sidemark" then
draw_note_mark = false -- side mark in the first line only draw_note_mark = false -- side mark in the first line only
end end
@ -583,48 +556,38 @@ function ReaderView:drawXPointerSavedHighlight(bb, x, y)
-- showing menu...). We might want to cache these boxes per page (and -- showing menu...). We might want to cache these boxes per page (and
-- clear that cache when page layout change or highlights are added -- clear that cache when page layout change or highlights are added
-- or removed). -- or removed).
local cur_view_top, cur_view_bottom -- Even in page mode, it's safer to use pos and ui.dimen.h
for _, items in pairs(self.highlight.saved) do -- than pages' xpointers pos, even if ui.dimen.h is a bit
if items then -- larger than pages' heights
for j = 1, #items do local cur_view_top = self.document:getCurrentPos()
local item = items[j] local cur_view_bottom
local pos0, pos1 = item.pos0, item.pos1 if self.view_mode == "page" and self.document:getVisiblePageCount() > 1 then
-- document:getScreenBoxesFromPositions() is expensive, so we cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h
-- first check this item is on current page else
if not cur_view_top then cur_view_bottom = cur_view_top + self.ui.dimen.h
-- Even in page mode, it's safer to use pos and ui.dimen.h end
-- than pages' xpointers pos, even if ui.dimen.h is a bit for _, item in ipairs(self.ui.annotation.annotations) do
-- larger than pages' heights if item.drawer then
cur_view_top = self.document:getCurrentPos() -- document:getScreenBoxesFromPositions() is expensive, so we
if self.view_mode == "page" and self.document:getVisiblePageCount() > 1 then -- first check if this item is on current page
cur_view_bottom = cur_view_top + 2 * self.ui.dimen.h local start_pos = self.document:getPosFromXPointer(item.pos0)
else local end_pos = self.document:getPosFromXPointer(item.pos1)
cur_view_bottom = cur_view_top + self.ui.dimen.h if start_pos <= cur_view_bottom and end_pos >= cur_view_top then
end local boxes = self.document:getScreenBoxesFromPositions(item.pos0, item.pos1, true) -- get_segments=true
end if boxes then
local spos0 = self.document:getPosFromXPointer(pos0) local draw_note_mark = item.note and self.highlight.note_mark
local spos1 = self.document:getPosFromXPointer(pos1) for _, box in ipairs(boxes) do
local start_pos = math.min(spos0, spos1) if box.h ~= 0 then
local end_pos = math.max(spos0, spos1) self:drawHighlightRect(bb, x, y, box, item.drawer, draw_note_mark)
if start_pos <= cur_view_bottom and end_pos >= cur_view_top then if draw_note_mark and self.highlight.note_mark == "sidemark" then
local boxes = self.document:getScreenBoxesFromPositions(pos0, pos1, true) -- get_segments=true draw_note_mark = false -- side mark in the first line only
if boxes then
local drawer = item.drawer or self.highlight.saved_drawer
local draw_note_mark = self.highlight.note_mark and
self.ui.bookmark:getBookmarkNote({datetime = item.datetime})
for _, box in ipairs(boxes) do
if box.h ~= 0 then
self:drawHighlightRect(bb, x, y, box, drawer, draw_note_mark)
if draw_note_mark and self.highlight.note_mark == "sidemark" then
draw_note_mark = false -- side mark in the first line only
end
end end
end -- end for each box end
end -- end if boxes end
end end
end -- end for each highlight end
end end
end -- end for all saved highlight end
end end
function ReaderView:drawHighlightRect(bb, _x, _y, rect, drawer, draw_note_mark) function ReaderView:drawHighlightRect(bb, _x, _y, rect, drawer, draw_note_mark)
@ -914,40 +877,6 @@ function ReaderView:onReadSettings(config)
self:resetLayout() self:resetLayout()
local page_scroll = config:readSetting("kopt_page_scroll") or self.document.configurable.page_scroll local page_scroll = config:readSetting("kopt_page_scroll") or self.document.configurable.page_scroll
self.page_scroll = page_scroll == 1 and true or false self.page_scroll = page_scroll == 1 and true or false
self.highlight.saved = config:readSetting("highlight", {})
-- Highlight formats in crengine and mupdf are incompatible.
-- Backup highlights when the document is opened with incompatible engine.
local page, page_highlights
while true do -- remove empty tables for pages without highlights and get the first page with highlights
page, page_highlights = next(self.highlight.saved)
if not page or #page_highlights > 0 then
break -- we're done (there is none, or there is some usable)
else
self.highlight.saved[page] = nil -- clean it up while we're at it, and find another one
end
end
if page_highlights then
local highlight_type = type(page_highlights[1].pos0)
if self.ui.rolling and highlight_type == "table" then
config:saveSetting("highlight_paging", self.highlight.saved)
self.highlight.saved = config:readSetting("highlight_rolling", {})
config:saveSetting("highlight", self.highlight.saved)
config:delSetting("highlight_rolling")
elseif self.ui.paging and highlight_type == "string" then
config:saveSetting("highlight_rolling", self.highlight.saved)
self.highlight.saved = config:readSetting("highlight_paging", {})
config:saveSetting("highlight", self.highlight.saved)
config:delSetting("highlight_paging")
end
else
if self.ui.rolling and config:has("highlight_rolling") then
self.highlight.saved = config:readSetting("highlight_rolling")
config:delSetting("highlight_rolling")
elseif self.ui.paging and config:has("highlight_paging") then
self.highlight.saved = config:readSetting("highlight_paging")
config:delSetting("highlight_paging")
end
end
self.inverse_reading_order = config:isTrue("inverse_reading_order") or G_reader_settings:isTrue("inverse_reading_order") self.inverse_reading_order = config:isTrue("inverse_reading_order") or G_reader_settings:isTrue("inverse_reading_order")
self.page_overlap_enable = config:isTrue("show_overlap_enable") or G_reader_settings:isTrue("page_overlap_enable") or G_defaults:readSetting("DSHOWOVERLAP") self.page_overlap_enable = config:isTrue("show_overlap_enable") or G_reader_settings:isTrue("page_overlap_enable") or G_defaults:readSetting("DSHOWOVERLAP")
self.page_overlap_style = config:readSetting("page_overlap_style") or G_reader_settings:readSetting("page_overlap_style") or "dim" self.page_overlap_style = config:readSetting("page_overlap_style") or G_reader_settings:readSetting("page_overlap_style") or "dim"
@ -1107,7 +1036,6 @@ function ReaderView:onSaveSettings()
if G_reader_settings:nilOrFalse("lock_rotation") then if G_reader_settings:nilOrFalse("lock_rotation") then
self.document.configurable.rotation_mode = Screen:getRotationMode() -- will be saved by ReaderConfig self.document.configurable.rotation_mode = Screen:getRotationMode() -- will be saved by ReaderConfig
end end
self.ui.doc_settings:saveSetting("highlight", self.highlight.saved)
self.ui.doc_settings:saveSetting("inverse_reading_order", self.inverse_reading_order) self.ui.doc_settings:saveSetting("inverse_reading_order", self.inverse_reading_order)
self.ui.doc_settings:saveSetting("show_overlap_enable", self.page_overlap_enable) self.ui.doc_settings:saveSetting("show_overlap_enable", self.page_overlap_enable)
self.ui.doc_settings:saveSetting("page_overlap_style", self.page_overlap_style) self.ui.doc_settings:saveSetting("page_overlap_style", self.page_overlap_style)

@ -24,6 +24,7 @@ local LanguageSupport = require("languagesupport")
local Notification = require("ui/widget/notification") local Notification = require("ui/widget/notification")
local PluginLoader = require("pluginloader") local PluginLoader = require("pluginloader")
local ReaderActivityIndicator = require("apps/reader/modules/readeractivityindicator") local ReaderActivityIndicator = require("apps/reader/modules/readeractivityindicator")
local ReaderAnnotation = require("apps/reader/modules/readerannotation")
local ReaderBack = require("apps/reader/modules/readerback") local ReaderBack = require("apps/reader/modules/readerback")
local ReaderBookmark = require("apps/reader/modules/readerbookmark") local ReaderBookmark = require("apps/reader/modules/readerbookmark")
local ReaderConfig = require("apps/reader/modules/readerconfig") local ReaderConfig = require("apps/reader/modules/readerconfig")
@ -83,7 +84,7 @@ local ReaderUI = InputContainer:extend{
password = nil, password = nil,
postInitCallback = nil, postInitCallback = nil,
postReaderCallback = nil, postReaderReadyCallback = nil,
} }
function ReaderUI:registerModule(name, ui_module, always_active) function ReaderUI:registerModule(name, ui_module, always_active)
@ -102,8 +103,8 @@ function ReaderUI:registerPostInitCallback(callback)
table.insert(self.postInitCallback, callback) table.insert(self.postInitCallback, callback)
end end
function ReaderUI:registerPostReadyCallback(callback) function ReaderUI:registerPostReaderReadyCallback(callback)
table.insert(self.postReaderCallback, callback) table.insert(self.postReaderReadyCallback, callback)
end end
function ReaderUI:init() function ReaderUI:init()
@ -116,7 +117,7 @@ function ReaderUI:init()
Device:setIgnoreInput(true) -- Avoid ANRs on Android with unprocessed events. Device:setIgnoreInput(true) -- Avoid ANRs on Android with unprocessed events.
self.postInitCallback = {} self.postInitCallback = {}
self.postReaderCallback = {} self.postReaderReadyCallback = {}
-- if we are not the top level dialog ourselves, it must be given in the table -- if we are not the top level dialog ourselves, it must be given in the table
if not self.dialog then if not self.dialog then
self.dialog = self self.dialog = self
@ -182,6 +183,12 @@ function ReaderUI:init()
view = self.view, view = self.view,
ui = self ui = self
}) })
self:registerModule("annotation", ReaderAnnotation:new{
dialog = self.dialog,
view = self.view,
ui = self,
document = self.document,
})
-- reader goto controller -- reader goto controller
-- "goto" being a dirty keyword in Lua? -- "goto" being a dirty keyword in Lua?
self:registerModule("gotopage", ReaderGoto:new{ self:registerModule("gotopage", ReaderGoto:new{
@ -491,10 +498,10 @@ function ReaderUI:init()
-- Need the same event for PDF document -- Need the same event for PDF document
self:handleEvent(Event:new("ReaderReady", self.doc_settings)) self:handleEvent(Event:new("ReaderReady", self.doc_settings))
for _,v in ipairs(self.postReaderCallback) do for _,v in ipairs(self.postReaderReadyCallback) do
v() v()
end end
self.postReaderCallback = nil self.postReaderReadyCallback = nil
Device:setIgnoreInput(false) -- Allow processing of events (on Android). Device:setIgnoreInput(false) -- Allow processing of events (on Android).
Input:inhibitInputUntil(0.2) Input:inhibitInputUntil(0.2)
@ -584,6 +591,7 @@ end
function ReaderUI:showReader(file, provider, seamless) function ReaderUI:showReader(file, provider, seamless)
logger.dbg("show reader ui") logger.dbg("show reader ui")
file = ffiUtil.realpath(file)
if lfs.attributes(file, "mode") ~= "file" then if lfs.attributes(file, "mode") ~= "file" then
UIManager:show(InfoMessage:new{ UIManager:show(InfoMessage:new{
text = T(_("File '%1' does not exist."), BD.filepath(file)) text = T(_("File '%1' does not exist."), BD.filepath(file))

@ -503,14 +503,14 @@ Show translated text in TextViewer, with alternate translations
@string source_lang[opt="auto"] (`"en"`, `"fr"`, ``) or `"auto"` to auto-detect source language @string source_lang[opt="auto"] (`"en"`, `"fr"`, ``) or `"auto"` to auto-detect source language
@string target_lang[opt] (`"en"`, `"fr"`, ``) @string target_lang[opt] (`"en"`, `"fr"`, ``)
--]] --]]
function Translator:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index) function Translator:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index)
if Device:hasClipboard() then if Device:hasClipboard() then
Device.input.setClipboardText(text) Device.input.setClipboardText(text)
end end
local NetworkMgr = require("ui/network/manager") local NetworkMgr = require("ui/network/manager")
if NetworkMgr:willRerunWhenOnline(function() if NetworkMgr:willRerunWhenOnline(function()
self:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index) self:showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index)
end) then end) then
return return
end end
@ -519,11 +519,11 @@ function Translator:showTranslation(text, detailed_view, source_lang, target_lan
-- translation service query. -- translation service query.
local Trapper = require("ui/trapper") local Trapper = require("ui/trapper")
Trapper:wrap(function() Trapper:wrap(function()
self:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index) self:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index)
end) end)
end end
function Translator:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, page, index) function Translator:_showTranslation(text, detailed_view, source_lang, target_lang, from_highlight, index)
if not target_lang then if not target_lang then
target_lang = self:getTargetLanguage() target_lang = self:getTargetLanguage()
end end
@ -632,8 +632,8 @@ function Translator:_showTranslation(text, detailed_view, source_lang, target_la
UIManager:close(textviewer) UIManager:close(textviewer)
UIManager:close(ui.highlight.highlight_dialog) UIManager:close(ui.highlight.highlight_dialog)
ui.highlight.highlight_dialog = nil ui.highlight.highlight_dialog = nil
if page then if index then
ui.highlight:editHighlight(page, index, false, text_main) ui.highlight:editHighlight(index, false, text_main)
else else
ui.highlight:addNote(text_main) ui.highlight:addNote(text_main)
end end
@ -645,8 +645,8 @@ function Translator:_showTranslation(text, detailed_view, source_lang, target_la
UIManager:close(textviewer) UIManager:close(textviewer)
UIManager:close(ui.highlight.highlight_dialog) UIManager:close(ui.highlight.highlight_dialog)
ui.highlight.highlight_dialog = nil ui.highlight.highlight_dialog = nil
if page then if index then
ui.highlight:editHighlight(page, index, false, text_all) ui.highlight:editHighlight(index, false, text_all)
else else
ui.highlight:addNote(text_all) ui.highlight:addNote(text_all)
end end

@ -65,11 +65,6 @@ local DictQuickLookup = InputContainer:extend{
rotated_update_wiki_languages_on_close = nil, rotated_update_wiki_languages_on_close = nil,
} }
local highlight_strings = {
highlight =_("Highlight"),
unhighlight = _("Unhighlight"),
}
function DictQuickLookup.getWikiSaveEpubDefaultDir() function DictQuickLookup.getWikiSaveEpubDefaultDir()
local dir = G_reader_settings:readSetting("home_dir") or filemanagerutil.getDefaultDir() local dir = G_reader_settings:readSetting("home_dir") or filemanagerutil.getDefaultDir()
if dir:sub(-1) ~= "/" then if dir:sub(-1) ~= "/" then
@ -438,19 +433,13 @@ function DictQuickLookup:init()
}, },
{ {
id = "highlight", id = "highlight",
text = self:getHighlightText(), text = _("Highlight"),
enabled = not self:isDocless() and self.highlight ~= nil, enabled = not self:isDocless() and self.highlight ~= nil,
callback = function() callback = function()
if self:getHighlightText() == highlight_strings.highlight then self.save_highlight = not self.save_highlight
self.ui:handleEvent(Event:new("Highlight"))
else
self.ui:handleEvent(Event:new("Unhighlight"))
end
-- Just update, repaint and refresh *this* button -- Just update, repaint and refresh *this* button
local this = self.button_table:getButtonById("highlight") local this = self.button_table:getButtonById("highlight")
if not this then return end this:setText(self.save_highlight and _("Unhighlight") or _("Highlight"), this.width)
this:enableDisable(self.highlight ~= nil)
this:setText(self:getHighlightText(), this.width)
this:refresh() this:refresh()
end, end,
}, },
@ -953,22 +942,6 @@ function DictQuickLookup:onShow()
return true return true
end end
function DictQuickLookup:getHighlightedItem()
if self:isDocless() then return end
return self.ui.highlight:getHighlightBookmarkItem()
end
function DictQuickLookup:getHighlightText()
local item = self:getHighlightedItem()
if not item then
return highlight_strings.highlight, false
elseif self.ui.bookmark:isBookmarkAdded(item) then
return highlight_strings.unhighlight, false
else
return highlight_strings.highlight, true
end
end
function DictQuickLookup:isPrevDictAvaiable() function DictQuickLookup:isPrevDictAvaiable()
return self.dict_index > 1 return self.dict_index > 1
end end
@ -1150,13 +1123,19 @@ function DictQuickLookup:onClose(no_clear)
self.ui:handleEvent(Event:new("UpdateWikiLanguages", self.wiki_languages)) self.ui:handleEvent(Event:new("UpdateWikiLanguages", self.wiki_languages))
end end
end end
if self.highlight and not no_clear then
-- delay unhighlight of selection, so we can see where we stopped when if self.save_highlight then
-- back from our journey into dictionary or wikipedia self.highlight:saveHighlight()
local clear_id = self.highlight:getClearId() self.highlight:clear()
UIManager:scheduleIn(0.5, function() else
self.highlight:clear(clear_id) if self.highlight and not no_clear then
end) -- delay unhighlight of selection, so we can see where we stopped when
-- back from our journey into dictionary or wikipedia
local clear_id = self.highlight:getClearId()
UIManager:scheduleIn(0.5, function()
self.highlight:clear(clear_id)
end)
end
end end
return true return true
end end

@ -57,8 +57,14 @@ function CoverMenu:updateCache(file, status, do_create, pages)
local percent_finished = doc_settings:readSetting("percent_finished") local percent_finished = doc_settings:readSetting("percent_finished")
local summary = doc_settings:readSetting("summary") local summary = doc_settings:readSetting("summary")
status = summary and summary.status status = summary and summary.status
local highlight = doc_settings:readSetting("highlight") local has_highlight
local has_highlight = highlight and next(highlight) and true local annotations = doc_settings:readSetting("annotations")
if annotations then
has_highlight = #annotations > 0
else
local highlight = doc_settings:readSetting("highlight")
has_highlight = highlight and next(highlight) and true
end
self.cover_info_cache[file] = table.pack(pages, percent_finished, status, has_highlight) -- may be a sparse array self.cover_info_cache[file] = table.pack(pages, percent_finished, status, has_highlight) -- may be a sparse array
else else
if self.cover_info_cache and self.cover_info_cache[file] then if self.cover_info_cache and self.cover_info_cache[file] then

@ -235,6 +235,23 @@ function MyClipping:getImage(image)
end end
end end
function MyClipping:parseAnnotations(annotations, book)
for _, item in ipairs(annotations) do
if item.drawer then
local clipping = {
sort = "highlight",
page = item.pageno,
time = self:getTime(item.datetime),
text = self:getText(item.text),
note = self:getText(item.note),
chapter = item.chapter,
drawer = item.drawer,
}
table.insert(book, { clipping })
end
end
end
function MyClipping:parseHighlight(highlights, bookmarks, book) function MyClipping:parseHighlight(highlights, bookmarks, book)
--DEBUG("book", book.file) --DEBUG("book", book.file)
@ -249,13 +266,14 @@ function MyClipping:parseHighlight(highlights, bookmarks, book)
local orphan_highlights = {} local orphan_highlights = {}
for page, items in pairs(highlights) do for page, items in pairs(highlights) do
for _, item in ipairs(items) do for _, item in ipairs(items) do
local clipping = {} local clipping = {
clipping.page = page sort = "highlight",
clipping.sort = "highlight" page = page,
clipping.time = self:getTime(item.datetime or "") time = self:getTime(item.datetime or ""),
clipping.text = self:getText(item.text) text = self:getText(item.text),
clipping.chapter = item.chapter chapter = item.chapter,
clipping.drawer = item.drawer drawer = item.drawer,
}
local bookmark_found = false local bookmark_found = false
for _, bookmark in pairs(bookmarks) do for _, bookmark in pairs(bookmarks) do
if bookmark.datetime == item.datetime then if bookmark.datetime == item.datetime then
@ -316,9 +334,13 @@ end
function MyClipping:getClippingsFromBook(clippings, doc_path) function MyClipping:getClippingsFromBook(clippings, doc_path)
local doc_settings = DocSettings:open(doc_path) local doc_settings = DocSettings:open(doc_path)
local highlights = doc_settings:readSetting("highlight") local highlights, bookmarks
if not highlights then return end local annotations = doc_settings:readSetting("annotations")
local bookmarks = doc_settings:readSetting("bookmarks") if annotations == nil then
highlights = doc_settings:readSetting("highlight")
if highlights == nil then return end
bookmarks = doc_settings:readSetting("bookmarks")
end
local props = doc_settings:readSetting("doc_props") local props = doc_settings:readSetting("doc_props")
props = FileManagerBookInfo.extendProps(props, doc_path) props = FileManagerBookInfo.extendProps(props, doc_path)
local title, author = self:getTitleAuthor(doc_path, props) local title, author = self:getTitleAuthor(doc_path, props)
@ -326,8 +348,13 @@ function MyClipping:getClippingsFromBook(clippings, doc_path)
file = doc_path, file = doc_path,
title = title, title = title,
author = author, author = author,
number_of_pages = doc_settings:readSetting("doc_pages"),
} }
self:parseHighlight(highlights, bookmarks, clippings[title]) if annotations then
self:parseAnnotations(annotations, clippings[title])
else
self:parseHighlight(highlights, bookmarks, clippings[title])
end
end end
function MyClipping:parseHistory() function MyClipping:parseHistory()
@ -361,7 +388,7 @@ function MyClipping:parseCurrentDoc(view)
output_filename = util.getSafeFilename(title), output_filename = util.getSafeFilename(title),
number_of_pages = view.document.info.number_of_pages, number_of_pages = view.document.info.number_of_pages,
} }
self:parseHighlight(view.highlight.saved, view.ui.bookmark.bookmarks, clippings[title]) self:parseAnnotations(view.ui.annotation.annotations, clippings[title])
return clippings return clippings
end end

@ -156,6 +156,7 @@ end
--- Parse and export highlights from the currently opened document. --- Parse and export highlights from the currently opened document.
function Exporter:exportCurrentNotes() function Exporter:exportCurrentNotes()
self.ui.annotation:updatePageNumbers()
local clippings = self:getDocumentClippings() local clippings = self:getDocumentClippings()
self:exportClippings(clippings) self:exportClippings(clippings)
end end

@ -187,7 +187,7 @@ function ReaderStatistics:initData()
self.data.pages = self.document:getPageCount() self.data.pages = self.document:getPageCount()
-- Update these numbers to what's actually stored in the settings -- Update these numbers to what's actually stored in the settings
self.data.highlights, self.data.notes = self.ui.bookmark:getNumberOfHighlightsAndNotes() self.data.highlights, self.data.notes = self.ui.annotation:getNumberOfHighlightsAndNotes()
self.id_curr_book = self:getIdBookDB() self.id_curr_book = self:getIdBookDB()
self.book_read_pages, self.book_read_time = self:getPageTimeTotalStats(self.id_curr_book) self.book_read_pages, self.book_read_time = self:getPageTimeTotalStats(self.id_curr_book)
if self.book_read_pages > 0 then if self.book_read_pages > 0 then
@ -2727,27 +2727,14 @@ function ReaderStatistics:onCloseDocument()
self:insertDB() self:insertDB()
end end
function ReaderStatistics:onAddHighlight() function ReaderStatistics:onAnnotationsModified(annotations)
if self.settings.is_enabled then if self.settings.is_enabled then
self.data.highlights = self.data.highlights + 1 if annotations.nb_highlights_added then
end self.data.highlights = self.data.highlights + annotations.nb_highlights_added
end end
if annotations.nb_notes_added then
function ReaderStatistics:onDelHighlight() self.data.notes = self.data.notes + annotations.nb_notes_added
if self.settings.is_enabled then end
self.data.highlights = self.data.highlights - 1
end
end
function ReaderStatistics:onAddNote()
if self.settings.is_enabled then
self.data.notes = self.data.notes + 1
end
end
function ReaderStatistics:onDelNote()
if self.settings.is_enabled then
self.data.notes = self.data.notes - 1
end end
end end

@ -20,7 +20,7 @@ describe("ReaderBookmark module", function()
readerui.highlight:onHoldPan(nil, { pos = pos1 }) readerui.highlight:onHoldPan(nil, { pos = pos1 })
readerui.highlight:onHoldRelease() readerui.highlight:onHoldRelease()
assert.truthy(readerui.highlight.highlight_dialog) assert.truthy(readerui.highlight.highlight_dialog)
readerui.highlight:onHighlight() readerui.highlight:saveHighlight()
UIManager:nextTick(function() UIManager:nextTick(function()
UIManager:close(readerui.highlight.highlight_dialog) UIManager:close(readerui.highlight.highlight_dialog)
UIManager:close(readerui) UIManager:close(readerui)
@ -67,17 +67,6 @@ describe("ReaderBookmark module", function()
UIManager:show(readerui) UIManager:show(readerui)
readerui.rolling:onGotoPage(10) readerui.rolling:onGotoPage(10)
end) end)
it("should compare bookmarks properly", function()
assert.truthy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, },
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, },
{ notes = 'bar', page = 1, pos0 = 0, pos1 = 2, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo0', page = 1, pos0 = 0, pos1 = 0, },
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, }))
end)
it("should show dogear after toggling non-bookmarked page", function() it("should show dogear after toggling non-bookmarked page", function()
assert.falsy(readerui.view.dogear_visible) assert.falsy(readerui.view.dogear_visible)
toggler_dogear(readerui) toggler_dogear(readerui)
@ -90,7 +79,7 @@ describe("ReaderBookmark module", function()
Screen:shot("screenshots/reader_bookmark_nodogear_epub.png") Screen:shot("screenshots/reader_bookmark_nodogear_epub.png")
assert.falsy(readerui.view.dogear_visible) assert.falsy(readerui.view.dogear_visible)
end) end)
it("should sort bookmarks with descending page numbers", function() it("should sort bookmarks with ascending page numbers", function()
local pages = {1, 20, 5, 30, 10, 40, 15, 25, 35, 45} local pages = {1, 20, 5, 30, 10, 40, 15, 25, 35, 45}
for _, page in ipairs(pages) do for _, page in ipairs(pages) do
readerui.rolling:onGotoPage(page) readerui.rolling:onGotoPage(page)
@ -99,11 +88,11 @@ describe("ReaderBookmark module", function()
readerui.bookmark:onShowBookmark() readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui) show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_10marks_epub.png") Screen:shot("screenshots/reader_bookmark_10marks_epub.png")
assert.are.same(10, #readerui.bookmark.bookmarks) assert.are.same(10, #readerui.annotation.annotations)
assert.are.same(15, readerui.document:getPageFromXPointer(readerui.annotation.annotations[4].page))
end) end)
it("should keep descending page numbers after removing bookmarks", function() it("should keep descending page numbers after removing bookmarks", function()
local pages = {1, 30, 10, 40, 20} local pages = {1, 30, 10, 40, 20}
readerui.bookmark.bookmarks = {}
for _, page in ipairs(pages) do for _, page in ipairs(pages) do
readerui.rolling:onGotoPage(page) readerui.rolling:onGotoPage(page)
toggler_dogear(readerui) toggler_dogear(readerui)
@ -111,7 +100,7 @@ describe("ReaderBookmark module", function()
readerui.bookmark:onShowBookmark() readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui) show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_5marks_epub.png") Screen:shot("screenshots/reader_bookmark_5marks_epub.png")
assert.are.same(5, #readerui.bookmark.bookmarks) assert.are.same(5, #readerui.annotation.annotations)
end) end)
it("should add bookmark by highlighting", function() it("should add bookmark by highlighting", function()
highlight_text(readerui, highlight_text(readerui,
@ -120,18 +109,18 @@ describe("ReaderBookmark module", function()
readerui.bookmark:onShowBookmark() readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui) show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_6marks_epub.png") Screen:shot("screenshots/reader_bookmark_6marks_epub.png")
assert.are.same(6, #readerui.bookmark.bookmarks) assert.are.same(6, #readerui.annotation.annotations)
end) end)
it("should get previous bookmark for certain page", function() it("should get previous bookmark for certain page", function()
local xpointer = readerui.document:getXPointer() local xpointer = readerui.document:getXPointer()
local bm_xpointer = readerui.bookmark:getPreviousBookmarkedPage(xpointer) local bm_xpointer = readerui.bookmark:getPreviousBookmarkedPage(xpointer)
assert.are.same(6, #readerui.bookmark.bookmarks) assert.are.same(6, #readerui.annotation.annotations)
assert.are.same(1, readerui.document:getPageFromXPointer(bm_xpointer)) assert.are.same(5, readerui.document:getPageFromXPointer(bm_xpointer))
end) end)
it("should get next bookmark for certain page", function() it("should get next bookmark for certain page", function()
local xpointer = readerui.document:getXPointer() local xpointer = readerui.document:getXPointer()
local bm_xpointer = readerui.bookmark:getNextBookmarkedPage(xpointer) local bm_xpointer = readerui.bookmark:getNextBookmarkedPage(xpointer)
assert.are.same(20, readerui.document:getPageFromXPointer(bm_xpointer)) assert.are.same(15, readerui.document:getPageFromXPointer(bm_xpointer))
end) end)
end) end)
@ -154,34 +143,6 @@ describe("ReaderBookmark module", function()
UIManager:show(readerui) UIManager:show(readerui)
readerui.paging:onGotoPage(10) readerui.paging:onGotoPage(10)
end) end)
it("should does bookmark comparison properly", function()
assert.truthy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', pos0 = { page = 1 , x = 2, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, },
{ notes = 'foo', pos0 = { page = 1 , x = 2, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, },
{ notes = 'foo', page = 1, pos1 = 2, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', page = 1, pos0 = 0, pos1 = 2, },
{ notes = 'foo', page = 1, pos0 = 2, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', pos0 = { page = 1 , x = 2, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, },
{ notes = 'foo', pos0 = { page = 2 , x = 2, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', pos0 = { page = 1 , x = 1, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, },
{ notes = 'foo', pos0 = { page = 1 , x = 2, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, }))
assert.falsy(readerui.bookmark:isBookmarkSame(
{ notes = 'foo', pos0 = { page = 1 , x = 1, y = 3},
pos1 = { page = 1, x = 20, y = 3 }, },
{ notes = 'foo', pos0 = { page = 1 , x = 1, y = 3},
pos1 = { page = 1, x = 20, y = 2 }, }))
end)
it("should show dogear after toggling non-bookmarked page", function() it("should show dogear after toggling non-bookmarked page", function()
toggler_dogear(readerui) toggler_dogear(readerui)
Screen:shot("screenshots/reader_bookmark_dogear_pdf.png") Screen:shot("screenshots/reader_bookmark_dogear_pdf.png")
@ -192,7 +153,7 @@ describe("ReaderBookmark module", function()
Screen:shot("screenshots/reader_bookmark_nodogear_pdf.png") Screen:shot("screenshots/reader_bookmark_nodogear_pdf.png")
assert.truthy(not readerui.view.dogear_visible) assert.truthy(not readerui.view.dogear_visible)
end) end)
it("should sort bookmarks with descending page numbers", function() it("should sort bookmarks with ascending page numbers", function()
local pages = {1, 20, 5, 30, 10, 40, 15, 25, 35, 45} local pages = {1, 20, 5, 30, 10, 40, 15, 25, 35, 45}
for _, page in ipairs(pages) do for _, page in ipairs(pages) do
readerui.paging:onGotoPage(page) readerui.paging:onGotoPage(page)
@ -201,7 +162,8 @@ describe("ReaderBookmark module", function()
readerui.bookmark:onShowBookmark() readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui) show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_10marks_pdf.png") Screen:shot("screenshots/reader_bookmark_10marks_pdf.png")
assert.are.same(10, #readerui.bookmark.bookmarks) assert.are.same(10, #readerui.annotation.annotations)
assert.are.same(15, readerui.annotation.annotations[4].page)
end) end)
it("should keep descending page numbers after removing bookmarks", function() it("should keep descending page numbers after removing bookmarks", function()
local pages = {1, 30, 10, 40, 20} local pages = {1, 30, 10, 40, 20}
@ -212,14 +174,14 @@ describe("ReaderBookmark module", function()
readerui.bookmark:onShowBookmark() readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui) show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_5marks_pdf.png") Screen:shot("screenshots/reader_bookmark_5marks_pdf.png")
assert.are.same(5, #readerui.bookmark.bookmarks) assert.are.same(5, #readerui.annotation.annotations)
end) end)
it("should add bookmark by highlighting", function() it("should add bookmark by highlighting", function()
highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }) highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 })
readerui.bookmark:onShowBookmark() readerui.bookmark:onShowBookmark()
show_bookmark_menu(readerui) show_bookmark_menu(readerui)
Screen:shot("screenshots/reader_bookmark_6marks_pdf.png") Screen:shot("screenshots/reader_bookmark_6marks_pdf.png")
assert.are.same(6, #readerui.bookmark.bookmarks) assert.are.same(6, #readerui.annotation.annotations)
end) end)
it("should get previous bookmark for certain page", function() it("should get previous bookmark for certain page", function()
assert.are.same(5, readerui.bookmark:getPreviousBookmarkedPage(10)) assert.are.same(5, readerui.bookmark:getPreviousBookmarkedPage(10))
@ -227,31 +189,5 @@ describe("ReaderBookmark module", function()
it("should get next bookmark for certain page", function() it("should get next bookmark for certain page", function()
assert.are.same(15, readerui.bookmark:getNextBookmarkedPage(10)) assert.are.same(15, readerui.bookmark:getNextBookmarkedPage(10))
end) end)
it("should search/add bookmarks properly", function()
-- clear bookmarks created by previous tests
readerui.bookmark.bookmarks = {}
local p1 = { x = 0, y = 0, page = 100 }
local bm1 = { notes = 'foo', page = 10,
pos0 = { x = 0, y = 0, page = 100 }, pos1 = p1, }
assert.falsy(readerui.bookmark:isBookmarkAdded(bm1))
readerui.bookmark:addBookmark(bm1)
assert.are.same(readerui.bookmark.bookmarks, {bm1})
local bm2 = { notes = 'foo', page = 1,
pos0 = { x = 0, y = 0, page = 1 }, pos1 = p1, }
assert.falsy(readerui.bookmark:isBookmarkAdded(bm2))
readerui.bookmark:addBookmark(bm2)
assert.are.same({bm1, bm2}, readerui.bookmark.bookmarks)
local bm3 = { notes = 'foo', page = 5,
pos0 = { x = 0, y = 0, page = 5 }, pos1 = p1, }
assert.falsy(readerui.bookmark:isBookmarkAdded(bm3))
readerui.bookmark:addBookmark(bm3)
assert.are.same({bm1, bm3, bm2}, readerui.bookmark.bookmarks)
assert.truthy(readerui.bookmark:isBookmarkAdded(bm1))
assert.truthy(readerui.bookmark:isBookmarkAdded(bm2))
assert.truthy(readerui.bookmark:isBookmarkAdded(bm3))
end)
end) end)
end) end)

@ -17,7 +17,7 @@ describe("Readerhighlight module", function()
readerui.highlight:onHold(nil, { pos = pos0 }) readerui.highlight:onHold(nil, { pos = pos0 })
readerui.highlight:onHoldRelease() readerui.highlight:onHoldRelease()
readerui.highlight:onHighlight() readerui.highlight:saveHighlight()
assert.spy(s).was_called() assert.spy(s).was_called()
assert.spy(s).was_called_with(match.is_ref(readerui.languagesupport), assert.spy(s).was_called_with(match.is_ref(readerui.languagesupport),
@ -50,7 +50,7 @@ describe("Readerhighlight module", function()
assert.truthy(readerui.highlight.highlight_dialog) assert.truthy(readerui.highlight.highlight_dialog)
assert.truthy(UIManager._window_stack[next_slot].widget assert.truthy(UIManager._window_stack[next_slot].widget
== readerui.highlight.highlight_dialog) == readerui.highlight.highlight_dialog)
readerui.highlight:onHighlight() readerui.highlight:saveHighlight()
UIManager:scheduleIn(1, function() UIManager:scheduleIn(1, function()
UIManager:close(readerui.highlight.highlight_dialog) UIManager:close(readerui.highlight.highlight_dialog)
UIManager:close(readerui) UIManager:close(readerui)
@ -64,7 +64,7 @@ describe("Readerhighlight module", function()
readerui.highlight:onHold(nil, { pos = pos0 }) readerui.highlight:onHold(nil, { pos = pos0 })
readerui.highlight:onHoldPan(nil, { pos = pos1 }) readerui.highlight:onHoldPan(nil, { pos = pos1 })
readerui.highlight:onHoldRelease() readerui.highlight:onHoldRelease()
readerui.highlight:onHighlight() readerui.highlight:saveHighlight()
readerui.highlight:clear() readerui.highlight:clear()
UIManager:close(readerui.highlight.highlight_dialog) UIManager:close(readerui.highlight.highlight_dialog)
readerui.highlight:onTap(nil, { pos = pos2 }) readerui.highlight:onTap(nil, { pos = pos2 })
@ -106,12 +106,13 @@ describe("Readerhighlight module", function()
end) end)
after_each(function() after_each(function()
readerui.highlight:clear() readerui.highlight:clear()
readerui.annotation.annotations = {}
end) end)
it("should highlight single word", function() it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 400, y = 70 }) highlight_single_word(readerui, Geom:new{ x = 400, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_epub.png") Screen:shot("screenshots/reader_highlight_single_word_epub.png")
assert.spy(selection_spy).was_called() assert.spy(selection_spy).was_called()
assert.truthy(readerui.view.highlight.saved[page]) assert.truthy(#readerui.annotation.annotations == 1)
end) end)
it("should highlight text", function() it("should highlight text", function()
highlight_text(readerui, highlight_text(readerui,
@ -119,7 +120,7 @@ describe("Readerhighlight module", function()
Geom:new{ x = 400, y = 170 }) Geom:new{ x = 400, y = 170 })
Screen:shot("screenshots/reader_highlight_text_epub.png") Screen:shot("screenshots/reader_highlight_text_epub.png")
assert.spy(selection_spy).was_called() assert.spy(selection_spy).was_called()
assert.truthy(readerui.view.highlight.saved[page]) assert.truthy(#readerui.annotation.annotations == 1)
end) end)
it("should response on tap gesture", function() it("should response on tap gesture", function()
tap_highlight_text(readerui, tap_highlight_text(readerui,
@ -154,6 +155,7 @@ describe("Readerhighlight module", function()
end) end)
after_each(function() after_each(function()
readerui.highlight:clear() readerui.highlight:clear()
readerui.annotation.annotations = {}
end) end)
it("should response on tap gesture #nocov", function() it("should response on tap gesture #nocov", function()
tap_highlight_text(readerui, tap_highlight_text(readerui,
@ -165,10 +167,12 @@ describe("Readerhighlight module", function()
it("should highlight single word", function() it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_pdf.png") Screen:shot("screenshots/reader_highlight_single_word_pdf.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
it("should highlight text", function() it("should highlight text", function()
highlight_text(readerui, Geom:new{ x = 260, y = 170 }, Geom:new{ x = 260, y = 250 }) highlight_text(readerui, Geom:new{ x = 260, y = 170 }, Geom:new{ x = 260, y = 250 })
Screen:shot("screenshots/reader_highlight_text_pdf.png") Screen:shot("screenshots/reader_highlight_text_pdf.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
end) end)
describe("for scanned page without text layer", function() describe("for scanned page without text layer", function()
@ -179,6 +183,7 @@ describe("Readerhighlight module", function()
end) end)
after_each(function() after_each(function()
readerui.highlight:clear() readerui.highlight:clear()
readerui.annotation.annotations = {}
end) end)
it("should respond to tap gesture #nocov", function() it("should respond to tap gesture #nocov", function()
tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 }) tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 })
@ -187,10 +192,12 @@ describe("Readerhighlight module", function()
it("should highlight single word", function() it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_pdf_scanned.png") Screen:shot("screenshots/reader_highlight_single_word_pdf_scanned.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
it("should highlight text", function() it("should highlight text", function()
highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }) highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 })
Screen:shot("screenshots/reader_highlight_text_pdf_scanned.png") Screen:shot("screenshots/reader_highlight_text_pdf_scanned.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
end) end)
describe("for reflowed page", function() describe("for reflowed page", function()
@ -202,6 +209,7 @@ describe("Readerhighlight module", function()
end) end)
after_each(function() after_each(function()
readerui.highlight:clear() readerui.highlight:clear()
readerui.annotation.annotations = {}
readerui.document.configurable.text_wrap = 0 readerui.document.configurable.text_wrap = 0
UIManager:close(readerui) -- close to flush settings UIManager:close(readerui) -- close to flush settings
-- We haven't torn it down yet -- We haven't torn it down yet
@ -214,10 +222,12 @@ describe("Readerhighlight module", function()
it("should highlight single word", function() it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_pdf_reflowed.png") Screen:shot("screenshots/reader_highlight_single_word_pdf_reflowed.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
it("should highlight text", function() it("should highlight text", function()
highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }) highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 })
Screen:shot("screenshots/reader_highlight_text_pdf_reflowed.png") Screen:shot("screenshots/reader_highlight_text_pdf_reflowed.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
end) end)
end) end)
@ -247,6 +257,7 @@ describe("Readerhighlight module", function()
end) end)
after_each(function() after_each(function()
readerui.highlight:clear() readerui.highlight:clear()
readerui.annotation.annotations = {}
end) end)
it("should highlight single word", function() it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
@ -255,6 +266,7 @@ describe("Readerhighlight module", function()
it("should highlight text", function() it("should highlight text", function()
highlight_text(readerui, Geom:new{ x = 260, y = 170 }, Geom:new{ x = 260, y = 250 }) highlight_text(readerui, Geom:new{ x = 260, y = 170 }, Geom:new{ x = 260, y = 250 })
Screen:shot("screenshots/reader_highlight_text_pdf_scroll.png") Screen:shot("screenshots/reader_highlight_text_pdf_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
it("should response on tap gesture", function() it("should response on tap gesture", function()
tap_highlight_text(readerui, tap_highlight_text(readerui,
@ -262,6 +274,7 @@ describe("Readerhighlight module", function()
Geom:new{ x = 260, y = 150 }, Geom:new{ x = 260, y = 150 },
Geom:new{ x = 280, y = 110 }) Geom:new{ x = 280, y = 110 })
Screen:shot("screenshots/reader_tap_highlight_text_pdf_scroll.png") Screen:shot("screenshots/reader_tap_highlight_text_pdf_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
end) end)
describe("for scanned page without text layer", function() describe("for scanned page without text layer", function()
@ -273,14 +286,17 @@ describe("Readerhighlight module", function()
end) end)
after_each(function() after_each(function()
readerui.highlight:clear() readerui.highlight:clear()
readerui.annotation.annotations = {}
end) end)
it("should highlight single word", function() it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_pdf_scanned_scroll.png") Screen:shot("screenshots/reader_highlight_single_word_pdf_scanned_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
it("should highlight text", function() it("should highlight text", function()
highlight_text(readerui, Geom:new{x = 192, y = 186}, Geom:new{x = 280, y = 186}) highlight_text(readerui, Geom:new{x = 192, y = 186}, Geom:new{x = 280, y = 186})
Screen:shot("screenshots/reader_highlight_text_pdf_scanned_scroll.png") Screen:shot("screenshots/reader_highlight_text_pdf_scanned_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
it("should response on tap gesture", function() it("should response on tap gesture", function()
tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 }) tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 })
@ -296,6 +312,7 @@ describe("Readerhighlight module", function()
end) end)
after_each(function() after_each(function()
readerui.highlight:clear() readerui.highlight:clear()
readerui.annotation.annotations = {}
readerui.document.configurable.text_wrap = 0 readerui.document.configurable.text_wrap = 0
UIManager:close(readerui) -- close to flush settings UIManager:close(readerui) -- close to flush settings
-- We haven't torn it down yet -- We haven't torn it down yet
@ -304,10 +321,12 @@ describe("Readerhighlight module", function()
it("should highlight single word", function() it("should highlight single word", function()
highlight_single_word(readerui, Geom:new{ x = 260, y = 70 }) highlight_single_word(readerui, Geom:new{ x = 260, y = 70 })
Screen:shot("screenshots/reader_highlight_single_word_pdf_reflowed_scroll.png") Screen:shot("screenshots/reader_highlight_single_word_pdf_reflowed_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
it("should highlight text", function() it("should highlight text", function()
highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }) highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 })
Screen:shot("screenshots/reader_highlight_text_pdf_reflowed_scroll.png") Screen:shot("screenshots/reader_highlight_text_pdf_reflowed_scroll.png")
assert.truthy(#readerui.annotation.annotations == 1)
end) end)
it("should response on tap gesture", function() it("should response on tap gesture", function()
tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 }) tap_highlight_text(readerui, Geom:new{ x = 260, y = 70 }, Geom:new{ x = 260, y = 150 }, Geom:new{ x = 280, y = 110 })

Loading…
Cancel
Save