Review comments
This commit is contained in:
@@ -20,6 +20,7 @@ namespace {
|
||||
struct XPathAnchor {
|
||||
size_t textOffset = 0;
|
||||
std::string xpath;
|
||||
std::string xpathNoIndex; // precomputed removeIndices(xpath)
|
||||
};
|
||||
|
||||
struct StackNode {
|
||||
@@ -129,8 +130,7 @@ struct ParserState {
|
||||
bool found = false;
|
||||
|
||||
for (const auto& anchor : anchors) {
|
||||
const std::string normalizedAnchor = normalizeXPath(anchor.xpath);
|
||||
const std::string anchorPath = ignoreIndices ? removeIndices(normalizedAnchor) : normalizedAnchor;
|
||||
const std::string& anchorPath = ignoreIndices ? anchor.xpathNoIndex : anchor.xpath;
|
||||
if (anchorPath == probe) {
|
||||
const int depth = pathDepth(anchorPath);
|
||||
if (!found || depth > bestDepth || (depth == bestDepth && anchor.textOffset < bestOffset)) {
|
||||
@@ -221,12 +221,13 @@ struct ParserState {
|
||||
}
|
||||
|
||||
if (!stack.back().hasTextAnchor) {
|
||||
anchors.push_back({totalTextBytes, currentXPath()});
|
||||
const std::string xpath = currentXPath();
|
||||
anchors.push_back({totalTextBytes, xpath, removeIndices(xpath)});
|
||||
stack.back().hasTextAnchor = true;
|
||||
} else if (anchors.empty() || totalTextBytes - anchors.back().textOffset >= 192) {
|
||||
const std::string xpath = currentXPath();
|
||||
if (anchors.empty() || anchors.back().xpath != xpath) {
|
||||
anchors.push_back({totalTextBytes, xpath});
|
||||
anchors.push_back({totalTextBytes, xpath, removeIndices(xpath)});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,6 +359,17 @@ void XMLCALL onCharacterData(void* userData, const XML_Char* text, const int len
|
||||
}
|
||||
|
||||
void XMLCALL onDefaultHandlerExpand(void* userData, const XML_Char* text, const int len) {
|
||||
// The default handler fires for comments, PIs, DOCTYPE, and entity references.
|
||||
// Only forward entity references (&..;) to avoid skewing text offsets with
|
||||
// non-visible markup.
|
||||
if (len < 3 || text[0] != '&' || text[len - 1] != ';') {
|
||||
return;
|
||||
}
|
||||
for (int i = 1; i < len - 1; ++i) {
|
||||
if (text[i] == '<' || text[i] == '>') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto* state = static_cast<ParserState*>(userData);
|
||||
state->onCharacterData(text, len);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ KOReaderPosition ProgressMapper::toKOReader(const std::shared_ptr<Epub>& epub, c
|
||||
// to a synthetic chapter-level path if parsing fails.
|
||||
result.xpath = ChapterXPathIndexer::findXPathForProgress(epub, pos.spineIndex, intraSpineProgress);
|
||||
if (result.xpath.empty()) {
|
||||
result.xpath = generateXPath(pos.spineIndex, pos.pageNumber, pos.totalPages);
|
||||
result.xpath = generateXPath(pos.spineIndex);
|
||||
}
|
||||
|
||||
// Get chapter info for logging
|
||||
@@ -150,7 +150,7 @@ CrossPointPosition ProgressMapper::toCrossPoint(const std::shared_ptr<Epub>& epu
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ProgressMapper::generateXPath(int spineIndex, int pageNumber, int totalPages) {
|
||||
std::string ProgressMapper::generateXPath(int spineIndex) {
|
||||
// Fallback path when element-level XPath extraction is unavailable.
|
||||
// KOReader uses 1-based XPath predicates; spineIndex is 0-based internally.
|
||||
return "/body/DocFragment[" + std::to_string(spineIndex + 1) + "]/body";
|
||||
|
||||
@@ -68,7 +68,7 @@ class ProgressMapper {
|
||||
private:
|
||||
/**
|
||||
* Generate XPath for KOReader compatibility.
|
||||
* Fallback format: /body/DocFragment[spineIndex]/body
|
||||
* Fallback format: /body/DocFragment[spineIndex + 1]/body
|
||||
*/
|
||||
static std::string generateXPath(int spineIndex, int pageNumber, int totalPages);
|
||||
static std::string generateXPath(int spineIndex);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user