-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathmerge.lua
More file actions
115 lines (99 loc) · 3.79 KB
/
merge.lua
File metadata and controls
115 lines (99 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
local function parse_datetime_cached()
local cache = {}
return function(str)
if not str then return 0 end
if cache[str] then return cache[str] end
local y, m, d, h, min, s = str:match("(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)")
local t = os.time{year=tonumber(y), month=tonumber(m), day=tonumber(d), hour=tonumber(h), min=tonumber(min), sec=tonumber(s)}
cache[str] = t
return t
end
end
local parse_datetime = parse_datetime_cached()
local function get_datetime(item)
return item.datetime_updated or item.datetime
end
local function get_newer(item1, item2)
local t1 = parse_datetime(get_datetime(item1))
local t2 = parse_datetime(get_datetime(item2))
return t1 >= t2 and item1 or item2
end
-- Generate a stable key using pos0+pos1 (XPath positions) which are consistent
-- across devices regardless of font size, screen size, or page layout settings.
-- Falls back to page + text hash for older annotations without position data.
local function generate_key(highlight)
-- Use pos0 + pos1 as primary key (stable across devices)
if highlight.pos0 and highlight.pos1 then
return string.format("%s|%s", highlight.pos0, highlight.pos1)
end
-- Fallback: use page + text hash for annotations without position data
local text = highlight.text or ""
local hash = tostring(#text) .. ":" .. (text:sub(1, 20) or "")
return string.format("%s|%s", highlight.page or "?", hash)
end
local function convert_to_map(highlights)
local map = {}
for i = 1, #highlights do
local h = highlights[i]
map[generate_key(h)] = h
end
return map
end
local function merge_highlights(local_annotations, server_annotations, last_sync_annotations)
local local_map = convert_to_map(local_annotations or {})
local server_map = convert_to_map(server_annotations or {})
local last_sync_map = convert_to_map(last_sync_annotations or {})
local merged = {}
-- Processa os highlights locais
for key, local_highlight in pairs(local_map) do
local server_highlight = server_map[key]
local last_sync_highlight = last_sync_map[key]
if not (server_highlight == nil and last_sync_highlight ~= nil) then
merged[key] = local_highlight
end
end
-- Processa highlights do servidor
for key, server_highlight in pairs(server_map) do
if last_sync_map[key] ~= nil and local_map[key] == nil then
-- foi deletado localmente, ignorar
else
if not local_map[key] then
merged[key] = server_highlight
else
merged[key] = get_newer(server_highlight, local_map[key])
end
end
end
-- Converte o resultado de volta para array
local merged_annotations = {}
for _, h in pairs(merged) do
merged_annotations[#merged_annotations+1] = h
end
-- Ordenação simples por pageno e pos0
table.sort(merged_annotations, function(a, b)
if a.pageno ~= b.pageno then
return (a.pageno or 0) < (b.pageno or 0)
end
if not a.pos0 then return true end
if not b.pos0 then return false end
-- Consider case where positions are tables
-- with keys x, y, zoom, page, rotation,
-- as is the case with pdf highlights
if type(a.pos0) == "table" then
if type(b.pos0) == "table" then
return a.pos0.y < b.pos0.y or
(a.pos0.y == b.pos0.y and a.pos0.x < b.pos0.y)
end
-- Posistions can't be compared
return false
elseif type(b.pos0) == "table" then
-- Posistions can't be compared
return true
end
return a.pos0 < b.pos0
end)
return merged_annotations
end
local M = {}
M.Merge_highlights = merge_highlights
return M