예제 #1
0
 public HtmlToEpubConverter(
     Counter counter,
     ILogWriter log,
     IOptionProviderInputFile options,
     MecabParser parser,
     MecabReader reader,
     MecabBackend backend,
     XHtmlMaker xhtmlMaker,
     JmdicFastReader dicReader,
     ContentsBreaker breaker,
     EpubMaker epubMaker,
     SentenceBreaker sentenceBreaker)
 {
     _inputFile       = options.InputFile;
     _counter         = counter;
     _log             = log;
     _parser          = parser;
     _reader          = reader;
     _mecabBackend    = backend;
     _xhtmlMaker      = xhtmlMaker;
     _dicReader       = dicReader;
     _breaker         = breaker;
     _epubMaker       = epubMaker;
     _sentenceBreaker = sentenceBreaker;
 }
예제 #2
0
        /// <summary>
        /// Creates an ePub file at the location specified by parameters
        /// </summary>
        /// <param name="parameters">BookPath and epubOutputPath should be set.</param>
        /// <param name="control">The epub code needs a control that goes back to the main thread, in order to run some tasks that need to be on the main thread</param>
        public static void CreateEpubArtifact(CreateArtifactsParameters parameters, Control control)
        {
            if (String.IsNullOrEmpty(parameters.EpubOutputPath))
            {
                return;
            }

            string directoryName = Path.GetDirectoryName(parameters.EpubOutputPath);

            Directory.CreateDirectory(directoryName);                   // Ensures that the directory exists

            BookServer      bookServer  = _projectContext.BookServer;
            BookThumbNailer thumbNailer = _projectContext.ThumbNailer;
            var             maker       = new EpubMaker(thumbNailer, bookServer);

            maker.ControlForInvoke = control;

            maker.Book            = _book;
            maker.Unpaginated     = true;         // so far they all are
            maker.OneAudioPerPage = true;         // default used in EpubApi
            // Enhance: maybe we want book to have image descriptions on page? use reader font sizes?

            // Make the epub
            maker.SaveEpub(parameters.EpubOutputPath, new NullWebSocketProgress());
        }
예제 #3
0
 internal void SaveAsEpub()
 {
     lock (_epubMakerLock)
     {
         if (_pendingSaveAsPath == null)
         {
             return;
         }
         EpubMaker.ZipAndSaveEpub(_pendingSaveAsPath, _progress);
         _pendingSaveAsPath = null;
     }
 }
예제 #4
0
 internal void PrepareToStageEpub()
 {
     if (EpubMaker != null)
     {
         //it has state that we don't want to reuse, so make a new one
         EpubMaker.Dispose();
         EpubMaker = null;
     }
     EpubMaker             = new EpubMaker(_thumbNailer, _isoloator);
     EpubMaker.Book        = BookSelection.CurrentSelection;
     EpubMaker.Unpaginated = true;             // Enhance: UI?
 }
