epub_pro 5.3.0
epub_pro: ^5.3.0 copied to clipboard
EPUB Parser for Dart. Suitable for use on the Server, the Web, or in Flutter
epub_pro #
This is a fork of a fork, of a fork, of dart-epub. All of which seem unmaintained.
I'm maintaining this so that I can read EPUBs on my app.
What's different? #
- Updated dependencies (so your installs will work)
- Fixed readBook crashing when EPUB manifest cover image doesn't exist
- Smart NCX/Spine reconciliation - When EPUBs have incomplete navigation (NCX) that doesn't include all spine items, the library automatically reconciles them by:
- Preserving the NCX hierarchy for items that are in the navigation
- Including orphaned spine items as subchapters under their logical parents
- Maintaining the correct reading order from the spine
- This matches how Apple Books and other readers handle malformed EPUBs
- Added chapter splitting functionality - automatically split long chapters (>5000 words) into smaller, more manageable parts
- Added lazy loading support for chapter splitting - read and split chapters on-demand for better memory efficiency
Internal #
dart pub publish --dry-run dart format . dart test
Information from the previous forks #
Epub Reader and Writer for Dart inspired by this fantastic C# Epub Reader
This does not rely on the dart:io package in any way, so it is avilable for both desktop and web-based implementations
Installing #
Add the package to the dependencies section of your pubspec.yaml
dependencies:
epub_pro: any
Example #
//Get the epub into memory somehow
String fileName = 'sample.epub';
String fullPath = path.join(io.Directory.current.path, fileName);
var targetFile = new io.File(fullPath);
List<int> bytes = await targetFile.readAsBytes();
// Opens a book and reads all of its content into the memory
EpubBook epubBook = await EpubReader.readBook(bytes);
// COMMON PROPERTIES
// Book's title
String? title = epubBook.title;
// Book's authors (comma separated list)
String? author = epubBook.author;
// Book's authors (list of authors names)
List<String?>? authors = epubBook.authors;
// Book's cover image (null if there is no cover)
Image? coverImage = epubBook.coverImage;
// CHAPTERS
// Enumerating chapters
epubBook.chapters.forEach((EpubChapter chapter) {
// Title of chapter
String? chapterTitle = chapter.title;
// HTML content of current chapter
String? chapterHtmlContent = chapter.htmlContent;
// Nested chapters
List<EpubChapter> subChapters = chapter.subChapters;
});
// CONTENT
// Book's content (HTML files, stlylesheets, images, fonts, etc.)
EpubContent? bookContent = epubBook.content;
// IMAGES
// All images in the book (file name is the key)
Map<String, EpubByteContentFile>? images = bookContent?.images;
EpubByteContentFile? firstImage =
images?.values.firstOrNull; // Get the first image in the book
// Content type (e.g. EpubContentType.IMAGE_JPEG, EpubContentType.IMAGE_PNG)
EpubContentType contentType = firstImage!.contentType!;
// MIME type (e.g. "image/jpeg", "image/png")
String mimeContentType = firstImage.contentMimeType!;
// HTML & CSS
// All XHTML files in the book (file name is the key)
Map<String, EpubTextContentFile>? htmlFiles = bookContent?.html;
// All CSS files in the book (file name is the key)
Map<String, EpubTextContentFile>? cssFiles = bookContent?.css;
// Entire HTML content of the book
htmlFiles?.values.forEach((EpubTextContentFile htmlFile) {
String? htmlContent = htmlFile.content;
});
// All CSS content in the book
cssFiles?.values.forEach((EpubTextContentFile cssFile) {
String cssContent = cssFile.content!;
});
// OTHER CONTENT
// All fonts in the book (file name is the key)
Map<String, EpubByteContentFile>? fonts = bookContent?.fonts;
// All files in the book (including HTML, CSS, images, fonts, and other types of files)
Map<String, EpubContentFile>? allFiles = bookContent?.allFiles;
// ACCESSING RAW SCHEMA INFORMATION
// EPUB OPF data
EpubPackage? package = epubBook.schema?.package;
// Enumerating book's contributors
package?.metadata?.contributors.forEach((contributor) {
String contributorName = contributor.contributor!;
String contributorRole = contributor.role!;
});
// EPUB NCX data
EpubNavigation navigation = epubBook.schema!.navigation!;
// Enumerating NCX metadata
navigation.head?.metadata.forEach((meta) {
String metadataItemName = meta.name!;
String metadataItemContent = meta.content!;
});
// Write the Book
var written = EpubWriter.writeBook(epubBook);
if (written != null) {
// Read the book into a new object!
var newBook = await EpubReader.readBook(written);
}
// CHAPTER SPLITTING
// Read book with automatic chapter splitting (chapters > 5000 words are split)
EpubBook splitBook = await EpubReader.readBookWithSplitChapters(bytes);
// The chapters are now split into manageable parts
splitBook.chapters.forEach((EpubChapter chapter) {
// Chapters with >5000 words will have titles like:
// "Original Chapter Title - Part 1"
// "Original Chapter Title - Part 2"
String? chapterTitle = chapter.title;
String? chapterHtmlContent = chapter.htmlContent;
});
// LAZY LOADING WITH CHAPTER SPLITTING
// Open book for lazy loading with automatic chapter splitting
EpubBookRef lazyBookRef = await EpubReader.openBookWithSplitChapters(bytes);
// Get chapter references that will be split as needed
List<EpubChapterRef> chapterRefs = await lazyBookRef.getChapterRefsWithSplitting();
// Content is loaded on-demand when you read it
for (var chapterRef in chapterRefs) {
if (chapterRef is EpubChapterSplitRef) {
// This is a split chapter part
print('${chapterRef.title} (Part ${chapterRef.partNumber} of ${chapterRef.totalParts})');
}
// Content is only loaded when you call readHtmlContent()
String content = await chapterRef.readHtmlContent();
}
// For comparison: regular lazy loading with splitting
EpubBookRef bookRef = await EpubReader.openBook(bytes);
List<EpubChapter> splitChapters = await bookRef.getChaptersWithSplitting();
// Each chapter is guaranteed to have ≤5000 words
for (var chapter in splitChapters) {
print('${chapter.title}: ${chapter.htmlContent?.length} characters');
}
// SMART NCX/SPINE RECONCILIATION
// The library automatically handles EPUBs with incomplete navigation
// For example, if an EPUB has chapters in the spine but not in the NCX:
// NCX: [Part 1, Part 2]
// Spine: [cover.xhtml, part1.xhtml, chapter01.xhtml, chapter02.xhtml, part2.xhtml, chapter03.xhtml]
// The library will create a proper hierarchy:
epubBook.chapters.forEach((chapter) {
print('Chapter: ${chapter.title}');
// Orphaned spine items become subchapters
chapter.subChapters.forEach((subChapter) {
print(' SubChapter: ${subChapter.title ?? subChapter.contentFileName}');
});
});
// Output:
// Chapter: null (cover.xhtml)
// Chapter: Part 1
// SubChapter: chapter01.xhtml
// SubChapter: chapter02.xhtml
// Chapter: Part 2
// SubChapter: chapter03.xhtml