forked from creatint/dart-epub
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a code complete initial pass. Tests need to be added.
- Loading branch information
Showing
57 changed files
with
1,681 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
language: dart | ||
sudo: false | ||
script: ./tool/travis.sh |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
# dartpub | ||
Epub Reader for Dart | ||
Epub Reader for Dart inspired by [this fantastic C# Epub Reader](https://github.com/versfx/EpubReader) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
library dartpub; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import 'epubChapter.dart'; | ||
import 'epubContent.dart'; | ||
import 'epubSchema.dart'; | ||
|
||
import 'package:image/image.dart'; | ||
|
||
class EpubBook { | ||
String FilePath; | ||
String Title; | ||
String Author; | ||
List<String> AuthorList; | ||
EpubSchema Schema; | ||
EpubContent Content; | ||
Image CoverImage; | ||
List<EpubChapter> Chapters; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import 'epubContentFile.dart'; | ||
|
||
class EpubByteContentFile extends EpubContentFile { | ||
List<int> Content; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class EpubChapter { | ||
String Title; | ||
String ContentFileName; | ||
String Anchor; | ||
String HtmlContent; | ||
List<EpubChapter> SubChapters; | ||
|
||
String toString() { | ||
return "Title: ${Title}, Subchapter count: ${SubChapters.length}"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import 'epubByteContentFile.dart'; | ||
import 'epubContentFile.dart'; | ||
import 'epubTextContentFile.dart'; | ||
|
||
class EpubContent{ | ||
Map<String, EpubTextContentFile> Html; | ||
Map<String, EpubTextContentFile> Css; | ||
Map<String, EpubByteContentFile> Images; | ||
Map<String, EpubByteContentFile> Fonts; | ||
Map<String, EpubContentFile> AllFiles; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import 'epubContentType.dart'; | ||
|
||
abstract class EpubContentFile | ||
{ | ||
String FileName; | ||
EpubContentType ContentType; | ||
String ContentMimeType; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
enum EpubContentType { | ||
XHTML_1_1, | ||
DTBOOK, | ||
DTBOOK_NCX, | ||
OEB1_DOCUMENT, | ||
XML, | ||
CSS, | ||
OEB1_CSS, | ||
IMAGE_GIF, | ||
IMAGE_JPEG, | ||
IMAGE_PNG, | ||
IMAGE_SVG, | ||
FONT_TRUETYPE, | ||
FONT_OPENTYPE, | ||
OTHER | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import '../schema/navigation/epubNavigation.dart'; | ||
import '../schema/opf/epubPackage.dart'; | ||
|
||
class EpubSchema { | ||
EpubPackage Package; | ||
EpubNavigation Navigation; | ||
String ContentDirectoryPath; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import 'epubContentFile.dart'; | ||
|
||
class EpubTextContentFile extends EpubContentFile { | ||
String Content; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import 'dart:async'; | ||
import 'dart:io' as IO; | ||
|
||
import 'entities/epubBook.dart'; | ||
import 'entities/epubByteContentFile.dart'; | ||
import 'entities/epubChapter.dart'; | ||
import 'entities/epubContent.dart'; | ||
import 'entities/epubContentFile.dart'; | ||
import 'entities/epubTextContentFile.dart'; | ||
import 'readers/contentReader.dart'; | ||
import 'readers/schemaReader.dart'; | ||
import 'refEntities/epubBookRef.dart'; | ||
import 'refEntities/epubByteContentFileRef.dart'; | ||
import 'refEntities/epubChapterRef.dart'; | ||
import 'refEntities/epubContentFileRef.dart'; | ||
import 'refEntities/epubContentRef.dart'; | ||
import 'refEntities/epubTextContentFileRef.dart'; | ||
import 'schema/opf/epubMetadataCreator.dart'; | ||
|
||
import 'package:archive/archive.dart'; | ||
|
||
class EpubReader { | ||
|
||
/// Opens the book synchronously without reading its content. Holds the handle to the EPUB file. | ||
static EpubBookRef OpenBook(String filePath) { | ||
EpubBookRef res; | ||
Future.wait([OpenBookAsync(filePath)]) | ||
.then((List<EpubBookRef> results) => res = results.first); | ||
return res; | ||
} | ||
|
||
/// Opens the book asynchronously without reading its content. Holds the handle to the EPUB file. | ||
static Future<EpubBookRef> OpenBookAsync(String filePath) async { | ||
var targetFile = new IO.File(filePath); | ||
if (!(await targetFile.exists())) | ||
throw new Exception("Specified epub file not found: ${filePath}"); | ||
|
||
List<int> bytes = await targetFile.readAsBytes(); | ||
Archive epubArchive = new ZipDecoder().decodeBytes(bytes); | ||
|
||
EpubBookRef bookRef = new EpubBookRef(epubArchive); | ||
bookRef.FilePath = filePath; | ||
bookRef.Schema = await SchemaReader.ReadSchemaAsync(epubArchive); | ||
bookRef.Title = bookRef.Schema | ||
.Package | ||
.Metadata | ||
.Titles | ||
.firstWhere((String name) => true, orElse: () => ""); | ||
bookRef.AuthorList = bookRef.Schema | ||
.Package | ||
.Metadata | ||
.Creators | ||
.map((EpubMetadataCreator creator) => creator.Creator).toList(); | ||
bookRef.Author = bookRef.AuthorList.join(", "); | ||
bookRef.Content = await ContentReader.ParseContentMap(bookRef); | ||
return bookRef; | ||
} | ||
|
||
/// Opens the book synchronously and reads all of its content into the memory. Does not hold the handle to the EPUB file. | ||
static EpubBook ReadBook(String filePath) { | ||
EpubBook res; | ||
Future.wait([ReadBookAsync(filePath)]) | ||
.then((List<EpubBook> result) => res = result.first); | ||
return res; | ||
} | ||
|
||
/// Opens the book asynchronously and reads all of its content into the memory. Does not hold the handle to the EPUB file. | ||
static Future<EpubBook> ReadBookAsync(String filePath) async { | ||
EpubBook result = new EpubBook(); | ||
|
||
EpubBookRef epubBookRef = await OpenBookAsync(filePath); | ||
result.FilePath = epubBookRef.FilePath; | ||
result.Schema = epubBookRef.Schema; | ||
result.Title = epubBookRef.Title; | ||
result.AuthorList = epubBookRef.AuthorList; | ||
result.Author = epubBookRef.Author; | ||
result.Content = await ReadContent(epubBookRef.Content); | ||
result.CoverImage = await epubBookRef.ReadCoverAsync(); | ||
List<EpubChapterRef> chapterRefs = await epubBookRef.GetChaptersAsync(); | ||
result.Chapters = await readChapters(chapterRefs); | ||
|
||
return result; | ||
} | ||
|
||
static Future<EpubContent> ReadContent(EpubContentRef contentRef) async { | ||
EpubContent result = new EpubContent(); | ||
result.Html = await readTextContentFiles(contentRef.Html); | ||
result.Css = await readTextContentFiles(contentRef.Css); | ||
result.Images = await readByteContentFiles(contentRef.Images); | ||
result.Fonts = await readByteContentFiles(contentRef.Fonts); | ||
result.AllFiles = new Map<String, EpubContentFile>(); | ||
|
||
result.Html.forEach((String key, EpubTextContentFile value){ | ||
result.AllFiles[key] = value; | ||
}); | ||
result.Css.forEach((String key, EpubTextContentFile value){ | ||
result.AllFiles[key] = value; | ||
}); | ||
|
||
result.Images.forEach((String key, EpubByteContentFile value){ | ||
result.AllFiles[key] = value; | ||
}); | ||
result.Fonts.forEach((String key, EpubByteContentFile value){ | ||
result.AllFiles[key] = value; | ||
}); | ||
|
||
await contentRef.AllFiles.forEach((String key, EpubContentFileRef value) async { | ||
if(!result.AllFiles.containsKey(key)) { | ||
result.AllFiles[key] = await readByteContentFile(value); | ||
} | ||
}); | ||
|
||
return result; | ||
} | ||
|
||
static Future<Map<String, EpubTextContentFile>> readTextContentFiles(Map<String, EpubTextContentFileRef> textContentFileRefs) async { | ||
Map<String, EpubTextContentFile> result = new Map<String, EpubTextContentFile>(); | ||
await textContentFileRefs.forEach((String key, EpubContentFileRef value) async { | ||
EpubTextContentFile textContentFile = new EpubTextContentFile(); | ||
{ | ||
textContentFile.FileName = value.FileName; | ||
textContentFile.ContentType = value.ContentType; | ||
textContentFile.ContentMimeType = value.ContentMimeType; | ||
}; | ||
textContentFile.Content = await value.ReadContentAsTextAsync(); | ||
result[key] = textContentFile; | ||
}); | ||
return result; | ||
} | ||
|
||
static Future<Map<String, EpubByteContentFile>> readByteContentFiles(Map<String, EpubByteContentFileRef> byteContentFileRefs) async { | ||
Map<String, EpubByteContentFile> result = new Map<String, EpubByteContentFile>(); | ||
byteContentFileRefs.forEach((String key, EpubByteContentFileRef value) async { | ||
result[key] = await readByteContentFile(value); | ||
}); | ||
return result; | ||
} | ||
|
||
static Future<EpubByteContentFile> readByteContentFile(EpubContentFileRef contentFileRef) async { | ||
EpubByteContentFile result = new EpubByteContentFile(); | ||
{ | ||
result.FileName = contentFileRef.FileName; | ||
result.ContentType = contentFileRef.ContentType; | ||
result.ContentMimeType = contentFileRef.ContentMimeType; | ||
}; | ||
result.Content = await contentFileRef.ReadContentAsBytesAsync(); | ||
return result; | ||
} | ||
|
||
static Future<List<EpubChapter>> readChapters(List<EpubChapterRef> chapterRefs) async { | ||
List<EpubChapter> result = new List<EpubChapter>(); | ||
await chapterRefs.forEach((EpubChapterRef chapterRef) async { | ||
EpubChapter chapter = new EpubChapter(); | ||
{ | ||
chapter.Title = chapterRef.Title; | ||
chapter.ContentFileName = chapterRef.ContentFileName; | ||
chapter.Anchor = chapterRef.Anchor; | ||
}; | ||
chapter.HtmlContent = await chapterRef.ReadHtmlContentAsync(); | ||
chapter.SubChapters = await readChapters(chapterRef.SubChapters); | ||
result.add(chapter); | ||
}); | ||
return result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import 'dart:async'; | ||
|
||
import '../refEntities/epubBookRef.dart'; | ||
import '../refEntities/epubByteContentFileRef.dart'; | ||
import '../schema/opf/epubManifestItem.dart'; | ||
import '../schema/opf/epubMetadataMeta.dart'; | ||
|
||
import 'package:image/image.dart' as images; | ||
|
||
class BookCoverReader { | ||
static Future<images.Image> ReadBookCoverAsync(EpubBookRef bookRef) async { | ||
List<EpubMetadataMeta> metaItems = bookRef.Schema.Package.Metadata.MetaItems; | ||
if (metaItems == null || metaItems.length == 0) | ||
return null; | ||
EpubMetadataMeta coverMetaItem = metaItems.firstWhere((EpubMetadataMeta metaItem) => metaItem.Name.toLowerCase() == "cover", orElse: () => null); | ||
if (coverMetaItem == null) | ||
return null; | ||
if (coverMetaItem.Content == null || coverMetaItem.Content.isEmpty) | ||
throw new Exception("Incorrect EPUB metadata: cover item content is missing."); | ||
EpubManifestItem coverManifestItem = bookRef.Schema | ||
.Package | ||
.Manifest | ||
.Items | ||
.firstWhere((EpubManifestItem manifestItem) => manifestItem.Id.toLowerCase() == coverMetaItem.Content.toLowerCase(), orElse: () => null)); | ||
if (coverManifestItem == null) | ||
throw new Exception("Incorrect EPUB manifest: item with ID = \"${coverMetaItem.Content}\" is missing."); | ||
EpubByteContentFileRef coverImageContentFileRef; | ||
if (!bookRef.Content.Images.containsKey(coverManifestItem.Href)) | ||
throw new Exception("Incorrect EPUB manifest: item with href = \"${coverManifestItem.Href}\" is missing."); | ||
coverImageContentFileRef = bookRef.Content.Images[coverManifestItem.Href]; | ||
List<int> coverImageContent = await coverImageContentFileRef.ReadContentAsBytesAsync(); | ||
images.Image retval = images.decodeImage(coverImageContent); | ||
return retval; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import '../refEntities/epubBookRef.dart'; | ||
import '../refEntities/epubChapterRef.dart'; | ||
import '../refEntities/epubTextContentFileRef.dart'; | ||
import '../schema/navigation/epubNavigationPoint.dart'; | ||
|
||
class ChapterReader { | ||
static List<EpubChapterRef> GetChapters(EpubBookRef bookRef) { | ||
return GetChaptersImpl(bookRef, bookRef.Schema.Navigation.NavMap.Points); | ||
} | ||
|
||
static List<EpubChapterRef> GetChaptersImpl(EpubBookRef bookRef, List<EpubNavigationPoint> navigationPoints) { | ||
List<EpubChapterRef> result = new List<EpubChapterRef>(); | ||
navigationPoints.forEach((EpubNavigationPoint navigationPoint) { | ||
String contentFileName; | ||
String anchor; | ||
int contentSourceAnchorCharIndex = navigationPoint.Content.Source.indexOf('#'); | ||
if (contentSourceAnchorCharIndex == -1) | ||
{ | ||
contentFileName = navigationPoint.Content.Source; | ||
anchor = null; | ||
} | ||
else | ||
{ | ||
contentFileName = navigationPoint.Content.Source.substring(0, contentSourceAnchorCharIndex); | ||
anchor = navigationPoint.Content.Source.substring(contentSourceAnchorCharIndex + 1); | ||
} | ||
EpubTextContentFileRef htmlContentFileRef; | ||
if (!bookRef.Content.Html.containsKey(contentFileName)) { | ||
throw new Exception("Incorrect EPUB manifest: item with href = \"${contentFileName}\" is missing."); | ||
} | ||
htmlContentFileRef = bookRef.Content.Html[contentFileName]; | ||
EpubChapterRef chapterRef = new EpubChapterRef(htmlContentFileRef); | ||
chapterRef.ContentFileName = contentFileName; | ||
chapterRef.Anchor = anchor; | ||
chapterRef.Title = navigationPoint.NavigationLabels.first.Text; | ||
chapterRef.SubChapters = GetChaptersImpl(bookRef, navigationPoint.ChildNavigationPoints); | ||
result.add(chapterRef); | ||
}); | ||
return result; | ||
} | ||
} |
Oops, something went wrong.