From 0a19b840a69089346ce12aa63f77b5104816c5b9 Mon Sep 17 00:00:00 2001 From: Binhao Qian Date: Sat, 21 Feb 2026 07:04:02 +0800 Subject: [PATCH 1/2] Fix epub files with toc using relative file path. --- lib/libebook/ebook_epub.cpp | 43 +++++++++++++++++------ lib/libebook/ebook_epub.h | 3 +- lib/libebook/helperxmlhandler_epubtoc.cpp | 15 ++++---- lib/libebook/helperxmlhandler_epubtoc.h | 3 +- 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/lib/libebook/ebook_epub.cpp b/lib/libebook/ebook_epub.cpp index 51755a35..d110fc88 100644 --- a/lib/libebook/ebook_epub.cpp +++ b/lib/libebook/ebook_epub.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -235,17 +236,26 @@ bool EBook_EPUB::parseBookinfo() if ( content_parser.tocname.isEmpty() ) return false; - // All the files, including TOC, are relative to the container_parser.contentPath - m_documentRoot.clear(); + // TOC is relative to the container_parser.contentPath + QString contentRoot; int sep = container_parser.contentPath.lastIndexOf( '/' ); if ( sep != -1 ) - m_documentRoot = container_parser.contentPath.left( sep + 1 ); // Keep the trailing slash + contentRoot = container_parser.contentPath.left( sep + 1 ); // Keep the trailing slash + + QString tocPath = combinePath( contentRoot, content_parser.tocname ); + + // All pages are relative to the container_parser.tocname + QString tocRoot; + sep = tocPath.lastIndexOf( '/' ); + + if ( sep != -1 ) + tocRoot = tocPath.left( sep + 1 ); // Keep the trailing slash // Parse the TOC - HelperXmlHandler_EpubTOC toc_parser( this ); + HelperXmlHandler_EpubTOC toc_parser( this, tocRoot ); - if ( !parseXML( content_parser.tocname, &toc_parser ) ) + if ( !parseXML( tocPath, &toc_parser ) ) return false; // Get the data @@ -256,12 +266,18 @@ bool EBook_EPUB::parseBookinfo() // Move the manifest entries into the list Q_FOREACH ( QString f, content_parser.manifest.values() ) - m_ebookManifest.push_back( pathToUrl( f ) ); + { + QString combined = combinePath( contentRoot, f ); + m_ebookManifest.push_back( pathToUrl( combined ) ); + } for ( const auto& si : qAsConst( content_parser.spine ) ) { if ( content_parser.manifest.contains( si ) ) - m_spinePath.push_back( content_parser.manifest[ si ] ); + { + QString combined = combinePath( contentRoot, content_parser.manifest[ si ] ); + m_spinePath.push_back( combined ); + } } // Copy the manifest information and fill up the other maps if we have it @@ -372,12 +388,11 @@ bool EBook_EPUB::getFileAsBinary( QByteArray& data, const QString& path ) const { // Retrieve the file size struct zip_stat fileinfo; - QString completeUrl; + + QString completeUrl = path; if ( !path.isEmpty() && path[0] == '/' ) - completeUrl = m_documentRoot + path.mid( 1 ); - else - completeUrl = m_documentRoot + path; + completeUrl = path.mid( 1 ); //qDebug("URL requested: %s (%s)", qPrintable(path), qPrintable(completeUrl)); @@ -413,3 +428,9 @@ bool EBook_EPUB::getFileAsBinary( QByteArray& data, const QString& path ) const zip_fclose( file ); return true; } + +QString EBook_EPUB::combinePath( const QString& baseDir, const QString& path ) +{ + QString combined = QDir( baseDir ).filePath( path ); + return QDir::cleanPath( combined ); +} diff --git a/lib/libebook/ebook_epub.h b/lib/libebook/ebook_epub.h index 7f2e3708..3cf90218 100644 --- a/lib/libebook/ebook_epub.h +++ b/lib/libebook/ebook_epub.h @@ -190,13 +190,14 @@ class EBook_EPUB : public EBook bool getFileAsString( QString& str, const QString& path ) const; bool getFileAsBinary( QByteArray& data, const QString& path ) const; + static QString combinePath( const QString& baseDir, const QString& path ); + // ZIP archive fd and structs QFile m_epubFile; struct zip* m_zipFile; // Ebook info QString m_title; - QString m_documentRoot; // List of files in the ebook QList m_ebookManifest; diff --git a/lib/libebook/helperxmlhandler_epubtoc.cpp b/lib/libebook/helperxmlhandler_epubtoc.cpp index 863fd628..c811f5b4 100644 --- a/lib/libebook/helperxmlhandler_epubtoc.cpp +++ b/lib/libebook/helperxmlhandler_epubtoc.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -24,12 +25,13 @@ #include "helperxmlhandler_epubtoc.h" -HelperXmlHandler_EpubTOC::HelperXmlHandler_EpubTOC( EBook_EPUB* epub ) +HelperXmlHandler_EpubTOC::HelperXmlHandler_EpubTOC( EBook_EPUB* epub, const QString& documentRoot ) + : m_inNavMap( false ), + m_inText( false ), + m_indent( 0 ), + m_epub( epub ), + m_documentRoot( documentRoot ) { - m_epub = epub; - m_inNavMap = false; - m_inText = false; - m_indent = 0; } bool HelperXmlHandler_EpubTOC::startElement( const QString&, const QString& localName, const QString&, const QXmlAttributes& atts ) @@ -103,7 +105,8 @@ void HelperXmlHandler_EpubTOC::checkNewTocEntry() { EBookTocEntry entry; entry.name = m_lastTitle; - entry.url = m_epub->pathToUrl( m_lastId ); + QString combined = QDir( m_documentRoot ).filePath( m_lastId ); + entry.url = m_epub->pathToUrl( QDir::cleanPath( combined ) ); entry.iconid = EBookTocEntry::IMAGE_AUTO; entry.indent = m_indent - 1; diff --git a/lib/libebook/helperxmlhandler_epubtoc.h b/lib/libebook/helperxmlhandler_epubtoc.h index 90cd083b..3571519a 100644 --- a/lib/libebook/helperxmlhandler_epubtoc.h +++ b/lib/libebook/helperxmlhandler_epubtoc.h @@ -33,7 +33,7 @@ class EBook_EPUB; class HelperXmlHandler_EpubTOC : public QXmlDefaultHandler { public: - HelperXmlHandler_EpubTOC( EBook_EPUB* epub ); + HelperXmlHandler_EpubTOC( EBook_EPUB* epub, const QString& documentRoot ); QList< EBookTocEntry > entries; @@ -50,6 +50,7 @@ class HelperXmlHandler_EpubTOC : public QXmlDefaultHandler QString m_lastId; QString m_lastTitle; EBook_EPUB* m_epub; + QString m_documentRoot; }; #endif // HELPERXMLHANDLER_EPUBTOC_H From 5f8812a943f9cb8421e42336b565a9b8702693a6 Mon Sep 17 00:00:00 2001 From: Binhao Qian Date: Mon, 23 Feb 2026 06:45:16 +0800 Subject: [PATCH 2/2] Resolve full path directly when parsing content.opf and toc.ncx file. --- lib/libebook/ebook_epub.cpp | 51 ++++++++----------- lib/libebook/ebook_epub.h | 7 ++- lib/libebook/helperxmlhandler_epubcontent.cpp | 10 ++-- lib/libebook/helperxmlhandler_epubcontent.h | 7 +-- lib/libebook/helperxmlhandler_epubtoc.cpp | 8 +-- lib/libebook/helperxmlhandler_epubtoc.h | 4 +- 6 files changed, 43 insertions(+), 44 deletions(-) diff --git a/lib/libebook/ebook_epub.cpp b/lib/libebook/ebook_epub.cpp index d110fc88..1eea519f 100644 --- a/lib/libebook/ebook_epub.cpp +++ b/lib/libebook/ebook_epub.cpp @@ -227,35 +227,22 @@ bool EBook_EPUB::parseBookinfo() return false; // Parse the content.opf - HelperXmlHandler_EpubContent content_parser; + // TOC is relative to the container_parser.contentPath + QString parentPath = getParentPath( container_parser.contentPath ); + HelperXmlHandler_EpubContent content_parser( parentPath ); if ( !parseXML( container_parser.contentPath, &content_parser ) ) return false; // At least the TOC must be present - if ( content_parser.tocname.isEmpty() ) + if ( content_parser.tocPath.isEmpty() ) return false; - // TOC is relative to the container_parser.contentPath - QString contentRoot; - int sep = container_parser.contentPath.lastIndexOf( '/' ); - - if ( sep != -1 ) - contentRoot = container_parser.contentPath.left( sep + 1 ); // Keep the trailing slash - - QString tocPath = combinePath( contentRoot, content_parser.tocname ); - - // All pages are relative to the container_parser.tocname - QString tocRoot; - sep = tocPath.lastIndexOf( '/' ); - - if ( sep != -1 ) - tocRoot = tocPath.left( sep + 1 ); // Keep the trailing slash - // Parse the TOC - HelperXmlHandler_EpubTOC toc_parser( this, tocRoot ); + parentPath = getParentPath( content_parser.tocPath ); + HelperXmlHandler_EpubTOC toc_parser( this, parentPath ); - if ( !parseXML( tocPath, &toc_parser ) ) + if ( !parseXML( content_parser.tocPath, &toc_parser ) ) return false; // Get the data @@ -266,18 +253,12 @@ bool EBook_EPUB::parseBookinfo() // Move the manifest entries into the list Q_FOREACH ( QString f, content_parser.manifest.values() ) - { - QString combined = combinePath( contentRoot, f ); - m_ebookManifest.push_back( pathToUrl( combined ) ); - } + m_ebookManifest.push_back( pathToUrl( f ) ); for ( const auto& si : qAsConst( content_parser.spine ) ) { if ( content_parser.manifest.contains( si ) ) - { - QString combined = combinePath( contentRoot, content_parser.manifest[ si ] ); - m_spinePath.push_back( combined ); - } + m_spinePath.push_back( content_parser.manifest[ si ] ); } // Copy the manifest information and fill up the other maps if we have it @@ -429,8 +410,18 @@ bool EBook_EPUB::getFileAsBinary( QByteArray& data, const QString& path ) const return true; } -QString EBook_EPUB::combinePath( const QString& baseDir, const QString& path ) +QString EBook_EPUB::combinePath( const QString& baseDirPath, const QString& path ) { - QString combined = QDir( baseDir ).filePath( path ); + QString combined = QDir( baseDirPath ).filePath( path ); return QDir::cleanPath( combined ); } + +QString EBook_EPUB::getParentPath( const QString& path ) +{ + int sep = path.lastIndexOf( '/' ); + + if ( sep != -1 ) + return path.left( sep + 1 ); // Keep the trailing slash + + return ""; +} diff --git a/lib/libebook/ebook_epub.h b/lib/libebook/ebook_epub.h index 3cf90218..3ba928c9 100644 --- a/lib/libebook/ebook_epub.h +++ b/lib/libebook/ebook_epub.h @@ -179,6 +179,10 @@ class EBook_EPUB : public EBook protected: void loadNavigation( Navigator& nav ) override; + public: + // Combine path and resolve relative path + static QString combinePath( const QString& baseDirPath, const QString& path ); + private: // Parses the XML file using a specified parser bool parseXML( const QString& uri, QXmlDefaultHandler* reader ); @@ -190,7 +194,8 @@ class EBook_EPUB : public EBook bool getFileAsString( QString& str, const QString& path ) const; bool getFileAsBinary( QByteArray& data, const QString& path ) const; - static QString combinePath( const QString& baseDir, const QString& path ); + // Get parent path + static QString getParentPath( const QString& path ); // ZIP archive fd and structs QFile m_epubFile; diff --git a/lib/libebook/helperxmlhandler_epubcontent.cpp b/lib/libebook/helperxmlhandler_epubcontent.cpp index 8e9b8e8a..451b6b9e 100644 --- a/lib/libebook/helperxmlhandler_epubcontent.cpp +++ b/lib/libebook/helperxmlhandler_epubcontent.cpp @@ -19,12 +19,14 @@ #include #include +#include "ebook_epub.h" #include "helperxmlhandler_epubcontent.h" -HelperXmlHandler_EpubContent::HelperXmlHandler_EpubContent() +HelperXmlHandler_EpubContent::HelperXmlHandler_EpubContent( const QString& basePath ) + : m_state( STATE_NONE ), + m_basePath( basePath ) { - m_state = STATE_NONE; } bool HelperXmlHandler_EpubContent::startElement( const QString&, const QString& localName, const QString&, const QXmlAttributes& atts ) @@ -48,10 +50,10 @@ bool HelperXmlHandler_EpubContent::startElement( const QString&, const QString& if ( idx_id == -1 || idx_href == -1 || idx_mtype == -1 ) return false; - manifest[ atts.value( idx_id ) ] = atts.value( idx_href ); + manifest[ atts.value( idx_id ) ] = EBook_EPUB::combinePath( m_basePath, atts.value( idx_href ) ); if ( atts.value( idx_mtype ) == "application/x-dtbncx+xml" ) - tocname = atts.value( idx_href ); + tocPath = EBook_EPUB::combinePath( m_basePath, atts.value( idx_href ) ); //qDebug() << "MANIFEST: " << atts.value( idx_id ) << "->" << atts.value( idx_href ); } diff --git a/lib/libebook/helperxmlhandler_epubcontent.h b/lib/libebook/helperxmlhandler_epubcontent.h index 0e27b4e6..2354bc2e 100644 --- a/lib/libebook/helperxmlhandler_epubcontent.h +++ b/lib/libebook/helperxmlhandler_epubcontent.h @@ -30,7 +30,7 @@ class QXmlAttributes; class HelperXmlHandler_EpubContent : public QXmlDefaultHandler { public: - HelperXmlHandler_EpubContent(); + HelperXmlHandler_EpubContent( const QString& basePath ); // Keep the tag-associated metadata QMap< QString, QString > metadata; @@ -41,8 +41,8 @@ class HelperXmlHandler_EpubContent : public QXmlDefaultHandler // Spine storage QList< QString > spine; - // TOC (NCX) filename - QString tocname; + // TOC (NCX) file path + QString tocPath; private: enum State @@ -60,6 +60,7 @@ class HelperXmlHandler_EpubContent : public QXmlDefaultHandler // Tracking State m_state; QString m_tagname; + QString m_basePath; }; #endif // HELPERXMLHANDLER_EPUBCONTENT_H diff --git a/lib/libebook/helperxmlhandler_epubtoc.cpp b/lib/libebook/helperxmlhandler_epubtoc.cpp index c811f5b4..4fd67a48 100644 --- a/lib/libebook/helperxmlhandler_epubtoc.cpp +++ b/lib/libebook/helperxmlhandler_epubtoc.cpp @@ -25,12 +25,12 @@ #include "helperxmlhandler_epubtoc.h" -HelperXmlHandler_EpubTOC::HelperXmlHandler_EpubTOC( EBook_EPUB* epub, const QString& documentRoot ) +HelperXmlHandler_EpubTOC::HelperXmlHandler_EpubTOC( EBook_EPUB* epub, const QString& basePath ) : m_inNavMap( false ), m_inText( false ), m_indent( 0 ), m_epub( epub ), - m_documentRoot( documentRoot ) + m_basePath( basePath ) { } @@ -105,8 +105,8 @@ void HelperXmlHandler_EpubTOC::checkNewTocEntry() { EBookTocEntry entry; entry.name = m_lastTitle; - QString combined = QDir( m_documentRoot ).filePath( m_lastId ); - entry.url = m_epub->pathToUrl( QDir::cleanPath( combined ) ); + QString combined = EBook_EPUB::combinePath( m_basePath, m_lastId ); + entry.url = m_epub->pathToUrl( combined ); entry.iconid = EBookTocEntry::IMAGE_AUTO; entry.indent = m_indent - 1; diff --git a/lib/libebook/helperxmlhandler_epubtoc.h b/lib/libebook/helperxmlhandler_epubtoc.h index 3571519a..41312be4 100644 --- a/lib/libebook/helperxmlhandler_epubtoc.h +++ b/lib/libebook/helperxmlhandler_epubtoc.h @@ -33,7 +33,7 @@ class EBook_EPUB; class HelperXmlHandler_EpubTOC : public QXmlDefaultHandler { public: - HelperXmlHandler_EpubTOC( EBook_EPUB* epub, const QString& documentRoot ); + HelperXmlHandler_EpubTOC( EBook_EPUB* epub, const QString& basePath ); QList< EBookTocEntry > entries; @@ -50,7 +50,7 @@ class HelperXmlHandler_EpubTOC : public QXmlDefaultHandler QString m_lastId; QString m_lastTitle; EBook_EPUB* m_epub; - QString m_documentRoot; + QString m_basePath; }; #endif // HELPERXMLHANDLER_EPUBTOC_H