예제 #5
0
        public static int Handle(GetUsedFontsParameters options)
        {
            if (!Directory.Exists(options.BookPath))
            {
                if (options.BookPath.Contains(".htm"))
                {
                    Debug.WriteLine("Supply only the directory, not the path to the file.");
                    Console.Error.WriteLine("Supply only the directory, not the path to the file.");
                }
                else
                {
                    Debug.WriteLine("Could not find " + options.BookPath);
                    Console.Error.WriteLine("Could not find " + options.BookPath);
                }
                return(1);
            }
            Console.WriteLine("Gathering font data.");

            // Some of this might be useful if we end up needing to instantiate the book to figure out what
            // is REALLY needed (as opposed to just mentioned in a style sheet).
            //var collectionFolder = Path.GetDirectoryName(options.BookPath);
            //var projectSettingsPath = Directory.GetFiles(collectionFolder, "*.bloomCollection").FirstOrDefault();
            //var collectionSettings = new CollectionSettings(projectSettingsPath);

            //XMatterPackFinder xmatterFinder = new XMatterPackFinder(new[] {BloomFileLocator.GetInstalledXMatterDirectory()});
            //var locator = new BloomFileLocator(collectionSettings, xmatterFinder, ProjectContext.GetFactoryFileLocations(),
            //	ProjectContext.GetFoundFileLocations(), ProjectContext.GetAfterXMatterFileLocations());

            //var bookInfo = new BookInfo(options.BookPath, true);
            //var book = new Book.Book(bookInfo, new BookStorage(options.BookPath, locator, new BookRenamedEvent(), collectionSettings),
            //	null, collectionSettings, null, null, new BookRefreshEvent());

            //book.BringBookUpToDate(new NullProgress());

            var fonts = EpubMaker.GetFontsUsed(options.BookPath, false).ToList();

            fonts.Sort();

            Directory.CreateDirectory(Path.GetDirectoryName(options.ReportPath));

            using (var report = new StreamWriter(options.ReportPath))
            {
                foreach (var font in fonts)
                {
                    report.WriteLine(font);
                }
            }
            Console.WriteLine("Finished gathering font data.");
            Debug.WriteLine("Finished gathering font data.");
            return(0);
        }
예제 #6
0
        public void UpdateAndSave(EpubPublishUiSettings newSettings, string path, bool force, WebSocketProgress progress = null)
        {
            bool succeeded;

            do
            {
                lock (this)
                {
                    succeeded = UpdatePreview(newSettings, force, progress);
                    if (succeeded)
                    {
                        EpubMaker.SaveEpub(path, _progress);
                        _webSocketServer.SendString(kWebsocketContext, kWebsocketEventId_epubReady, _previewSrc);
                    }
                }
            } while (!succeeded && !EpubMaker.AbortRequested);             // try until we get a complete epub, not interrupted by user changing something.
        }
예제 #7
0
        internal void PrepareToStageEpub()
        {
            lock (_epubMakerLock)
            {
                if (EpubMaker != null)
                {
                    //it has state that we don't want to reuse, so make a new one
                    EpubMaker.Dispose();
                    EpubMaker = null;
                }

                EpubMaker = new EpubMaker(_thumbNailer, _bookServer);
            }

            EpubMaker.Book            = _bookSelection.CurrentSelection;
            EpubMaker.Unpaginated     = true;         // Enhance: UI?
            EpubMaker.OneAudioPerPage = true;
        }
예제 #8
0
        public string SetupEpubControlContent()
        {
            // This gets called on a background thread but one step needs to happen on the UI thread,
            // so the Maker needs a control to Invoke on. An Api class doesn't naturally have one to give it,
            // so we arrange that this class is given the Bloom main window by the PublishView when the preview
            // window first comes up. In production, this is roughly equivalent to just using
            // Form.ActiveForms.Last(), but that fails when debugging; this is more robust.
            EpubMaker.ControlForInvoke = ControlForInvoke;
            EpubMaker.StageEpub(_progress);
            if (StagingDirectory == null)
            {
                return(null);                // aborted, hopefully already reported.
            }
            var fileLocator = _bookSelection.CurrentSelection.GetFileLocator();
            var root        = fileLocator.LocateDirectoryWithThrow("Readium");
            var tempFolder  = Path.GetDirectoryName(StagingDirectory);

            // This is kludge. I hope it can be improved. To make a preview we currently need all the Readium
            // files in a folder that is a parent of the staging folder containing the book content.
            // This allows us to tell Readium about the book by passing the name of the folder using the ?ePUB=
            // URL parameter. It doesn't work to use the original Readium file and make the parameter a full path.
            // It's possible that there is some variation that would work, e.g., make the param a full file:/// url
            // to the book folder. It's also possible we could get away with only copying the HTML file itself,
            // if we modified it to have localhost: links to the JS and CSS. Haven't tried this yet. The current
            // approach at least works.
            // Note October 2018: upgraded to Readium 0.3.2. Doc indicates this definitely should support
            // using any url (that doesn't involve a cross-domain problem) in the ?epub= param.
            // So we could probably now rewrite this to not copy the Readium files over. Not sure it's worth the effort.
            DirectoryUtilities.CopyDirectoryContents(root, tempFolder);

            // Not sure if we will need this. The current UI does not appear to have a way to indicate whether
            // we have a talking book, a book without audio, or one that has audio but it is not being published.
            //var audioSituationClass = "noAudioAvailable";
            //if (EpubMaker.PublishWithoutAudio)
            //	audioSituationClass = "haveAudioButNotMakingTalkingBook";
            //else if (BookHasAudio)
            //	audioSituationClass = "isTalkingBook";

            var targetFile = Path.Combine(tempFolder, "index.html");

            var iframeSource = targetFile.ToLocalhost() + "?epub=" + Path.GetFileName(StagingDirectory);

            return(iframeSource);
        }
