#pragma once #include #include #include /** * CrossPoint position representation. */ struct CrossPointPosition { int spineIndex; // Current spine item (chapter) index int pageNumber; // Current page within the spine item int totalPages; // Total pages in the current spine item }; /** * KOReader position representation. */ struct KOReaderPosition { std::string xpath; // XPath-like progress string float percentage; // Progress percentage (0.0 to 1.0) }; /** * Maps between CrossPoint and KOReader position formats. * * CrossPoint tracks position as (spineIndex, pageNumber). * KOReader uses XPath-like strings + percentage. * * CrossPoint first tries to extract an element-level XPath by reparsing the * current spine XHTML and mapping intra-spine progress to text anchors. * If extraction fails, it falls back to a synthetic chapter-level XPath. */ class ProgressMapper { public: /** * Convert CrossPoint position to KOReader format. * * @param epub The EPUB book * @param pos CrossPoint position * @return KOReader position */ static KOReaderPosition toKOReader(const std::shared_ptr& epub, const CrossPointPosition& pos); /** * Convert KOReader position to CrossPoint format. * * Note: The returned pageNumber may be approximate since different * rendering settings produce different page counts. * * @param epub The EPUB book * @param koPos KOReader position * @param currentSpineIndex Index of the currently open spine item (for density estimation) * @param totalPagesInCurrentSpine Total pages in the current spine item (for density estimation) * @return CrossPoint position */ static CrossPointPosition toCrossPoint(const std::shared_ptr& epub, const KOReaderPosition& koPos, int currentSpineIndex = -1, int totalPagesInCurrentSpine = 0); private: /** * Generate XPath for KOReader compatibility. * Fallback format: /body/DocFragment[spineIndex]/body */ static std::string generateXPath(int spineIndex, int pageNumber, int totalPages); };