mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
TOC: validate and fix misordered page numbers (#5907)
Our code expects (rightfully) the TOC items to be ordered and to have increasing page numbers, but we may occasionally not get that from the engines (usually, because of bugs or duplicated IDs in the document). Check for that and fix page numbers (possibly making them wrong) to have a working TOC.
This commit is contained in:
@@ -10,8 +10,10 @@ local Geom = require("ui/geometry")
|
||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||
local Menu = require("ui/widget/menu")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local logger = require("logger")
|
||||
local _ = require("gettext")
|
||||
local Screen = Device.screen
|
||||
local T = require("ffi/util").template
|
||||
|
||||
local ReaderToc = InputContainer:new{
|
||||
toc = nil,
|
||||
@@ -89,6 +91,111 @@ function ReaderToc:fillToc()
|
||||
end
|
||||
end
|
||||
self.toc = self.ui.document:getToc()
|
||||
self:validateAndFixToc()
|
||||
end
|
||||
|
||||
function ReaderToc:validateAndFixToc()
|
||||
-- Our code expects (rightfully) the TOC items to be ordered and to have
|
||||
-- increasing page numbers, but we may occasionally not get that from the
|
||||
-- engines (usually, because of bugs or duplicated IDs in the document).
|
||||
local toc = self.toc
|
||||
local first = 1
|
||||
local last = #toc
|
||||
|
||||
-- For testing: shuffle a bit a valid TOC and make it randomely invalid
|
||||
-- for i = first, last do
|
||||
-- toc[i].page = toc[i].page + math.random(10) - 5
|
||||
-- end
|
||||
|
||||
-- Do a cheap quick scan first
|
||||
logger.dbg("validateAndFixToc(): quick scan")
|
||||
local has_bogus
|
||||
local cur_page = 0
|
||||
for i = first, last do
|
||||
local page = toc[i].page
|
||||
if page < cur_page then
|
||||
has_bogus = true
|
||||
break
|
||||
end
|
||||
cur_page = page
|
||||
end
|
||||
if not has_bogus then -- no TOC items, or all are valid
|
||||
logger.dbg("validateAndFixToc(): TOC is fine")
|
||||
return
|
||||
end
|
||||
logger.dbg("validateAndFixToc(): TOC needs fixing")
|
||||
|
||||
-- Bad ordering previously noticed: try to fix the wrong items' page
|
||||
-- by setting it to the previous or next good item page.
|
||||
local nb_bogus = 0
|
||||
local nb_fixed_pages = 0
|
||||
-- We fix only one bogus item per loop, taking the option that
|
||||
-- changes the least nb of items.
|
||||
-- Sample cases, *N* being the page noticed as bogus:
|
||||
-- 1 4 57 *6* 9 13 24 best to reset 57 to 4 (or 6, or 5)
|
||||
-- 1 4 57 *6* 79 84 96 best to reset 6 to 57 (or 79 or 68)
|
||||
-- 1 4 55 56 57 *6* 7 8 9 10 best to reset 55,56,57 to 4
|
||||
-- 1 4 55 56 57 *6* 7 60 62 best to reset 6,7 to 57
|
||||
-- (These cases are met in the following code with cur_page=57 and page=6)
|
||||
cur_page = 0
|
||||
for i = first, last do
|
||||
local page = toc[i].fixed_page or toc[i].page
|
||||
if page >= cur_page then
|
||||
cur_page = page
|
||||
else
|
||||
-- Bogus page (or bogus previous page)
|
||||
nb_bogus = nb_bogus + 1
|
||||
-- See how many pages we'd need fixing on either side
|
||||
local nb_prev = 0
|
||||
for j = i-1, first, -1 do
|
||||
local ppage = toc[j].fixed_page or toc[j].page
|
||||
if ppage <= page then
|
||||
break
|
||||
else
|
||||
nb_prev = nb_prev + 1
|
||||
end
|
||||
end
|
||||
local nb_next = 1
|
||||
for j = i+1, last do
|
||||
local npage = toc[j].fixed_page or toc[j].page
|
||||
if npage >= cur_page then
|
||||
break
|
||||
else
|
||||
nb_next = nb_next + 1
|
||||
end
|
||||
end
|
||||
logger.dbg("BOGUS TOC:", i, page, ">", i-1, cur_page, "-", nb_prev, nb_next)
|
||||
if nb_prev <= nb_next then -- less changes when fixing previous pages
|
||||
local fixed_page
|
||||
if i-nb_prev-1 >= 1 then
|
||||
fixed_page = toc[i-nb_prev-1].fixed_page or toc[i-nb_prev-1].page
|
||||
else
|
||||
fixed_page = 1
|
||||
end
|
||||
for j = i-1, i-nb_prev, -1 do
|
||||
toc[j].fixed_page = fixed_page
|
||||
logger.dbg(" fix prev", j, toc[j].page, "=>", fixed_page)
|
||||
end
|
||||
else
|
||||
local fixed_page = cur_page -- (might be better to use next one, but not safer)
|
||||
for j = i, i+nb_next-1 do
|
||||
toc[j].fixed_page = fixed_page
|
||||
logger.dbg(" fix next", j, toc[j].page, "=>", fixed_page)
|
||||
end
|
||||
end
|
||||
cur_page = toc[i].fixed_page or toc[i].page
|
||||
end
|
||||
end
|
||||
if nb_bogus > 0 then
|
||||
for i = first, last do
|
||||
if toc[i].fixed_page and toc[i].fixed_page ~= toc[i].page then
|
||||
toc[i].orig_page = toc[i].page -- keep the original one, for display only
|
||||
toc[i].page = toc[i].fixed_page
|
||||
nb_fixed_pages = nb_fixed_pages + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
logger.info(string.format("TOC had %d bogus page numbers: fixed %d items to keep them ordered.", nb_bogus, nb_fixed_pages))
|
||||
end
|
||||
|
||||
function ReaderToc:getTocIndexByPage(pn_or_xp)
|
||||
@@ -280,6 +387,9 @@ function ReaderToc:onShowToc()
|
||||
for _,v in ipairs(self.toc) do
|
||||
v.text = self.toc_indent:rep(v.depth-1)..self:cleanUpTocTitle(v.title)
|
||||
v.mandatory = v.page
|
||||
if v.orig_page then -- bogus page fixed: show original page number
|
||||
v.mandatory = T("(%1) %2", v.orig_page, v.page)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user