/// <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); } }
/// <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"); }
/// <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"); }