using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Threading.Tasks; using VersOne.Epub.Internal; namespace VersOne.Epub { public static class EpubReader { /// /// Opens the book synchronously without reading its whole content. Holds the handle to the EPUB file. /// /// path to the EPUB file /// public static EpubBookRef OpenBook(string filePath) { return OpenBookAsync(filePath).Result; } /// /// Opens the book synchronously without reading its whole content. /// /// path to the EPUB file /// public static EpubBookRef OpenBook(Stream stream) { return OpenBookAsync(stream).Result; } /// /// Opens the book asynchronously without reading its whole content. Holds the handle to the EPUB file. /// /// path to the EPUB file /// public static Task OpenBookAsync(string filePath) { if (!File.Exists(filePath)) { throw new FileNotFoundException("Specified epub file not found.", filePath); } return OpenBookAsync(GetZipArchive(filePath)); } /// /// Opens the book asynchronously without reading its whole content. /// /// path to the EPUB file /// public static Task OpenBookAsync(Stream stream) { return OpenBookAsync(GetZipArchive(stream)); } /// /// Opens the book synchronously and reads all of its content into the memory. Does not hold the handle to the EPUB file. /// /// path to the EPUB file /// public static EpubBook ReadBook(string filePath) { return ReadBookAsync(filePath).Result; } /// /// Opens the book synchronously and reads all of its content into the memory. Does not hold the handle to the EPUB file. /// /// path to the EPUB file /// public static EpubBook ReadBook(Stream stream) { return ReadBookAsync(stream).Result; } /// /// Opens the book asynchronously and reads all of its content into the memory. Does not hold the handle to the EPUB file. /// /// path to the EPUB file /// public static async Task ReadBookAsync(string filePath) { EpubBookRef epubBookRef = await OpenBookAsync(filePath).ConfigureAwait(false); return await ReadBookAsync(epubBookRef).ConfigureAwait(false); } /// /// Opens the book asynchronously and reads all of its content into the memory. /// /// path to the EPUB file /// public static async Task ReadBookAsync(Stream stream) { EpubBookRef epubBookRef = await OpenBookAsync(stream).ConfigureAwait(false); return await ReadBookAsync(epubBookRef).ConfigureAwait(false); } private static async Task OpenBookAsync(ZipArchive zipArchive, string filePath = null) { EpubBookRef result = null; try { result = new EpubBookRef(zipArchive); result.FilePath = filePath; result.Schema = await SchemaReader.ReadSchemaAsync(zipArchive).ConfigureAwait(false); result.Title = result.Schema.Package.Metadata.Titles.FirstOrDefault() ?? String.Empty; result.AuthorList = result.Schema.Package.Metadata.Creators.Select(creator => creator.Creator).ToList(); result.Author = String.Join(", ", result.AuthorList); result.Content = await Task.Run(() => ContentReader.ParseContentMap(result)).ConfigureAwait(false); return result; } catch { result?.Dispose(); throw; } } private static async Task ReadBookAsync(EpubBookRef epubBookRef) { EpubBook result = new EpubBook(); using (epubBookRef) { 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).ConfigureAwait(false); result.CoverImage = await epubBookRef.ReadCoverAsync().ConfigureAwait(false); List chapterRefs = await epubBookRef.GetChaptersAsync().ConfigureAwait(false); result.Chapters = await ReadChapters(chapterRefs).ConfigureAwait(false); } return result; } private static ZipArchive GetZipArchive(string filePath) { return ZipFile.OpenRead(filePath); } private static ZipArchive GetZipArchive(Stream stream) { return new ZipArchive(stream, ZipArchiveMode.Read); } private static async Task ReadContent(EpubContentRef contentRef) { EpubContent result = new EpubContent(); result.Html = await ReadTextContentFiles(contentRef.Html).ConfigureAwait(false); result.Css = await ReadTextContentFiles(contentRef.Css).ConfigureAwait(false); result.Images = await ReadByteContentFiles(contentRef.Images).ConfigureAwait(false); result.Fonts = await ReadByteContentFiles(contentRef.Fonts).ConfigureAwait(false); result.AllFiles = new Dictionary(); foreach (KeyValuePair textContentFile in result.Html.Concat(result.Css)) { result.AllFiles.Add(textContentFile.Key, textContentFile.Value); } foreach (KeyValuePair byteContentFile in result.Images.Concat(result.Fonts)) { result.AllFiles.Add(byteContentFile.Key, byteContentFile.Value); } foreach (KeyValuePair contentFileRef in contentRef.AllFiles) { if (!result.AllFiles.ContainsKey(contentFileRef.Key)) { result.AllFiles.Add(contentFileRef.Key, await ReadByteContentFile(contentFileRef.Value).ConfigureAwait(false)); } } return result; } private static async Task> ReadTextContentFiles(Dictionary textContentFileRefs) { Dictionary result = new Dictionary(); foreach (KeyValuePair textContentFileRef in textContentFileRefs) { EpubTextContentFile textContentFile = new EpubTextContentFile { FileName = textContentFileRef.Value.FileName, ContentType = textContentFileRef.Value.ContentType, ContentMimeType = textContentFileRef.Value.ContentMimeType }; textContentFile.Content = await textContentFileRef.Value.ReadContentAsTextAsync().ConfigureAwait(false); result.Add(textContentFileRef.Key, textContentFile); } return result; } private static async Task> ReadByteContentFiles(Dictionary byteContentFileRefs) { Dictionary result = new Dictionary(); foreach (KeyValuePair byteContentFileRef in byteContentFileRefs) { result.Add(byteContentFileRef.Key, await ReadByteContentFile(byteContentFileRef.Value).ConfigureAwait(false)); } return result; } private static async Task ReadByteContentFile(EpubContentFileRef contentFileRef) { EpubByteContentFile result = new EpubByteContentFile { FileName = contentFileRef.FileName, ContentType = contentFileRef.ContentType, ContentMimeType = contentFileRef.ContentMimeType }; result.Content = await contentFileRef.ReadContentAsBytesAsync().ConfigureAwait(false); return result; } private static async Task> ReadChapters(List chapterRefs) { List result = new List(); foreach (EpubChapterRef chapterRef in chapterRefs) { EpubChapter chapter = new EpubChapter { Title = chapterRef.Title, ContentFileName = chapterRef.ContentFileName, Anchor = chapterRef.Anchor }; chapter.HtmlContent = await chapterRef.ReadHtmlContentAsync().ConfigureAwait(false); chapter.SubChapters = await ReadChapters(chapterRef.SubChapters).ConfigureAwait(false); result.Add(chapter); } return result; } } }