fix: KOReader sync drift when syncing at chapter start (#2245)
This commit is contained in:
@@ -56,6 +56,64 @@ int parseTextNodeIndex(const std::string& xpath) {
|
||||
return val > 0 ? val : 1;
|
||||
}
|
||||
|
||||
bool isChapterStartXPath(const std::string& xpath) {
|
||||
if (xpath.find("/p[") != std::string::npos || xpath.find("/li[") != std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr char kDocFragment[] = "/body/DocFragment[";
|
||||
const size_t docFragPos = xpath.find(kDocFragment);
|
||||
if (docFragPos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
const size_t docFragEnd = xpath.find(']', docFragPos + strlen(kDocFragment));
|
||||
if (docFragEnd == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
if (docFragEnd + 1 == xpath.size()) {
|
||||
return true;
|
||||
}
|
||||
if (xpath[docFragEnd + 1] == '.') {
|
||||
if (docFragEnd + 2 >= xpath.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = docFragEnd + 2; i < xpath.size(); i++) {
|
||||
if (xpath[i] != '0') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr char kDocBody[] = "]/body";
|
||||
const size_t docBodyPos = xpath.find(kDocBody);
|
||||
if (docBodyPos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
size_t bodyContentStart = docBodyPos + strlen(kDocBody);
|
||||
if (bodyContentStart == xpath.size()) {
|
||||
return true;
|
||||
}
|
||||
if (xpath[bodyContentStart] != '/') {
|
||||
return false;
|
||||
}
|
||||
bodyContentStart++;
|
||||
if (bodyContentStart == xpath.size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const size_t dotPos = xpath.rfind('.');
|
||||
if (dotPos == std::string::npos || dotPos <= bodyContentStart || dotPos + 1 >= xpath.size()) {
|
||||
return false;
|
||||
}
|
||||
if (xpath.find('/', bodyContentStart) != std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = dotPos + 1; i < xpath.size(); i++) {
|
||||
if (xpath[i] != '0') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parsed representation of one step in the XPath ancestry.
|
||||
struct XPathStep {
|
||||
char tag[12]; // element name, null-terminated
|
||||
@@ -586,10 +644,12 @@ CrossPointPosition ProgressMapper::toCrossPoint(const std::shared_ptr<Epub>& epu
|
||||
}
|
||||
|
||||
float intra = 0.0f;
|
||||
bool resolvedIntra = false;
|
||||
if (useAncestry) {
|
||||
ParagraphStreamer s(xpathSteps, xpathStepCount, xpathChar, xpathTextNode);
|
||||
if (streamSpine(epub, result.spineIndex, s) && s.found()) {
|
||||
intra = s.progress();
|
||||
resolvedIntra = true;
|
||||
const int pAtMatch = s.getParagraphAtMatch();
|
||||
if (pAtMatch > 0) {
|
||||
result.paragraphIndex = static_cast<uint16_t>(pAtMatch);
|
||||
@@ -615,11 +675,17 @@ CrossPointPosition ProgressMapper::toCrossPoint(const std::shared_ptr<Epub>& epu
|
||||
ParagraphStreamer s(xpathP, xpathChar, xpathTextNode);
|
||||
if (streamSpine(epub, result.spineIndex, s) && s.found()) {
|
||||
intra = s.progress();
|
||||
resolvedIntra = true;
|
||||
LOG_DBG("PM", "XPath p[%d]/text()[%d]+%d -> %.1f%% (target=%zu total=%zu)", xpathP, xpathTextNode, xpathChar,
|
||||
intra * 100, s.getTargetVisChars(), s.getTotalVisChars());
|
||||
}
|
||||
}
|
||||
if (intra <= 0.0f) {
|
||||
if (!resolvedIntra && xpathSpine >= 0 && xpathSpine < spineCount && isChapterStartXPath(koPos.xpath)) {
|
||||
intra = 0.0f;
|
||||
resolvedIntra = true;
|
||||
LOG_DBG("PM", "Chapter-start XPath %s -> spine=%d page start", koPos.xpath.c_str(), result.spineIndex);
|
||||
}
|
||||
if (!resolvedIntra) {
|
||||
const size_t bytesIn = (targetBytes > prevCum) ? (targetBytes - prevCum) : 0;
|
||||
intra = std::max(0.0f, std::min(1.0f, static_cast<float>(bytesIn) / static_cast<float>(spineSize)));
|
||||
}
|
||||
@@ -696,4 +762,4 @@ std::string ProgressMapper::generateXPath(const std::shared_ptr<Epub>& epub, int
|
||||
|
||||
const int p = s.paragraphCount();
|
||||
return (p > 0) ? base + "/p[" + std::to_string(p) + "]" : base;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user