예제 #9
0
        /// <summary>
        /// Given a book, typically one in a temporary folder made just for exporting (or testing),
        /// and given the set of fonts found while creating that book and removing hidden elements,
        /// find the files needed for those fonts.
        /// Copy the font file for the normal style of that font family from the system font folder,
        /// if permitted; or post a warning in progress if we can't embed it.
        /// Create an extra css file (fonts.css) which tells the book to find the font files for those font families
        /// in the local folder, and insert a link to it into the book.
        /// </summary>
        /// <param name="fontFileFinder">use new FontFinder() for real, or a stub in testing</param>
        public static void EmbedFonts(Book.Book book, IWebSocketProgress progress, HashSet <string> fontsWanted, IFontFinder fontFileFinder)
        {
            const string defaultFont = "Andika New Basic";             // already in BR, don't need to embed or make rule.

            fontsWanted.Remove(defaultFont);
            PublishHelper.CheckFontsForEmbedding(progress, fontsWanted, fontFileFinder, out List <string> filesToEmbed, out HashSet <string> badFonts);
            foreach (var file in filesToEmbed)
            {
                // Enhance: do we need to worry about problem characters in font file names?
                var dest = Path.Combine(book.FolderPath, Path.GetFileName(file));
                RobustFile.Copy(file, dest);
            }
            // Create the fonts.css file, which tells the browser where to find the fonts for those families.
            var sb = new StringBuilder();

            foreach (var font in fontsWanted)
            {
                if (badFonts.Contains(font))
                {
                    continue;
                }
                var group = fontFileFinder.GetGroupForFont(font);
                if (group != null)
                {
                    EpubMaker.AddFontFace(sb, font, "normal", "normal", group.Normal);
                }
                // We don't need (or want) a rule to use Andika instead.
                // The reader typically WILL use Andika, because we have a rule making it the default font
                // for the whole body of the document, and BloomReader always has it available.
                // However, it's possible that although we aren't allowed to embed the desired font,
                // the device actually has it installed. In that case, we want to use it.
            }
            RobustFile.WriteAllText(Path.Combine(book.FolderPath, "fonts.css"), sb.ToString());
            // Tell the document to use the new stylesheet.
            book.OurHtmlDom.AddStyleSheet("fonts.css");
            // Repair defaultLangStyles.css and other places in the output book if needed.
            if (badFonts.Any())
            {
                PublishHelper.FixCssReferencesForBadFonts(book.FolderPath, defaultFont, badFonts);
                PublishHelper.FixXmlDomReferencesForBadFonts(book.OurHtmlDom.RawDom, defaultFont, badFonts);
            }
        }
예제 #10
0
        public void Dispose()
        {
            if (RobustFile.Exists(PdfFilePath))
            {
                try
                {
                    RobustFile.Delete(PdfFilePath);
                }
                catch (Exception)
                {
                }
            }
            if (EpubMaker != null)
            {
                EpubMaker.Dispose();
                EpubMaker = null;
            }

            GC.SuppressFinalize(this);
        }
