From 3a9b77b6cfc5848b22876e9ea5762a568deb3ce8 Mon Sep 17 00:00:00 2001 From: hius07 <62179190+hius07@users.noreply.github.com> Date: Sun, 31 Mar 2024 11:54:24 +0300 Subject: [PATCH] Update readerannotation.lua --- .../apps/reader/modules/readerannotation.lua | 333 +++++++++++++----- 1 file changed, 239 insertions(+), 94 deletions(-) diff --git a/frontend/apps/reader/modules/readerannotation.lua b/frontend/apps/reader/modules/readerannotation.lua index 5de9e7421..86c2e4442 100644 --- a/frontend/apps/reader/modules/readerannotation.lua +++ b/frontend/apps/reader/modules/readerannotation.lua @@ -4,11 +4,25 @@ local ReaderAnnotation = WidgetContainer:extend{ annotations = nil, } +-- build, read, save + function ReaderAnnotation:buildAnnotation(bm, highlights, update_pageno) -- bm - corresponding bookmark, highlights - 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 update_pageno then -- cannot be done in onReadSettings() - pageno = self.ui.paging and bm.page or self.document:getPageFromXPointer(bm.page) + if update_pageno then + if note and self.ui.bookmark:isBookmarkAutoText(bm) then + note = nil + end + if chapter == nil then + local pn_or_xp = (self.ui.rolling and bm.pos0) and bm.pos0 or bm.page + chapter = self.ui.toc:getTocTitleByPage(pn_or_xp) + end + pageno = self.ui.bookmark:getBookmarkPageString(bm.page) end if not hl then -- page bookmark or orphaned bookmark hl = {} @@ -24,7 +38,6 @@ function ReaderAnnotation:buildAnnotation(bm, highlights, update_pageno) end end end - local note = bm.text ~= "" and bm.text or nil if self.ui.paging then -- old single-page reflow highlights do not have page in position if not bm.pos0.page then @@ -39,7 +52,7 @@ function ReaderAnnotation:buildAnnotation(bm, highlights, update_pageno) text = bm.notes, -- highlighted text, editable text_edited = hl.edited, -- true if highlighted text has been edited note = note, -- user's note, editable - chapter = bm.chapter, -- book chapter title + 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) @@ -49,35 +62,6 @@ function ReaderAnnotation:buildAnnotation(bm, highlights, update_pageno) } end -function ReaderAnnotation.buildBookmark(an) - return { - datetime = an.datetime, - highlighted = an.drawer and true or nil, - notes = an.text, - text = an.note, - chapter = an.chapter, - page = an.page, - pos0 = an.pos0, - pos1 = an.pos1, - } -end - -function ReaderAnnotation.buildHighlight(an) - return { - datetime = an.datetime, - drawer = an.drawer, - color = an.color, - text = an.text, - edited = an.text_edited, - chapter = an.chapter, - page = an.page, - pos0 = an.pos0, - pos1 = an.pos1, - pboxes = an.pboxes, - ext = an.ext, - } -end - function ReaderAnnotation:getHighlightByDatetime(highlights, datetime) for pageno, page_highlights in pairs(highlights) do for i, highlight in ipairs(page_highlights) do @@ -93,31 +77,25 @@ function ReaderAnnotation:getAnnotationsFromBookmarksHighlights(bookmarks, highl for i = #bookmarks, 1, -1 do table.insert(annotations, self:buildAnnotation(bookmarks[i], highlights, update_pageno)) end - return annotations -end - -function ReaderAnnotation:getBookmarksHighlightsFromAnnotations(annotations) - local bookmarks, highlights = {}, {} - for i = #annotations, 1, -1 do - local an = annotations[i] - table.insert(bookmarks, self.buildBookmark(an)) - if an.drawer then - if highlights[an.pageno] == nil then - highlights[an.pageno] = {} + if #annotations > 1 then + local sort_func = function(a, b) + if self.ui.rolling then + return self:isItemInPositionOrderRolling(a, b) + else + return self:isItemInPositionOrderPaging(a, b) end - table.insert(highlights[an.pageno], self.buildHighlight(an)) end + table.sort(annotations, sort_func) end - return bookmarks, highlights + return annotations end function ReaderAnnotation:onReadSettings(config) - local bookmarks, highlights local annotations = config:readSetting("annotations") if annotations then + -- Annotation formats in crengine and mupdf are incompatible. local has_annotations = #annotations > 0 local annotations_type = has_annotations and type(annotations[1].page) - -- Annotation formats in crengine and mupdf are incompatible. 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) @@ -132,66 +110,233 @@ function ReaderAnnotation:onReadSettings(config) annotations = config:readSetting("annotations_paging") or {} config:delSetting("annotations_paging") end - bookmarks, highlights = self:getBookmarksHighlightsFromAnnotations(annotations) self.annotations = annotations - else -- old bookmarks/highlights - bookmarks = config:readSetting("bookmarks") or {} - highlights = config:readSetting("highlight") or {} - 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 -- backup incompatible old backup - local bookmarks_paging = config:readSetting("bookmarks_paging") - local highlights_paging = config:readSetting("highlight_paging") - local annotations_paging = self:getAnnotationsFromBookmarksHighlights(bookmarks_paging, highlights_paging) - config:saveSetting("annotations_paging", annotations_paging) - config:delSetting("bookmarks_paging") - config:delSetting("highlight_paging") - end - else -- incompatible format loaded, or empty - if has_bookmarks then -- backup incompatible format if not empty - annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights) - config:saveSetting("annotations_paging", annotations) + else -- first run + self.ui:registerPostInitCallback(function() + self:createAnnotations(config) + end) + end +end + +function ReaderAnnotation:createAnnotations(config) + local bookmarks = config:readSetting("bookmarks") or {} + local highlights = config:readSetting("highlight") or {} + + if config:hasNot("highlights_imported") then + -- old bookmarks were not created along the highlights + 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 - -- load compatible format - bookmarks = config:readSetting("bookmarks_rolling") or {} - highlights = config:readSetting("highlight_rolling") or {} + 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_paging = self:getAnnotationsFromBookmarksHighlights(bookmarks_paging, highlights_paging) + config:saveSetting("annotations_paging", annotations_paging) + 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 + 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_rolling = self:getAnnotationsFromBookmarksHighlights(bookmarks_rolling, highlights_rolling) + config:saveSetting("annotations_rolling", annotations_rolling) config:delSetting("bookmarks_rolling") config:delSetting("highlight_rolling") end else - 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_rolling = self:getAnnotationsFromBookmarksHighlights(bookmarks_rolling, highlights_rolling) - config:saveSetting("annotations_rolling", annotations_rolling) - config:delSetting("bookmarks_rolling") - config:delSetting("highlight_rolling") + if has_bookmarks then + 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:onCloseDocument() + self:updatePageNumbers() + self.ui.doc_settings:saveSetting("annotations", self.annotations) +end + +-- items handling + +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 - if has_bookmarks then - annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights) - config:saveSetting("annotations_rolling", annotations) + 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 - bookmarks = config:readSetting("bookmarks_paging") or {} - highlights = config:readSetting("highlight_paging") or {} - config:delSetting("bookmarks_paging") - config:delSetting("highlight_paging") + 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 - self.annotations = self:getAnnotationsFromBookmarksHighlights(bookmarks, highlights) end - self.ui.bookmark.bookmarks = bookmarks - self.view.highlight.saved = highlights + + for i, v in ipairs(self.annotations) do + if doesMatch(item, v) then + return i + end + end end -function ReaderAnnotation:onCloseDocument() - local annotations = self:getAnnotationsFromBookmarksHighlights(self.ui.bookmark.bookmarks, self.view.highlight.saved, true) - self.ui.doc_settings:saveSetting("annotations", annotations) - self.ui.doc_settings:saveSetting("bookmarks", self.ui.bookmark.bookmarks) - self.ui.doc_settings:saveSetting("highlight", self.view.highlight.saved) +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) + local index = self:getInsertionIndex(item) + table.insert(self.annotations, index, item) + return index +end + +function ReaderAnnotation:updatePageNumbers() + if self.ui.rolling then + for _, item in ipairs(self.annotations) do + item.pageno = self.ui.bookmark:getBookmarkPageString(item.page) + end + end end return ReaderAnnotation