Пример #1
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");
        }
Пример #2
0
        /// <summary>
        /// Checks the wanted fonts for being valid for  embedding, both for licensing and for the type of file
        /// (based on the filename extension).
        /// The list of rejected fonts is returned in badFonts and the list of files to copy for good fonts is
        /// returned in filesToEmbed.  Messages are written to the progress output as the processing goes along.
        /// </summary>
        /// <remarks>
        /// fontFileFinder must be either a new instance or a stub for testing.
        /// Setting fontFileFinder.NoteFontsWeCantInstall ensures that fontFileFinder.GetFilesForFont(font)
        /// will not return any files for fonts that we know cannot be embedded without reference to the
        /// license details.
        /// </remarks>
        public static void CheckFontsForEmbedding(IWebSocketProgress progress, HashSet <string> fontsWanted, IFontFinder fontFileFinder,
                                                  out List <string> filesToEmbed, out HashSet <string> badFonts)
        {
            filesToEmbed = new List <string>();
            badFonts     = new HashSet <string>();
            const string defaultFont = "Andika New Basic";

            fontFileFinder.NoteFontsWeCantInstall = true;
            if (_fontMetadataMap == null)
            {
                _fontMetadataMap = new Dictionary <string, FontMetadata>();
                foreach (var meta in FontsApi.AvailableFontMetadata)
                {
                    _fontMetadataMap.Add(meta.name, meta);
                }
            }
            foreach (var font in fontsWanted)
            {
                var fontFiles      = fontFileFinder.GetFilesForFont(font);
                var filesFound     = fontFiles.Any();             // unembeddable fonts determined don't have any files recorded
                var badLicense     = false;
                var missingLicense = false;
                var badFileType    = false;
                var fileExtension  = "";
                if (_fontMetadataMap.TryGetValue(font, out var meta))
                {
                    fileExtension = meta.fileExtension;
                    switch (meta.determinedSuitability)
                    {
                    case FontMetadata.kUnsuitable:
                        badLicense     = true;
                        missingLicense = false;
                        break;

                    case FontMetadata.kInvalid:
                        // We don't really know the values for badLicense and missingLicense, but they don't matter.
                        badFileType = true;
                        break;

                    case FontMetadata.kUnknown:                              // same as not finding the metadata for the font.
                        badLicense     = false;
                        missingLicense = true;
                        break;

                    case FontMetadata.kOK:
                        badLicense     = false;
                        missingLicense = false;
                        break;
                    }
                }
                else
                {
                    missingLicense = true;
                    // This is usually covered by the case kInvalid above, but needed if no metadata at all.
                    if (filesFound)
                    {
                        fileExtension = Path.GetExtension(fontFiles.First()).ToLowerInvariant();
                        badFileType   = !FontMetadata.fontFileTypesBloomKnows.Contains(fileExtension);
                    }
                }
                if (filesFound && !badFileType && !badLicense)
                {
                    filesToEmbed.AddRange(fontFiles);
                    if (missingLicense)
                    {
                        progress.MessageWithParams("PublishTab.Android.File.Progress.UnknownLicense", "{0} is a font name", "Checking {0} font: Unknown license", ProgressKind.Progress, font);
                    }
                    else
                    {
                        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 (badFileType)
                {
                    progress.MessageWithParams("PublishTab.Android.File.Progress.IncompatibleFontFileFormat", "{0} is a font name, {1} is a file extension (for example: .ttc)", "This book has text in a font named \"{0}\". Bloom cannot publish this font's format ({1}).", ProgressKind.Error, font, fileExtension);
                }
                else if (fontFileFinder.FontsWeCantInstall.Contains(font) || badLicense)
                {
                    progress.MessageWithParams("PublishTab.Android.File.Progress.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("PublishTab.Android.File.Progress.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("PublishTab.Android.File.Progress.SubstitutingAndika", "{0} is a font name", "Bloom will substitute \"{0}\" instead.", ProgressKind.Error, defaultFont, font);
                badFonts.Add(font);                 // need to prevent the bad/missing font from showing up in fonts.css and elsewhere
            }
        }