예제 #11
0
        internal void SaveAsEpub()
        {
            using (var dlg = new SaveFileDialog())
            {
                if (!string.IsNullOrEmpty(_lastDirectory) && Directory.Exists(_lastDirectory))
                {
                    dlg.InitialDirectory = _lastDirectory;
                }

                string suggestedName = string.Format("{0}-{1}.epub", Path.GetFileName(BookSelection.CurrentSelection.FolderPath),
                                                     _collectionSettings.GetLanguage1Name("en"));
                dlg.FileName = suggestedName;
                dlg.Filter   = "EPUB|*.epub";
                if (DialogResult.OK == dlg.ShowDialog())
                {
                    _lastDirectory = Path.GetDirectoryName(dlg.FileName);
                    EpubMaker.FinishEpub(dlg.FileName);
                    ReportAnalytics("Save ePUB");
                }
            }
        }
예제 #12
0
        /// <summary>
        /// Given a book, typically one in a temporary folder made just for exporting (or testing),
        /// examine the CSS files and determine what fonts should be necessary. (Enhance: we could actually
        /// load the book into a DOM and find out what font IS used for each block.)
        /// Copy the font file for the normal style of that font family from the system font folder,
        /// if permitted; or post a warning in progress if we can't embed it.
        /// Create an extra css file (fonts.css) which tells the book to find the font files for those font families
        /// in the local folder, and insert a link to it into the book.
        /// </summary>
        /// <param name="book"></param>
        /// <param name="progress"></param>
        /// <param name="fontFileFinder">use new FontFinder() for real, or a stub in testing</param>
        public static void EmbedFonts(Book book, IWebSocketProgress progress, IFontFinder fontFileFinder)
        {
            const string defaultFont = "Andika New Basic";             // already in BR, don't need to embed or make rule.
            // The 'false' here says to ignore all but the first font face in CSS's ordered lists of desired font faces.
            // If someone is publishing an Epub, they should have that font showing. For one thing, this makes it easier
            // for us to not embed fonts we don't want/ need.For another, it makes it less likely that an epub will look
            // different or have glyph errors when shown on a machine that does have that primary font.
            var fontsWanted = EpubMaker.GetFontsUsed(book.FolderPath, false).ToList();

            fontsWanted.Remove(defaultFont);
            fontFileFinder.NoteFontsWeCantInstall = true;
            var filesToEmbed = new List <string>();

            foreach (var font in fontsWanted)
            {
                var fontFiles = fontFileFinder.GetFilesForFont(font);
                if (fontFiles.Count() > 0)
                {
                    filesToEmbed.AddRange(fontFiles);
                    progress.MessageWithParams("CheckFontOK", "{0} is a font name", "Checking {0} font: License OK for embedding.", font);
                    // Assumes only one font file per font; if we embed multiple ones will need to enhance this.
                    var size         = new FileInfo(fontFiles.First()).Length;
                    var sizeToReport = (size / 1000000.0).ToString("F1");                     // purposely locale-specific; might be e.g. 1,2
                    progress.MessageWithColorAndParams("Embedding",
                                                       "{1} is a number with one decimal place, the number of megabytes the font file takes up",
                                                       "blue",
                                                       "Embedding font {0} at a cost of {1} megs",
                                                       font, sizeToReport);
                    continue;
                }
                if (fontFileFinder.FontsWeCantInstall.Contains(font))
                {
                    progress.ErrorWithParams("LicenseForbids", "{0} is a font name", "Checking {0} font: License does not permit embedding.", font);
                }
                else
                {
                    progress.ErrorWithParams("NoFontFound", "{0} is a font name", "Checking {0} font: No font found to embed.", font);
                }
                progress.ErrorWithParams("SubstitutingAndika", "{0} and {1} are font names", "Substituting \"{0}\" for \"{1}\"", defaultFont, font);
            }
            foreach (var file in filesToEmbed)
            {
                // Enhance: do we need to worry about problem characters in font file names?
                var dest = Path.Combine(book.FolderPath, Path.GetFileName(file));
                RobustFile.Copy(file, dest);
            }
            // Create the fonts.css file, which tells the browser where to find the fonts for those families.
            var sb = new StringBuilder();

            foreach (var font in fontsWanted)
            {
                var group = fontFileFinder.GetGroupForFont(font);
                if (group != null)
                {
                    EpubMaker.AddFontFace(sb, font, "normal", "normal", group.Normal);
                }
                // We don't need (or want) a rule to use Andika instead.
                // The reader typically WILL use Andika, because we have a rule making it the default font
                // for the whole body of the document, and BloomReader always has it available.
                // However, it's possible that although we aren't allowed to embed the desired font,
                // the device actually has it installed. In that case, we want to use it.
            }
            RobustFile.WriteAllText(Path.Combine(book.FolderPath, "fonts.css"), sb.ToString());
            // Tell the document to use the new stylesheet.
            book.OurHtmlDom.AddStyleSheet("fonts.css");
        }
