/// <summary> /// Some book layouts rely on the first page facing the second page. A Wall Calendar is one example. /// Here we check if the first content page has this requirement and, if so, we insert a blank "flyleaf" /// page. /// </summary> private void InjectFlyleafIfNeeded(Layout layout) { // the "inside" here means "not counting the cover" var numberOfFrontMatterPagesInside = XMatterDom.SafeSelectNodes("//div[contains(@class,'bloom-frontMatter')]").Count - 1; var firstPageWouldNotBePartOfASpread = numberOfFrontMatterPagesInside % 2 != 0; if (firstPageWouldNotBePartOfASpread) { var lastFrontMatterPage = _bookDom.SelectSingleNode("//div[contains(@class,'bloom-frontMatter')][last()]"); var firstContentPageAndAlsoStartsSpread = _bookDom.SelectSingleNode( "//div[contains(@class,'bloom-frontMatter')][last()]" // last frontmatter page + "/following-sibling::div[contains(@data-page, 'spread-start')]"); // page after that says it needs to be facing the next page if (firstContentPageAndAlsoStartsSpread != null) { var flyDom = new XmlDocument(); flyDom.LoadXml(@" <div class='bloom-flyleaf bloom-frontMatter bloom-page' data-page='required singleton'> <div class='pageLabel'>Flyleaf</div> <div style='height: 100px; width:100%' data-hint='This page was automatically inserted because the following page is marked as part of a two page spread.'> </div> </div>" ); var flyleaf = _bookDom.RawDom.ImportNode(flyDom.FirstChild, true) as XmlElement; flyleaf.SetAttribute("id", Guid.NewGuid().ToString()); lastFrontMatterPage.ParentNode.InsertAfter(flyleaf, lastFrontMatterPage); SizeAndOrientation.UpdatePageSizeAndOrientationClasses(flyleaf, layout); } } }
/// <summary> /// At runtime, this string comes out of a dummy css 'content' line. For unit tests, it just comes from the test. /// </summary> /// <param name="contents"></param> /// <returns></returns> public static List <Layout> GetConfigurationsFromConfigurationOptionsString(string contents) { var layouts = new List <Layout>(); contents = "{\"root\": " + contents + "}"; //I found it really hard to work with the json libraries, so I just convert it to xml. It's weird xml, but at least it's not like trying to mold smoke. XmlDocument doc = (XmlDocument)JsonConvert.DeserializeXmlNode(contents); var root = doc.SelectSingleNode("root"); foreach (XmlElement element in root.SelectNodes("layouts")) { foreach (var sizeAndOrientation in element.ChildNodes) { if (sizeAndOrientation is XmlText) { layouts.Add(new Layout() { SizeAndOrientation = SizeAndOrientation.FromString(((XmlText)sizeAndOrientation).InnerText) }); } else if (sizeAndOrientation is XmlElement) { SizeAndOrientation soa = SizeAndOrientation.FromString(((XmlElement)sizeAndOrientation).Name); foreach (XmlElement option in ((XmlElement)sizeAndOrientation).ChildNodes) { if (option.Name.ToLowerInvariant() != "styles") { continue; //we don't handle anything else yet } layouts.Add(new Layout() { SizeAndOrientation = soa, Style = option.InnerText }); // List<string> choices = null; // if (!soa.Options.TryGetValue(option.Name, out choices)) // { // choices = new List<string>(); // } // else // { // soa.Options.Remove(option.Name); // } // // foreach (XmlText choice in option.ChildNodes) // { // choices.Add(choice.Value); // } // soa.Options.Add(option.Name, choices); } // layouts.Add(soa); } } } return(layouts); }
public void InjectXMatter(Dictionary <string, string> writingSystemCodes, Layout layout) { //don't want to pollute shells with this content if (!string.IsNullOrEmpty(FolderPathForCopyingXMatterFiles)) { //copy over any image files used by this front matter string path = Path.GetDirectoryName(PathToXMatterHtml); foreach (var file in Directory.GetFiles(path, "*.png").Concat(Directory.GetFiles(path, "*.jpg").Concat(Directory.GetFiles(path, "*.gif").Concat(Directory.GetFiles(path, "*.bmp"))))) { File.Copy(file, FolderPathForCopyingXMatterFiles.CombineForPath(Path.GetFileName(file)), true); } } //note: for debugging the template/css purposes, it makes our life easier if, at runtime, the html is pointing the original. //makes it easy to drop into a css editor and fix it up with the content we're looking at. //TODO:But then later, we want to save it so that these are found in the same dir as the book. _bookDom.AddStyleSheet(PathToStyleSheetForPaperAndOrientation); //it's important that we append *after* this, so that these values take precendance (the template will just have empty values for this stuff) //REVIEW: I think all stylesheets now get sorted once they are all added: see HtmlDoc.SortStyleSheetLinks() XmlNode divBeforeNextFrontMattterPage = _bookDom.RawDom.SelectSingleNode("//body/div[@id='bloomDataDiv']"); foreach (XmlElement xmatterPage in XMatterDom.SafeSelectNodes("/html/body/div[contains(@data-page,'required')]")) { var newPageDiv = _bookDom.RawDom.ImportNode(xmatterPage, true) as XmlElement; //give a new id, else thumbnail caches get messed up becuase every book has, for example, the same id for the cover. newPageDiv.SetAttribute("id", Guid.NewGuid().ToString()); if (IsBackMatterPage(xmatterPage)) { //note: this is redundant unless this is the 1st backmatterpage in the list divBeforeNextFrontMattterPage = _bookDom.RawDom.SelectSingleNode("//body/div[last()]"); } //we want the xmatter pages to match what we found in the source book SizeAndOrientation.UpdatePageSizeAndOrientationClasses(newPageDiv, layout); newPageDiv.InnerXml = newPageDiv.InnerXml.Replace("'V'", '"' + writingSystemCodes["V"] + '"'); newPageDiv.InnerXml = newPageDiv.InnerXml.Replace("\"V\"", '"' + writingSystemCodes["V"] + '"'); newPageDiv.InnerXml = newPageDiv.InnerXml.Replace("'N1'", '"' + writingSystemCodes["N1"] + '"'); newPageDiv.InnerXml = newPageDiv.InnerXml.Replace("\"N1\"", '"' + writingSystemCodes["N1"] + '"'); if (!String.IsNullOrEmpty(writingSystemCodes["N2"])) //otherwise, styleshee will hide it { newPageDiv.InnerXml = newPageDiv.InnerXml.Replace("'N2'", '"' + writingSystemCodes["N2"] + '"'); newPageDiv.InnerXml = newPageDiv.InnerXml.Replace("\"N2\"", '"' + writingSystemCodes["N2"] + '"'); } _bookDom.RawDom.SelectSingleNode("//body").InsertAfter(newPageDiv, divBeforeNextFrontMattterPage); divBeforeNextFrontMattterPage = newPageDiv; //enhance... this is really ugly. I'm just trying to clear out any remaining "{blah}" left over from the template foreach (XmlElement e in newPageDiv.SafeSelectNodes("//*[starts-with(text(),'{')]")) { foreach (var node in e.ChildNodes) { XmlText t = node as XmlText; if (t != null && t.Value.StartsWith("{")) { t.Value = ""; //otherwise html tidy will through away span's (at least) that are empty, so we never get a chance to fill in the values. } } } } }
public void InjectXMatter(Dictionary <string, string> writingSystemCodes, Layout layout, bool orderXmatterForDeviceUse, string metadataIsoCode) { //don't want to pollute shells with this content if (!string.IsNullOrEmpty(FolderPathForCopyingXMatterFiles)) { //copy over any image files used by this front matter string path = Path.GetDirectoryName(PathToXMatterHtml); foreach (var file in Directory.GetFiles(path, "*.png").Concat(Directory.GetFiles(path, "*.jpg").Concat(Directory.GetFiles(path, "*.gif").Concat(Directory.GetFiles(path, "*.bmp"))))) { string destFileName = FolderPathForCopyingXMatterFiles.CombineForPath(Path.GetFileName(file)); Utils.LongPathAware.ThrowIfExceedsMaxPath(destFileName); RobustFile.Copy(file, destFileName, true); } } //note: for debugging the template/css purposes, it makes our life easier if, at runtime, the html is pointing the original. //makes it easy to drop into a css editor and fix it up with the content we're looking at. //TODO:But then later, we want to save it so that these are found in the same dir as the book. _bookDom.AddStyleSheet(PathToXMatterStylesheet.ToLocalhost()); // Get the xMatter stylesheet link in the proper place rather than at the end. // See https://issues.bloomlibrary.org/youtrack/issue/BL-8845. _bookDom.SortStyleSheetLinks(); //it's important that we append *after* this, so that these values take precedence (the template will just have empty values for this stuff) //REVIEW: I think all stylesheets now get sorted once they are all added: see HtmlDoc.SortStyleSheetLinks() XmlNode divBeforeNextFrontMatterPage = _bookDom.RawDom.SelectSingleNode("//body/div[@id='bloomDataDiv']"); foreach (XmlElement xmatterPage in XMatterDom.SafeSelectNodes("/html/body/div[contains(@data-page,'required')]")) { var newPageDiv = _bookDom.RawDom.ImportNode(xmatterPage, true) as XmlElement; //give a new id, else thumbnail caches get messed up because every book has, for example, the same id for the cover. newPageDiv.SetAttribute("id", Guid.NewGuid().ToString()); // Various fields don't have a useful lang attribute value (doesn't exist or is "*") but we need a lang attribute to set the font properly. // By setting it on the page, all those fields can properly inherit the language 2 code and thus use its font. See BL-8545. // Since we are only doing this for xmatter, we don't have to worry about the lang attribute staying around when it shouldn't. // Old Blooms will effectively remove it when injecting xmatter. New Blooms will always set it to the current language 2 code. newPageDiv.SetAttribute("lang", metadataIsoCode); // We have some xmatters that don't know about devices, and these we replace with the // standard Device Xmatter as needed. // There is a second type where we have explicitly made a special xmatter just for devices: // ABC, Dari, and Pasti are examples of these. // Finally, we have other xmatters that deal with device formatting via a stylesheet, without having // a second folder and html for devices (e.g. Kyrgyzstan 2020). For this last one, we want to // just automatically reorder the pages, when we are preparing the document for publishing // to device contexts. var moveNonCoverToBack = orderXmatterForDeviceUse && !PathToXMatterStylesheet.Contains("Device-XMatter"); if (IsBackMatterPage(xmatterPage) || (moveNonCoverToBack && ShouldBeInBackForDeviceUse(xmatterPage))) { //note: this is redundant unless this is the 1st backmatterpage in the list divBeforeNextFrontMatterPage = _bookDom.RawDom.SelectSingleNode("//body/div[last()]"); } //we want the xmatter pages to match what we found in the source book SizeAndOrientation.UpdatePageSizeAndOrientationClasses(newPageDiv, layout); //any @lang attributes that have a metalanguage code (N1, N2, V) get filled with the actual code. //note that this older method is crude, as you're in trouble if the user changes one of those to //a different language. Instead, use data-metalanguage. foreach (XmlElement node in newPageDiv.SafeSelectNodes("//*[@lang]")) { var lang = node.GetAttribute("lang"); if (writingSystemCodes.ContainsKey(lang)) { node.SetAttribute("lang", writingSystemCodes[lang]); } } _bookDom.RawDom.SelectSingleNode("//body").InsertAfter(newPageDiv, divBeforeNextFrontMatterPage); divBeforeNextFrontMatterPage = newPageDiv; //enhance... this is really ugly. I'm just trying to clear out any remaining "{blah}" left over from the template foreach (XmlElement e in newPageDiv.SafeSelectNodes("//*[starts-with(text(),'{')]")) { foreach (var node in e.ChildNodes) { XmlText t = node as XmlText; if (t != null && t.Value.StartsWith("{")) { t.Value = ""; //otherwise html tidy will through away span's (at least) that are empty, so we never get a chance to fill in the values. } } } } InjectFlyleafIfNeeded(layout); }