private string BuildChapterHtml(EpubChapterHolder cHolder) { string result = string.Empty; int startTagPosition = 0; // string position of this chapter's id int lengthOfRequiredText = 0; // the number of chars between the start of the chapter and the start of the next chapter (or end of the file if the next chapter isn't in the same file) // Load current chapter html into HtmlAgilityPack to parse // Method: Find the string position of the start and end id's of this chapter, then just grab the chars between them using .Substring(). (This is a much faster method than using xpath to select the relevant nodes). // Then load the result into HtmlAgilityPack because it will fix any html issues i.e. close any unclosed tags etc var doc = new HtmlDocument(); doc.LoadHtml(cHolder.CurrentChapter.EpubChapter.HtmlContentFileRef.ReadContentAsText() ?? ""); //.ReadHtmlContent() ?? ""); var bodyNodes = doc.DocumentNode.SelectSingleNode("//body"); // select everything inside the <body> tag var startNode = bodyNodes.SelectSingleNode(string.Format("//node()[@id='{0}']", cHolder.CurrentChapter.EpubChapter.Link.Anchor)); // return the start node i.e. the start of the current chapter var startTag = startNode.OuterHtml; startTagPosition = bodyNodes.InnerHtml.IndexOf(startTag); // index of the start tag if (!string.IsNullOrEmpty(cHolder.NextChapter.EpubChapter?.Link.Anchor ?? "")) // there is a next Chapter { var endNode = bodyNodes.SelectSingleNode(string.Format("//node()[@id='{0}']", cHolder.NextChapter.EpubChapter.Link.Anchor)); // return the end node i.e. the start of the next chapter if (endNode != null) // next chapter found { var endTag = endNode.OuterHtml; int endTagPosition = bodyNodes.InnerHtml.IndexOf(endTag); // index of the end tag lengthOfRequiredText = endTagPosition - startTagPosition; } else // next chapter is not in this chapter file { // don't need to do anything here, as lengthOfRequiredText will remain 0 } } var theHtml = string.Empty; if (lengthOfRequiredText > 0) // there is a next tag { theHtml = bodyNodes.InnerHtml.Substring(startTagPosition, lengthOfRequiredText); // return the content beteen the start of the start tag and the start of the end tag } else // no end tag so just get all the content to the end of the body { theHtml = bodyNodes.InnerHtml.Substring(startTagPosition); } // update all chapter links to use the friendly url var charSetOccurences = new Regex("href=\"(.+)#([^\"]+)\"", RegexOptions.IgnoreCase); // find all href links with anchors var charSetMatches = charSetOccurences.Matches(theHtml); foreach (Match match in charSetMatches) { if (theHtml.IndexOf(string.Format(@"id=""{0}""", match.Groups[2].Value)) > 0) // the matching id tag has been found, so update the href to remove the url before the # { theHtml = theHtml.Replace(match.Groups[1].Value + "#" + match.Groups[2].Value, "#" + match.Groups[2].Value); } } // validate/fix html doc.LoadHtml(theHtml ?? ""); // by now loading the resultant html into the HtmlDocument object this should fix any html issues i.e. close any unclosed tags etc result = doc.DocumentNode.OuterHtml; // now add any sub chapters result = result + BuildSubChapters(cHolder.CurrentChapter.EpubChapter, "", true); return(result); }
/// <summary> /// Display the chapter. This might be a whole xhtml file or a section of an xhtml file between 2 'chapter' id's (or epub navnodes) /// </summary> /// <param name="ePubDisplayModel"></param> /// <param name="epubBookRef"></param> public void FindAndProcessChapter(ref EpubDisplayModel ePubDisplayModel, EpubBookRef epubBookRef) { EpubChapterHolder cHolder = new EpubChapterHolder(); // Enumerating chapters var allChapters = epubBookRef.GetNavigation();//.GetChapters(); for (int i = 0; i < allChapters.Count(); i++) { var _chapter = allChapters[i]; string encTitle = EpubHelpers.EncodeChapterTitleForUrl(_chapter.Title); if (encTitle == _processAction && !cHolder.CurrentChapter.IsValid()) // chapter found! select this chapter to display { cHolder.CurrentChapter.ChapterIndex = i; cHolder.CurrentChapter.EpubChapter = _chapter; cHolder.CurrentChapter.TitleAndLinkUrl.LinkTitle = _chapter.Title; } ePubDisplayModel.TOC_Items.Add(new EpubLink() { LinkTitle = _chapter.Title }); // add TOC to display model } if (!cHolder.CurrentChapter.IsValid()) // chapter not found - so get the default chapter and redirect to that { if (_startAtIndex < 0 || _startAtIndex >= allChapters.Count()) // check if the _startAtIndex property is within valid limits - if not then set the index to 0 { _startAtIndex = 0; } var _chapter = allChapters[_startAtIndex]; cHolder.CurrentChapter.TitleAndLinkUrl.LinkTitle = _chapter.Title; ePubDisplayModel.RedirectToChapter = cHolder.CurrentChapter.TitleAndLinkUrl.LinkUrl; return; // exit this method as we need to perform a redirect in the calling controller rather than } ePubDisplayModel.TOC_Items[cHolder.CurrentChapter.ChapterIndex].IsCurrent = true; // try and find the previous chapter details int previousChapterIndex = cHolder.CurrentChapter.ChapterIndex - 1; if (previousChapterIndex >= 0) // check index is valid { var _chapter = allChapters[previousChapterIndex]; cHolder.PreviousChapter.ChapterIndex = previousChapterIndex; cHolder.PreviousChapter.EpubChapter = _chapter; cHolder.PreviousChapter.TitleAndLinkUrl.LinkTitle = _chapter.Title; ePubDisplayModel.Nav_PreviousChapterLink.LinkTitle = _chapter.Title; } // try and find the next chapter details int nextChapterIndex = cHolder.CurrentChapter.ChapterIndex + 1; if (nextChapterIndex < allChapters.Count()) // check index is valid { var _chapter = allChapters[nextChapterIndex]; cHolder.NextChapter.ChapterIndex = nextChapterIndex; cHolder.NextChapter.EpubChapter = _chapter; cHolder.NextChapter.TitleAndLinkUrl.LinkTitle = _chapter.Title; ePubDisplayModel.Nav_NextChapterLink.LinkTitle = _chapter.Title; } ePubDisplayModel.ChapterHtml = BuildChapterHtml(cHolder); }