예제 #13
0
        private void RemoveUnwantedContentInternal(HtmlDom dom, Book.Book book, bool removeInactiveLanguages, EpubMaker epubMaker, ISet <string> warningMessages, bool keepPageLabels = false)
        {
            // The ControlForInvoke can be null for tests.  If it's not null, we better not need an Invoke!
            Debug.Assert(ControlForInvoke == null || !ControlForInvoke.InvokeRequired);           // should be called on UI thread.
            Debug.Assert(dom != null && dom.Body != null);

            // Collect all the page divs.
            var pageElts = new List <XmlElement>();

            if (epubMaker != null)
            {
                pageElts.Add((XmlElement)dom.Body.FirstChild);                  // already have a single-page dom prepared for export
            }
            else
            {
                foreach (XmlElement page in book.GetPageElements())
                {
                    pageElts.Add(page);
                }
            }

            RemoveEnterpriseFeaturesIfNeeded(book, pageElts, warningMessages);

            // Remove any left-over bubbles
            foreach (XmlElement elt in dom.RawDom.SafeSelectNodes("//label"))
            {
                if (HasClass(elt, "bubble"))
                {
                    elt.ParentNode.RemoveChild(elt);
                }
            }
            // Remove page labels and descriptions.  Also remove pages (or other div elements) that users have
            // marked invisible.  (The last mimics the effect of bookLayout/languageDisplay.less for editing
            // or PDF published books.)
            foreach (XmlElement elt in dom.RawDom.SafeSelectNodes("//div"))
            {
                if (!book.IsTemplateBook)
                {
                    if (!keepPageLabels && HasClass(elt, "pageLabel"))
                    {
                        elt.ParentNode.RemoveChild(elt);
                    }

                    if (HasClass(elt, "pageDescription"))
                    {
                        elt.ParentNode.RemoveChild(elt);
                    }
                }

                // REVIEW: is this needed now with the new strategy?
                if (HasClass(elt, "bloom-editable") && HasClass(elt, "bloom-visibility-user-off"))
                {
                    elt.ParentNode.RemoveChild(elt);
                }
            }
            // Our recordingmd5 attribute is not allowed by epub
            foreach (XmlElement elt in HtmlDom.SelectAudioSentenceElementsWithRecordingMd5(dom.RawDom.DocumentElement))
            {
                elt.RemoveAttribute("recordingmd5");
            }
            // Users should not be able to edit content of published books
            foreach (XmlElement elt in dom.RawDom.SafeSelectNodes("//div[@contenteditable]"))
            {
                elt.RemoveAttribute("contenteditable");
            }

            foreach (var div in dom.Body.SelectNodes("//div[@role='textbox']").Cast <XmlElement>())
            {
                div.RemoveAttribute("role");                                    // this isn't an editable textbox in an ebook
                div.RemoveAttribute("aria-label");                              // don't want this without a role
                div.RemoveAttribute("spellcheck");                              // too late for spell checking in an ebook
                div.RemoveAttribute("content-editable");                        // too late for editing in an ebook
            }

            // Clean up img elements (BL-6035/BL-6036 and BL-7218)
            foreach (var img in dom.Body.SelectNodes("//img").Cast <XmlElement>())
            {
                // Ensuring a proper alt attribute is handled elsewhere
                var src = img.GetOptionalStringAttribute("src", null);
                if (String.IsNullOrEmpty(src) || src == "placeHolder.png")
                {
                    // If this is a template book, then the whole point of the book is to not have content. So then we want to preserve the placeholders so
                    // that people looking at the book on Bloom Library can see how the template pages are constructed.
                    if (!book.IsTemplateBook)
                    {
                        // If the image file doesn't exist, we want to find out about it.  But if there is no
                        // image file, epubcheck complains and it doesn't do any good anyway.
                        img.ParentNode.RemoveChild(img);
                    }
                }
                else
                {
                    var parent = img.ParentNode as XmlElement;
                    parent.RemoveAttribute("title");      // We don't want this in published books.
                    img.RemoveAttribute(
                        "title");                         // We don't want this in published books.  (probably doesn't exist)
                    img.RemoveAttribute("type");          // This is invalid, but has appeared for svg branding images.
                }
            }

            if (epubMaker != null)
            {
                // epub-check doesn't like these attributes (BL-6036).  I suppose BloomReader might find them useful.
                foreach (var div in dom.Body.SelectNodes("//div[contains(@class, 'split-pane-component-inner')]").Cast <XmlElement>())
                {
                    div.RemoveAttribute("min-height");
                    div.RemoveAttribute("min-width");
                }
            }

            // These elements are inserted and supposedly removed by the ckeditor javascript code.
            // But at least one book created by our test team still has one output to an epub.  If it
            // exists, it probably has a style attribute (position:fixed) that epubcheck won't like.
            // (fixed position way off the screen to hide it)
            foreach (var div in dom.Body.SelectNodes("//*[@data-cke-hidden-sel]").Cast <XmlElement>())
            {
                div.ParentNode.RemoveChild(div);
            }

            // Finally we try to remove elements (except image descriptions) that aren't visible.
            // To accurately determine visibility, we point a real browser at the document.
            // We've had some problems with this, which we now think are fixed; if it doesn't work, for
            // BloomReader we just allow the document to be a little bigger than it needs to be.
            // BloomReader will obey rules like display:none.
            // For epubs, we don't; display:none is not reliably obeyed, so the reader could see
            // unexpected things.

            HtmlDom displayDom = null;

            foreach (XmlElement page in pageElts)
            {
                EnsureAllThingsThatCanBeHiddenHaveIds(page);
                if (displayDom == null)
                {
                    displayDom = book.GetHtmlDomWithJustOnePage(page);
                }
                else
                {
                    var pageNode = displayDom.RawDom.ImportNode(page, true);
                    displayDom.Body.AppendChild(pageNode);
                }
            }
            if (displayDom == null)
            {
                return;
            }
            if (epubMaker != null)
            {
                epubMaker.AddEpubVisibilityStylesheetAndClass(displayDom);
            }
            if (this != _latestInstance)
            {
                return;
            }
            if (!_browser.NavigateAndWaitTillDone(displayDom, 10000, "publish", () => this != _latestInstance,
                                                  false))
            {
                // We started having problems with timeouts here (BL-7892).
                // We may as well carry on. We only need the browser to have navigated so calls to IsDisplayed(elt)
                // below will give accurate answers. Even if the browser hasn't gotten that far yet (e.g., in
                // a long document), it may stay ahead of us. We'll report a failure (currently only for epubs, see above)
                // if we actually can't find the element we need in IsDisplayed().
                Debug.WriteLine("Failed to navigate fully to RemoveUnwantedContentInternal DOM");
                Logger.WriteEvent("Failed to navigate fully to RemoveUnwantedContentInternal DOM");
            }
            if (this != _latestInstance)
            {
                return;
            }

            var toBeDeleted = new List <XmlElement>();

            // Deleting the elements in place during the foreach messes up the list and some things that should be deleted aren't
            // (See BL-5234). So we gather up the elements to be deleted and delete them afterwards.
            foreach (XmlElement page in pageElts)
            {
                // BL-9501 Don't remove pages from template books, which are often empty but we still want to show their components
                if (!book.IsTemplateBook)
                {
                    // As the constant's name here suggests, in theory, we could include divs
                    // that don't have .bloom-editable, and all their children.
                    // But I'm not smart enough to write that selector and for bloomds, all we're doing here is saving space,
                    // so those other divs we are missing doesn't seem to matter as far as I can think.
                    var kSelectThingsThatCanBeHiddenButAreNotText = ".//img";
                    var selector = removeInactiveLanguages
                                                ? kSelectThingsThatCanBeHidden
                                                : kSelectThingsThatCanBeHiddenButAreNotText;
                    foreach (XmlElement elt in page.SafeSelectNodes(selector))
                    {
                        // Even when they are not displayed we want to keep image descriptions if they aren't empty.
                        // This is necessary for retaining any associated audio files to play.
                        // (If they are empty, they won't have any audio and may trigger embedding an unneeded font.)
                        // See https://issues.bloomlibrary.org/youtrack/issue/BL-7237.
                        // As noted above, if the displayDom is not sufficiently loaded for a definitive
                        // answer to IsDisplayed, we will throw when making epubs but not for bloom reader.
                        if (!IsDisplayed(elt, epubMaker != null) && !IsNonEmptyImageDescription(elt))
                        {
                            toBeDeleted.Add(elt);
                        }
                    }

                    foreach (var elt in toBeDeleted)
                    {
                        elt.ParentNode.RemoveChild(elt);
                    }
                }
                // We need the font information for wanted text elements as well.  This is a side-effect but related to
                // unwanted elements in that we don't need fonts that are used only by unwanted elements.  Note that
                // elements don't need to be actually visible to provide computed style information such as font-family.
                foreach (XmlElement elt in page.SafeSelectNodes(".//div"))
                {
                    StoreFontUsed(elt);
                }
                RemoveTempIds(page);                 // don't need temporary IDs any more.
                toBeDeleted.Clear();
            }
        }
