NB: html class names are case sensitive! In this code, we want to accept stuff regardless of case, but always generate Capitalized paper size and orientation names
        /// <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);
                }
            }
        }
Example #2
0
        /// <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);
        }
Example #3
0
        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.
                        }
                    }
                }
            }
        }
Example #4
0
        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);
        }