diff --git a/main.lua b/main.lua index b0907a0..6802ae7 100644 --- a/main.lua +++ b/main.lua @@ -835,24 +835,57 @@ function Bookhoard:collectBookData() return book_data end +-- KOReader stores annotations in one of two models depending on version: +-- v2 (2024.07+): self.ui.annotation.annotations; entries carry +-- text = highlighted text, note = user note, drawer/color style, +-- pos0/pos1 = xpointer (CRE) or {page=N} table (pdf), page = xpointer +-- (CRE) or number (pdf). Sidecar key "annotations". +-- v1 (older): self.ui.bookmark.bookmarks; entries carry text = note +-- label / bookmark title, notes = highlighted text, pos0/pos1 strings. +-- Returns "v2"/"v1"/nil and the entries array. +function Bookhoard:getAnnotationStore() + if self.ui.annotation and type(self.ui.annotation.annotations) == "table" then + return "v2", self.ui.annotation.annotations + end + if self.ui.bookmark and type(self.ui.bookmark.bookmarks) == "table" then + return "v1", self.ui.bookmark.bookmarks + end + return nil, nil +end + function Bookhoard:collectAnnotations() local bookmarks = json_util.InitArray({}) local highlights = json_util.InitArray({}) local notes = json_util.InitArray({}) - if not self.ui.bookmark then + local model, entries = self:getAnnotationStore() + if not model then return bookmarks, highlights, notes end - local all_bookmarks = self.ui.bookmark.bookmarks - if not all_bookmarks then - return bookmarks, highlights, notes - end local file_sha256 = self:getFileSHA256() local total_pages = self.ui.document:getPageCount() local has_pages = self.ui.document.info.has_pages - for _, bm in ipairs(all_bookmarks) do + local function wirePos(p, fallback_page) + -- v2 pdf positions are tables {page=N,x,y}; the wire format is a + -- string — the page number is the durable locator there. + if type(p) == "table" then + return tostring(p.page or fallback_page or "") + end + return p or "" + end + + for _, bm in ipairs(entries) do + local sel_text, user_note + if model == "v2" then + sel_text = bm.text or "" -- highlighted text + user_note = bm.note or "" -- user note / bookmark label + else + sel_text = bm.notes or "" -- v1: notes held the highlighted text + user_note = bm.text or "" -- v1: text held the note/label + end + -- Thin-client policy: no per-annotation CRE lookups here (a -- getPageFromXPointer call each is the most expensive thing this -- loop can do on weak hardware). Percentages are arithmetic for @@ -867,19 +900,19 @@ function Bookhoard:collectAnnotations() local entry = { chapter = chapter, datetime = bm.datetime or "", - notes = bm.notes or "", - pos0 = bm.pos0 or "", - pos1 = bm.pos1 or "", + notes = user_note, + pos0 = wirePos(bm.pos0, bm.page), + pos1 = wirePos(bm.pos1, bm.page), page = tostring(bm.page or ""), - text = bm.text or "", + text = sel_text, book_sha256 = file_sha256, } if percentage then entry.percentage = Math.roundPercent(percentage) end - local has_text = bm.text and bm.text ~= "" - local has_notes = bm.notes and bm.notes ~= "" + local has_text = sel_text ~= "" + local has_notes = user_note ~= "" if has_text and has_notes then entry.type = "note" @@ -892,6 +925,7 @@ function Bookhoard:collectAnnotations() table.insert(highlights, entry) else entry.type = "bookmark" + entry.text = user_note -- bookmark label travels in `text` table.insert(bookmarks, entry) end end @@ -1161,30 +1195,40 @@ function Bookhoard:_doGetProgress(interactive) end function Bookhoard:applyServerAnnotations(annotations) - if not self.ui.bookmark or not self.ui.bookmark.bookmarks then - return - end if not annotations then return end + local model, entries = self:getAnnotationStore() + if not model then return end - local local_bms = self.ui.bookmark.bookmarks local changed = false local has_pages = self.ui.document.info.has_pages local function findLocalByPos0(pos0) - for i, bm in ipairs(local_bms) do - if bm.pos0 == pos0 then + for i, bm in ipairs(entries) do + local p = bm.pos0 + if type(p) == "table" then p = tostring(p.page or "") end + if p == pos0 then return i end end return nil end - local function makePageFromPos(pos0) - if has_pages then - local page = tonumber(pos0:match("(%d+)$")) - return page or pos0 + -- Get/set the highlighted text and the user note with model-appropriate + -- field names (v2: text/note; v1: notes/text). + local function getFields(bm) + if model == "v2" then + return bm.text or "", bm.note or "" + end + return bm.notes or "", bm.text or "" + end + local function setFields(bm, sel_text, user_note) + if model == "v2" then + bm.text = sel_text ~= "" and sel_text or nil + bm.note = user_note ~= "" and user_note or nil + else + bm.notes = sel_text + bm.text = user_note end - return pos0 end local function isValidPos0(pos0) @@ -1201,32 +1245,69 @@ function Bookhoard:applyServerAnnotations(annotations) return pos0:sub(1, 6) == "/body/" end + local function makePageFromPos(pos0) + if has_pages then + local page = tonumber(pos0:match("(%d+)$")) + return page or pos0 + end + return pos0 + end + + -- v2 pdf positions are {page=N} tables; CRE keeps xpointer strings. + local function makeLocalPos(pos) + if has_pages then + local page = tonumber(pos) + return page and { page = page } or nil + end + return pos + end + local function addOrUpdate(server_entry, has_text) local pos0 = server_entry.pos0 or "" if not isValidPos0(pos0) then return end + local srv_text = server_entry.text or "" + local srv_notes = server_entry.notes or "" local idx = findLocalByPos0(pos0) if idx then - local bm = local_bms[idx] - local srv_text = server_entry.text or "" - local srv_notes = server_entry.notes or "" - if (bm.text or "") ~= srv_text or (bm.notes or "") ~= srv_notes then - bm.text = srv_text - bm.notes = srv_notes - if server_entry.color then bm.color = server_entry.color end + local bm = entries[idx] + local cur_text, cur_note = getFields(bm) + if cur_text ~= srv_text or cur_note ~= srv_notes then + setFields(bm, srv_text, srv_notes) + if server_entry.color and server_entry.color ~= "" then bm.color = server_entry.color end changed = true end + elseif model == "v2" then + -- `drawer` marks a renderable highlight in v2; its absence + -- makes the entry a page bookmark (label goes in `note`). + local entry = { + datetime = server_entry.datetime ~= "" and server_entry.datetime or nil, + chapter = server_entry.chapter ~= "" and server_entry.chapter or nil, + pos0 = makeLocalPos(pos0), + pos1 = makeLocalPos(server_entry.pos1 ~= "" and server_entry.pos1 or pos0), + page = makePageFromPos(pos0), + } + if has_text then + entry.drawer = "lighten" + entry.text = srv_text + if srv_notes ~= "" then entry.note = srv_notes end + if server_entry.color and server_entry.color ~= "" then entry.color = server_entry.color end + else + if srv_text ~= "" then entry.note = srv_text end -- bookmark label + end + self.ui.annotation:addItem(entry) + changed = true else local entry = { page = makePageFromPos(pos0), pos0 = pos0, pos1 = server_entry.pos1 or pos0, datetime = server_entry.datetime or "", - text = server_entry.text or "", - notes = server_entry.notes or "", + notes = srv_text, + text = srv_notes, } - if server_entry.color then entry.color = server_entry.color end - if server_entry.chapter then entry.chapter = server_entry.chapter end - table.insert(local_bms, entry) + if server_entry.color and server_entry.color ~= "" then entry.color = server_entry.color end + if server_entry.chapter and server_entry.chapter ~= "" then entry.chapter = server_entry.chapter end + table.insert(entries, entry) changed = true end end @@ -1254,8 +1335,10 @@ function Bookhoard:applyServerAnnotations(annotations) local pos0 = del.pos0 or "" if pos0 ~= "" then local idx = findLocalByPos0(pos0) - if idx and (local_bms[idx].text or "") ~= "" then - table.remove(local_bms, idx) + local bm = idx and entries[idx] + local cur_text = bm and (getFields(bm)) or "" + if idx and cur_text ~= "" then + table.remove(entries, idx) changed = true end end @@ -1268,7 +1351,7 @@ function Bookhoard:applyServerAnnotations(annotations) if pos0 ~= "" then local idx = findLocalByPos0(pos0) if idx then - table.remove(local_bms, idx) + table.remove(entries, idx) changed = true end end @@ -1276,7 +1359,9 @@ function Bookhoard:applyServerAnnotations(annotations) end if changed then - if self.ui.bookmark.onSortBookmarks then + -- addItem() inserts v2 entries at their sorted position already; + -- explicit re-sorting is a v1-model need only. + if model == "v1" and self.ui.bookmark and self.ui.bookmark.onSortBookmarks then self.ui.bookmark:onSortBookmarks() end if self.ui.saveSettings then