/// <summary> /// Gets XHTML content for a file. /// </summary> /// <param name="ebook"></param> /// <param name="navPoint"></param> /// <returns></returns> public static async Task <string> GetContentsAsync(this EBook ebook, ManifestItem manifestItem, bool embedImages = true) { var fullContentPath = Path.GetFullPath(ebook._rootFolder.Path.EnsureEnd("\\") + ebook.ContentLocation); var tocPath = Path.GetFullPath(Path.GetDirectoryName(fullContentPath).EnsureEnd("\\") + ebook.Manifest["ncx"].ContentLocation); var filePath = Path.GetFullPath(Path.GetDirectoryName(tocPath).EnsureEnd("\\") + manifestItem.ContentLocation); var contentFile = await ebook._rootFolder.GetFileFromPathAsync(filePath.Substring(ebook._rootFolder.Path.Length)); var contents = await FileIO.ReadTextAsync(contentFile); contents = WebUtility.HtmlDecode(contents); if (embedImages) { var contentPath = Path.Combine(ebook._rootFolder.Path, manifestItem.ContentLocation); var imageMatches = new Regex(@"<img.*/>", RegexOptions.IgnoreCase).Matches(contents).OfType <Match>().ToList(); foreach (var match in imageMatches) { var imageNode = HtmlNode.CreateNode(match.Value); var imageSource = imageNode.Attributes["src"].Value; var imgPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(contentPath), imageSource)); var imageFile = await ebook._rootFolder.GetFileFromPathAsync(imgPath.Substring(ebook._rootFolder.Path.Length)); var image = await FileIO.ReadBufferAsync(imageFile); var base64 = Convert.ToBase64String(image.ToArray()); imageNode.Attributes["src"].Value = $"data:image/{imageFile.FileType};base64,{base64}"; contents = contents.Replace(match.Value, imageNode.OuterHtml); } } return(contents); }
internal static async Task <Spine> GetSpineAsync(this EBook ebook) { var spine = new Spine(); IEnumerable <XElement> GetSpineItemNodes(string xml) { var doc = XDocument.Parse(xml); var ns = doc.Root.GetDefaultNamespace(); var node = doc.Element(ns + "package").Element(ns + "spine"); spine.Toc = node.Attribute("toc").Value; var itemNodes = node.Elements(ns + "itemref"); return(itemNodes); } var contentFile = await ebook._rootFolder.GetFileFromPathAsync(ebook.ContentLocation); var contentXml = await FileIO.ReadTextAsync(contentFile); foreach (var itemNode in GetSpineItemNodes(contentXml).ToList()) { spine.Add(new SpineItem { IdRef = itemNode.Attribute("idref").Value }); } return(spine); }
/// <summary> /// Verifies that the EBook has a valid mimetype file. /// </summary> /// <param name="ebook">The EBook to be checked.</param> /// <returns>A bool indicating whether or not the miemtype is valid.</returns> internal static async Task <bool> VerifyMimetypeAsync(this EBook ebook) { bool VerifyMimetypeString(string value) => value == "application/epub+zip"; if (ebook._rootFolder == null) // Make sure a root folder was specified. { return(false); } var mimetypeFile = await ebook._rootFolder.GetItemAsync("mimetype"); if (mimetypeFile == null) // Make sure file exists. { return(false); } var fileContents = await FileIO.ReadTextAsync(mimetypeFile as StorageFile); if (!VerifyMimetypeString(fileContents)) // Make sure file contents are correct. { return(false); } return(true); }
public static Metadata GetMetadata(this EBook eBook) { var contentXml = File.ReadAllText(eBook.ContentLocation); var doc = XDocument.Parse(contentXml); var ns = doc.Root.GetDefaultNamespace(); var metadataNode = doc.Element(ns + "package").Element(ns + "metadata"); var dcNamespace = metadataNode.GetNamespaceOfPrefix("dc"); string GetValue(string node) { return(metadataNode.Element(dcNamespace + node)?.Value); } var metadata = new Metadata { AlternativeTitle = GetValue("alternative"), Audience = GetValue("audience"), Available = GetValue("available") == null ? default(DateTime) : DateTime.Parse(GetValue("available")), Contributor = GetValue("contributor"), Created = GetValue("created") == null ? default(DateTime) : DateTime.Parse(GetValue("created")), Creator = GetValue("creator"), Description = GetValue("description"), Language = GetValue("language"), Title = GetValue("title") }; foreach (var node in metadataNode.Elements(dcNamespace + "date").ToList()) { // TODO: Parse dates. May be a parseable date or just a year. } return(metadata); }
/// <summary> /// Gets a list of ManifestItems for an EBook. /// </summary> /// <param name="ebook"></param> /// <returns></returns> internal static async Task <IEnumerable <ManifestItem> > GetManifestAsync(this EBook ebook) { var items = new List <ManifestItem>(); IEnumerable <XElement> GetManifestItemNodes(string xml) { var doc = XDocument.Parse(xml); var ns = doc.Root.GetDefaultNamespace(); var node = doc.Element(ns + "package").Element(ns + "manifest"); var itemNodes = node.Elements(ns + "item"); return(itemNodes); } var contentFile = await ebook._rootFolder.GetFileFromPathAsync(ebook.ContentLocation); var contentXml = await FileIO.ReadTextAsync(contentFile); foreach (var itemNode in GetManifestItemNodes(contentXml).ToList()) { items.Add(new ManifestItem { ContentLocation = itemNode.Attribute("href").Value, Id = itemNode.Attribute("id").Value, MediaType = itemNode.Attribute("media-type").Value }); } return(items); }
public static string GetCoverLocation(this EBook eBook) { if (!eBook.Manifest.ContainsKey("cover")) { return(null); } var relativeLocation = eBook.Manifest["cover"].ContentLocation; var coverLocation = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(eBook.ContentLocation), relativeLocation)); return(coverLocation); }
/// <summary> /// Gets the table of contents for an EBook. /// </summary> /// <param name="ebook"></param> /// <returns></returns> internal static async Task <TableOfContents> GetTableOfContentsAsync(this EBook ebook) { var hashRegex = new Regex(@"(?<=.html)(#.*)"); var relativeLocation = ebook.Manifest[ebook.Spine.Toc].ContentLocation; var tocFile = await ebook._rootFolder.GetFileFromPathAsync(Path.Combine(Path.GetDirectoryName(ebook.ContentLocation), relativeLocation)); var xml = await FileIO.ReadTextAsync(tocFile); var doc = XDocument.Parse(xml); var ns = doc.Root.GetDefaultNamespace(); var tableOfContents = new TableOfContents { Title = doc.Element(ns + "ncx").Element(ns + "docTitle").Element(ns + "text").Value }; var navMapNode = doc.Element(ns + "ncx").Element(ns + "navMap"); IEnumerable <NavPoint> ParseNavPoints(XElement node, int level) { var navPoints = new List <NavPoint>(); var navPointNodes = node.Elements(ns + "navPoint").ToList(); foreach (var navPointNode in navPointNodes) { var navPoint = new NavPoint { ContentPath = hashRegex.Replace(navPointNode.Element(ns + "content")?.Attribute("src").Value, string.Empty), Id = navPointNode.Attribute("id")?.Value, Level = level, PlayOrder = int.Parse(navPointNode.Attribute("playOrder")?.Value), Text = navPointNode.Element(ns + "navLabel")?.Element(ns + "text")?.Value }; foreach (var subNavPoint in ParseNavPoints(navPointNode, level + 1).ToList()) { navPoint.Items.Add(subNavPoint); } navPoints.Add(navPoint); } return(navPoints); } foreach (var navPoint in ParseNavPoints(navMapNode, 0).ToList()) { tableOfContents.Items.Add(navPoint); } return(tableOfContents); }
public static void Initialize(this EBook eBook) { eBook.ContentLocation = eBook.GetContentLocation(); var manifestItems = eBook.GetManifest().ToList(); foreach (var item in manifestItems) { eBook.Manifest[item.Id] = item; } eBook.CoverLocation = eBook.GetCoverLocation(); eBook.Metadata = eBook.GetMetadata(); }
public static string GetContentLocation(this EBook eBook) { var containerPath = Path.Combine(eBook.BaseDirectory, "META-INF", "container.xml"); var xml = File.ReadAllText(containerPath); var doc = XDocument.Parse(xml); var ns = doc.Root.GetDefaultNamespace(); var node = doc.Element(ns + "container").Element(ns + "rootfiles").Element(ns + "rootfile"); if (node.Attribute("media-type")?.Value != "application/oebps-package+xml") { throw new Exception($"Invalid media type on rootfile node. Unexpected value \"{node.Attribute("media-type")?.Value}\""); } var path = Path.GetFullPath(Path.Combine(eBook.BaseDirectory, node.Attribute("full-path")?.Value)); return(path); }
/// <summary> /// Gets a bitmap image of the cover. /// </summary> /// <param name="ebook"></param> /// <returns></returns> internal static async Task <ImageSource> GetCoverAsync(this EBook ebook) { if (!ebook.Manifest.ContainsKey("cover")) { return(null); } var relativeLocation = ebook.Manifest["cover"].ContentLocation; var coverFile = await ebook._rootFolder.GetFileFromPathAsync(Path.Combine(Path.GetDirectoryName(ebook.ContentLocation), relativeLocation)); var stream = await coverFile.OpenReadAsync(); var bitmap = new BitmapImage(); bitmap.SetSource(stream); return(bitmap); }
public static IEnumerable <ManifestItem> GetManifest(this EBook eBook) { var contentXml = File.ReadAllText(eBook.ContentLocation); var doc = XDocument.Parse(contentXml); var ns = doc.Root.GetDefaultNamespace(); var manifestNode = doc.Element(ns + "package").Element(ns + "manifest"); var itemNodes = manifestNode.Elements(ns + "item").ToList(); foreach (var node in itemNodes) { yield return(new ManifestItem { ContentLocation = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(eBook.ContentLocation), node.Attribute("href").Value)), Id = node.Attribute("id").Value, MediaType = node.Attribute("media-type").Value }); } }
public static bool IsMimetypeValid(this EBook eBook) { var expectedMimetype = "application/epub+zip"; var mimetypeFiles = Directory.GetFiles(eBook.BaseDirectory, "mimetype"); if (mimetypeFiles.Length != 1) { return(false); } var mimetypeFile = mimetypeFiles[0]; var mimetype = File.ReadAllText(mimetypeFile).Trim(' ', '\n', '\r', '\t').ToLower(); if (mimetype != expectedMimetype) { return(false); } return(true); }
public static Spine GetSpine(this EBook eBook) { var spine = new Spine(); var contentXml = File.ReadAllText(eBook.ContentLocation); var doc = XDocument.Parse(contentXml); var ns = doc.Root.GetDefaultNamespace(); var spineNode = doc.Element(ns + "package").Element(ns + "spine"); spine.Toc = spineNode.Attribute("toc").Value; var itemNodes = spineNode.Elements(ns + "itemref").ToList(); foreach (var node in itemNodes) { spine.Add(new SpineItem { IdRef = node.Attribute("idref").Value }); } return(spine); }
/// <summary> /// Gets metadata from an EBook. /// </summary> /// <param name="ebook">The EBook for which to get metadata.</param> /// <returns>The metadata object.</returns> internal static async Task <Metadata> GetMetadataAsync(this EBook ebook) { XElement GetMetadataNode(string xml) { var doc = XDocument.Parse(xml); var ns = doc.Root.GetDefaultNamespace(); var node = doc.Element(ns + "package").Element(ns + "metadata"); return(node); } var contentFile = await ebook._rootFolder.GetFileFromPathAsync(ebook.ContentLocation); var contentXml = await FileIO.ReadTextAsync(contentFile); var metadataNode = GetMetadataNode(contentXml); var dcNamespace = metadataNode.GetNamespaceOfPrefix("dc"); string GetValue(string node) => metadataNode.Element(dcNamespace + node)?.Value; var metadata = new Metadata { AlternativeTitle = GetValue("alternative"), Audience = GetValue("audience"), Available = GetValue("available") == null ? default(DateTime) : DateTime.Parse(GetValue("available")), Contributor = GetValue("contributor"), Created = GetValue("created") == null ? default(DateTime) : DateTime.Parse(GetValue("created")), Creator = GetValue("creator"), Date = GetValue("date") == null ? default(DateTime) : DateTime.Parse(GetValue("date")), Description = GetValue("description"), Language = GetValue("language"), Title = GetValue("title") }; return(metadata); }
/// <summary> /// Gets the location of the content.opf file. /// </summary> /// <param name="ebook">The EBook for which to get the content location.</param> /// <returns>A string location.</returns> internal static async Task <string> GetContentLocationAsync(this EBook ebook) { async Task <string> GetContentXmlAsync() { var folder = await ebook._rootFolder.GetFolderAsync("META-INF"); var file = await folder.GetFileAsync("container.xml"); var xml = await FileIO.ReadTextAsync(file); return(xml); } XElement GetRootFileNode(string xml) { var doc = XDocument.Parse(xml); var ns = doc.Root.GetDefaultNamespace(); var node = doc.Element(ns + "container").Element(ns + "rootfiles").Element(ns + "rootfile"); return(node); } bool VerifyMediaType(XElement node) => node.Attribute("media-type")?.Value == "application/oebps-package+xml"; var containerXml = await GetContentXmlAsync(); var rootFileNode = GetRootFileNode(containerXml); if (!VerifyMediaType(rootFileNode)) { throw new Exception("Invalid media type on rootfile node."); } return(rootFileNode.Attribute("full-path")?.Value); }
public static async Task <string> GetContentsAsync(this EBook ebook, NavPoint navPoint) { var manifestItem = ebook.Manifest.FirstOrDefault(a => Path.GetFileName(a.Value.ContentLocation) == Path.GetFileName(navPoint.ContentPath)).Value; return(await ebook.GetContentsAsync(manifestItem)); }
public static async Task <string> GetContentsAsync(this EBook ebook, SpineItem spineItem) { var manifestItem = ebook.Manifest[spineItem.IdRef]; return(await ebook.GetContentsAsync(manifestItem)); }
public static TableOfContents GetTableOfContents(this EBook eBook) { if (eBook.Spine == null) { eBook.Spine = eBook.GetSpine(); } var tocXml = File.ReadAllText(eBook.Manifest[eBook.Spine.Toc].ContentLocation); var doc = XDocument.Parse(tocXml); var ns = doc.Root.GetDefaultNamespace(); var ncxNode = doc.Element(ns + "ncx"); var tableOfContents = new TableOfContents { Title = ncxNode.Element(ns + "docTitle").Element(ns + "text").Value }; var navMapNode = ncxNode.Element(ns + "navMap"); IEnumerable <NavPoint> ParseNavPoints(XElement node, int level) { var navPoints = new List <NavPoint>(); var navPointNodes = node.Elements(ns + "navPoint").ToList(); foreach (var navPointNode in navPointNodes) { var filePath = navPointNode.Element(ns + "content").Attribute("src").Value; var pathBuilder = new StringBuilder(); foreach (var c in filePath) { if (c == '#' && pathBuilder.ToString().EndsWith(".html")) { break; } pathBuilder.Append(c); } var navPoint = new NavPoint { ContentPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(eBook.Manifest[eBook.Spine.Toc].ContentLocation), pathBuilder.ToString())), Id = navPointNode.Attribute("id")?.Value, Level = level, PlayOrder = int.Parse(navPointNode.Attribute("playOrder")?.Value), Text = navPointNode.Element(ns + "navLabel")?.Element(ns + "text")?.Value }; foreach (var subNavPoint in ParseNavPoints(navPointNode, level + 1).ToList()) { navPoint.Items.Add(subNavPoint); } navPoints.Add(navPoint); } return(navPoints); } foreach (var navPoint in ParseNavPoints(navMapNode, 0).ToList()) { tableOfContents.Items.Add(navPoint); } return(tableOfContents); }
public static SpineItem GetSpineItem(this EBook ebook, ManifestItem manifestItem) { var spineItem = ebook.Spine.FirstOrDefault(a => a.IdRef == manifestItem.Id); return(spineItem); }
public static string GetContents(this EBook eBook, NavPoint navPoint, bool embedImages = false, bool embedStyles = false) { var manifestItem = eBook.Manifest.SingleOrDefault(item => Path.GetFileName(item.Value.ContentLocation) == Path.GetFileName(navPoint.ContentPath)); return(eBook.GetContents(manifestItem.Value, embedImages, embedStyles)); }
public static string GetContents(this EBook eBook, SpineItem item, bool embedImages = false, bool embedStyles = false) { var manifestItem = eBook.Manifest[item.IdRef]; return(GetContents(eBook, manifestItem, embedImages, embedStyles)); }
public static async Task <StorageFile> GetFileAsync(this EBook ebook, string path) { return(await ebook._rootFolder.GetFileFromPathAsync(path.Substring(ebook._rootFolder.Path.Length))); }
public static string GetContents(this EBook eBook, ManifestItem manifestItem, bool embedImages = false, bool embedStyles = false) { var contents = File.ReadAllText(manifestItem.ContentLocation); contents = WebUtility.HtmlDecode(contents); if (embedImages) { var imageMatches = new Regex(@"<img.*/>", RegexOptions.IgnoreCase).Matches(contents).OfType <Match>().ToList(); foreach (var match in imageMatches) { var node = HtmlNode.CreateNode(match.Value); var source = node.Attributes["src"].Value; if (source.StartsWith("data:")) { continue; } var imagePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(manifestItem.ContentLocation), source)); var buffer = File.ReadAllBytes(imagePath); var base64 = Convert.ToBase64String(buffer); node.Attributes["src"].Value = $"data:image/{new FileInfo(imagePath).Extension};base64,{base64}"; contents = contents.Replace(match.Value, node.OuterHtml); } } if (embedStyles) { var styleRegex = new Regex(@"<style>(.*?)<\/style>", RegexOptions.IgnoreCase | RegexOptions.Singleline); var styleMatch = styleRegex.Match(contents); var styleBuilder = new StringBuilder(); if (styleMatch.Success) { styleBuilder.Append(styleMatch.Groups[styleMatch.Groups.Count - 1]); } var stylesheetRegex = new Regex(@"<link.*?rel=""stylesheet"".*?>", RegexOptions.IgnoreCase); var stylesheetMatches = stylesheetRegex.Matches(contents).OfType <Match>().ToList(); foreach (var match in stylesheetMatches) { var node = HtmlNode.CreateNode(match.Value); var location = node.Attributes["href"].Value; var file = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(manifestItem.ContentLocation), location)); if (!File.Exists(file)) { continue; } var css = File.ReadAllText(file); styleBuilder.Append(css); contents = contents.Replace(match.Value, string.Empty); } var styleTag = $"<style>{styleBuilder.ToString()}</style>"; if (styleMatch.Success) { contents = contents.Replace(styleMatch.Value, styleTag); } else { var endOfHead = contents.IndexOf("</head>"); contents = contents.Insert(endOfHead, styleTag); } } return(contents); }