예제 #14
0
 /// <summary>
 /// Remove unwanted content from the XHTML of this book.  As a side-effect, store the fonts used in the remaining
 /// content of the book.
 /// </summary>
 public void RemoveUnwantedContent(HtmlDom dom, Book.Book book, bool removeInactiveLanguages, ISet <string> warningMessages, EpubMaker epubMaker = null, bool keepPageLabels = false)
 {
     FontsUsed.Clear();
     // Removing unwanted content involves a real browser really navigating. I'm not sure exactly why,
     // but things freeze up if we don't do it on the UI thread.
     if (ControlForInvoke != null)
     {
         // Linux/Mono can choose a toast as the ActiveForm.  When it closes, bad things can happen
         // trying to use it to Invoke.
         if (ControlForInvoke.IsDisposed)
         {
             ControlForInvoke = Form.ActiveForm;
         }
         ControlForInvoke.Invoke((Action)(delegate
         {
             RemoveUnwantedContentInternal(dom, book, removeInactiveLanguages, epubMaker, warningMessages, keepPageLabels);
         }));
     }
     else
     {
         RemoveUnwantedContentInternal(dom, book, removeInactiveLanguages, epubMaker, warningMessages, keepPageLabels);
     }
 }
예제 #15
0
        /// <summary>
        /// Given a book, typically one in a temporary folder made just for exporting (or testing),
        /// and given the set of fonts found while creating that book and removing hidden elements,
        /// find the files needed for those fonts.
        /// Copy the font file for the normal style of that font family from the system font folder,
        /// if permitted; or post a warning in progress if we can't embed it.
        /// Create an extra css file (fonts.css) which tells the book to find the font files for those font families
        /// in the local folder, and insert a link to it into the book.
        /// </summary>
        /// <param name="book"></param>
        /// <param name="progress"></param>
        /// <param name="fontFileFinder">use new FontFinder() for real, or a stub in testing</param>
        public static void EmbedFonts(Book.Book book, WebSocketProgress progress, HashSet <string> fontsWanted, IFontFinder fontFileFinder)
        {
            const string defaultFont = "Andika New Basic";             // already in BR, don't need to embed or make rule.

            fontsWanted.Remove(defaultFont);
            fontFileFinder.NoteFontsWeCantInstall = true;
            var filesToEmbed = new List <string>();

            foreach (var font in fontsWanted)
            {
                var fontFiles = fontFileFinder.GetFilesForFont(font);
                if (fontFiles.Count() > 0)
                {
                    filesToEmbed.AddRange(fontFiles);
                    progress.MessageWithParams("PublishTab.Android.File.Progress.CheckFontOK", "{0} is a font name", "Checking {0} font: License OK for embedding.", ProgressKind.Progress, font);
                    // Assumes only one font file per font; if we embed multiple ones will need to enhance this.
                    var size         = new FileInfo(fontFiles.First()).Length;
                    var sizeToReport = (size / 1000000.0).ToString("F1");                     // purposely locale-specific; might be e.g. 1,2
                    progress.MessageWithParams("PublishTab.Android.File.Progress.Embedding",
                                               "{1} is a number with one decimal place, the number of megabytes the font file takes up",
                                               "Embedding font {0} at a cost of {1} megs",
                                               ProgressKind.Note,
                                               font, sizeToReport);
                    continue;
                }
                if (fontFileFinder.FontsWeCantInstall.Contains(font))
                {
                    //progress.Error("Common.Warning", "Warning");
                    progress.MessageWithParams("LicenseForbids", "{0} is a font name", "This book has text in a font named \"{0}\". The license for \"{0}\" does not permit Bloom to embed the font in the book.", ProgressKind.Error, font);
                }
                else
                {
                    progress.MessageWithParams("NoFontFound", "{0} is a font name", "This book has text in a font named \"{0}\", but Bloom could not find that font on this computer.", ProgressKind.Error, font);
                }
                progress.MessageWithParams("SubstitutingAndika", "{0} is a font name", "Bloom will substitute \"{0}\" instead.", ProgressKind.Error, defaultFont, font);
            }
            foreach (var file in filesToEmbed)
            {
                // Enhance: do we need to worry about problem characters in font file names?
                var dest = Path.Combine(book.FolderPath, Path.GetFileName(file));
                RobustFile.Copy(file, dest);
            }
            // Create the fonts.css file, which tells the browser where to find the fonts for those families.
            var sb = new StringBuilder();

            foreach (var font in fontsWanted)
            {
                var group = fontFileFinder.GetGroupForFont(font);
                if (group != null)
                {
                    EpubMaker.AddFontFace(sb, font, "normal", "normal", group.Normal);
                }
                // We don't need (or want) a rule to use Andika instead.
                // The reader typically WILL use Andika, because we have a rule making it the default font
                // for the whole body of the document, and BloomReader always has it available.
                // However, it's possible that although we aren't allowed to embed the desired font,
                // the device actually has it installed. In that case, we want to use it.
            }
            RobustFile.WriteAllText(Path.Combine(book.FolderPath, "fonts.css"), sb.ToString());
            // Tell the document to use the new stylesheet.
            book.OurHtmlDom.AddStyleSheet("fonts.css");
        }