Files
crosspoint-reader-mod/lib/KOReaderSync/ChapterXPathIndexer.h
cottongin dfbc931c14 mod: Phase 1 - bring forward mod-exclusive files with ActivityManager migration
Brings ~55 mod-exclusive files to the upstream-based mod/master-resync branch:

Activities (migrated to new ActivityManager pattern):
- Clock/Time: SetTimeActivity, SetTimezoneOffsetActivity, NtpSyncActivity
- Dictionary: DictionaryDefinitionActivity, DictionarySuggestionsActivity,
  DictionaryWordSelectActivity, LookedUpWordsActivity
- Bookmark: EpubReaderBookmarkSelectionActivity
- Book management: BookManageMenuActivity, EndOfBookMenuActivity
- OPDS: OpdsServerListActivity, OpdsSettingsActivity
- Utility: DirectoryPickerActivity, NumericStepperActivity

Utilities (unchanged):
- BookManager, BookSettings, BookmarkStore, BootNtpSync
- Dictionary, LookupHistory, TimeSync, OpdsServerStore

Libraries: PlaceholderCover, TableData, ChapterXPathIndexer
Scripts: inject_mod_version, generate_book_icon, preview_placeholder_cover
Docs: KOReader sync XPath mapping

Migration changes:
- ActivityWithSubactivity -> Activity base class
- Callback constructors -> finish()/setResult() pattern
- enterNewActivity() -> startActivityForResult()
- Activity::RenderLock&& -> RenderLock&&

These files won't compile yet - they reference mod settings and I18n
strings that will be added in subsequent phases.

Made-with: Cursor
2026-03-07 15:10:00 -05:00

68 lines
2.5 KiB
C++

#pragma once
#include <Epub.h>
#include <memory>
#include <string>
/**
* Lightweight XPath/progress bridge for KOReader sync.
*
* Why this exists:
* - CrossPoint stores reading position as chapter/page.
* - KOReader sync uses XPath + percentage.
*
* This utility reparses exactly one spine XHTML item with Expat and builds
* transient text anchors (<xpath, textOffset>) so we can translate in both
* directions without keeping a full DOM in memory.
*
* Design constraints (ESP32-C3):
* - No persistent full-book structures.
* - Parse-on-demand and free memory immediately.
* - Keep fallback behavior deterministic if parsing/matching fails.
*/
class ChapterXPathIndexer {
public:
/**
* Convert an intra-spine progress ratio to the nearest element-level XPath.
*
* @param epub Loaded EPUB instance
* @param spineIndex Current spine item index
* @param intraSpineProgress Position within the spine item [0.0, 1.0]
* @return Best matching XPath for KOReader, or empty string on failure
*/
static std::string findXPathForProgress(const std::shared_ptr<Epub>& epub, int spineIndex, float intraSpineProgress);
/**
* Resolve a KOReader XPath to an intra-spine progress ratio.
*
* Matching strategy:
* 1) exact anchor path match,
* 2) index-insensitive path match,
* 3) ancestor fallback.
*
* @param epub Loaded EPUB instance
* @param spineIndex Spine item index to parse
* @param xpath Incoming KOReader XPath
* @param outIntraSpineProgress Resolved position within spine [0.0, 1.0]
* @param outExactMatch True only for full exact path match
* @return true if any match was resolved; false means caller should fallback
*/
static bool findProgressForXPath(const std::shared_ptr<Epub>& epub, int spineIndex, const std::string& xpath,
float& outIntraSpineProgress, bool& outExactMatch);
/**
* Parse DocFragment index from KOReader-style path segment:
* /body/DocFragment[N]/body/...
*
* KOReader uses 1-based DocFragment indices; N is converted to the 0-based
* spine index stored in outSpineIndex (i.e. outSpineIndex = N - 1).
*
* @param xpath KOReader XPath
* @param outSpineIndex 0-based spine index derived from DocFragment[N]
* @return true when DocFragment[N] exists and N is a valid integer >= 1
* (converted to 0-based outSpineIndex); false otherwise
*/
static bool tryExtractSpineIndexFromXPath(const std::string& xpath, int& outSpineIndex);
};