#region PDFsharp - A .NET library for processing PDF // // Authors: // Stefan Lange // // Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany) // // http://www.pdfsharp.com // http://sourceforge.net/projects/pdfsharp // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #endregion using System; using System.Diagnostics; using System.Collections.Generic; using System.Text; #if CORE || GDI using System.Drawing; using GdiFontFamily = System.Drawing.FontFamily; using GdiFont = System.Drawing.Font; #endif #if WPF using System.Windows; using System.Windows.Media; using System.Windows.Resources; using WpfFontFamily = System.Windows.Media.FontFamily; using WpfGlyphTypeface = System.Windows.Media.GlyphTypeface; using WpfTypeface = System.Windows.Media.Typeface; #endif using PdfSharp.Drawing; using PdfSharp.Fonts.OpenType; using PdfSharp.Internal; #pragma warning disable 1591 // ReSharper disable RedundantNameQualifier namespace PdfSharp.Fonts { /// /// Provides functionality to map a fontface request to a physical font. /// internal static class FontFactory { //// Suffix for internal face names to indicate that the font data comes from the platform //// and not from the users font resolver. //public const string PlatformTag = "platform:"; /// /// Converts specified information about a required typeface into a specific font. /// /// Name of the font family. /// The font resolving options. /// Typeface key if already known by caller, null otherwise. /// /// Information about the typeface, or null if no typeface can be found. /// public static FontResolverInfo ResolveTypeface(string familyName, FontResolvingOptions fontResolvingOptions, string typefaceKey) { if (string.IsNullOrEmpty(typefaceKey)) typefaceKey = XGlyphTypeface.ComputeKey(familyName, fontResolvingOptions); try { Lock.EnterFontFactory(); // Was this typeface requested before? FontResolverInfo fontResolverInfo; if (FontResolverInfosByName.TryGetValue(typefaceKey, out fontResolverInfo)) return fontResolverInfo; // Case: This typeface was not yet resolved before. // Is there a custom font resolver available? IFontResolver customFontResolver = GlobalFontSettings.FontResolver; if (customFontResolver != null) { // Case: Use custom font resolver. fontResolverInfo = customFontResolver.ResolveTypeface(familyName, fontResolvingOptions.IsBold, fontResolvingOptions.IsItalic); // If resolved by custom font resolver register info and font source. if (fontResolverInfo != null && !(fontResolverInfo is PlatformFontResolverInfo)) { // OverrideStyleSimulations is true only for internal quality tests. if (fontResolvingOptions.OverrideStyleSimulations) { // Override style simulation returned by custom font resolver. fontResolverInfo = new FontResolverInfo(fontResolverInfo.FaceName, fontResolvingOptions.MustSimulateBold, fontResolvingOptions.MustSimulateItalic, fontResolverInfo.CollectionNumber); } string resolverInfoKey = fontResolverInfo.Key; FontResolverInfo existingFontResolverInfo; if (FontResolverInfosByName.TryGetValue(resolverInfoKey, out existingFontResolverInfo)) { // Case: A new typeface was resolved with the same info as a previous one. // Discard new object an reuse previous one. fontResolverInfo = existingFontResolverInfo; // Associate with typeface key. FontResolverInfosByName.Add(typefaceKey, fontResolverInfo); #if DEBUG // The font source should exist. Debug.Assert(FontSourcesByName.ContainsKey(fontResolverInfo.FaceName)); #endif } else { // Case: No such font resolver info exists. // Add to both dictionaries. FontResolverInfosByName.Add(typefaceKey, fontResolverInfo); Debug.Assert(resolverInfoKey == fontResolverInfo.Key); FontResolverInfosByName.Add(resolverInfoKey, fontResolverInfo); // Create font source if not yet exists. XFontSource previousFontSource; if (FontSourcesByName.TryGetValue(fontResolverInfo.FaceName, out previousFontSource)) { // Case: The font source exists, because a previous font resolver info comes // with the same face name, but was different in style simulation flags. // Nothing to do. } else { // Case: Get font from custom font resolver and create font source. byte[] bytes = customFontResolver.GetFont(fontResolverInfo.FaceName); XFontSource fontSource = XFontSource.GetOrCreateFrom(bytes); // Add font source's font resolver name if it is different to the face name. if (string.Compare(fontResolverInfo.FaceName, fontSource.FontName, StringComparison.OrdinalIgnoreCase) != 0) FontSourcesByName.Add(fontResolverInfo.FaceName, fontSource); } } } } else { // Case: There was no custom font resolver set. // Use platform font resolver. // If it was successful resolver info and font source are cached // automatically by PlatformFontResolver.ResolveTypeface. fontResolverInfo = PlatformFontResolver.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey); } // Return value is null if the typeface could not be resolved. // In this case PDFsharp stops. return fontResolverInfo; } finally { Lock.ExitFontFactory(); } } #if GDI /// /// Registers the font face. /// public static XFontSource RegisterFontFace(byte[] fontBytes) { try { Lock.EnterFontFactory(); ulong key = FontHelper.CalcChecksum(fontBytes); XFontSource fontSource; if (FontSourcesByKey.TryGetValue(key, out fontSource)) { throw new InvalidOperationException("Font face already registered."); } fontSource = XFontSource.GetOrCreateFrom(fontBytes); Debug.Assert(FontSourcesByKey.ContainsKey(key)); Debug.Assert(fontSource.Fontface != null); //fontSource.Fontface = new OpenTypeFontface(fontSource); //FontSourcesByKey.Add(checksum, fontSource); //FontSourcesByFontName.Add(fontSource.FontName, fontSource); XGlyphTypeface glyphTypeface = new XGlyphTypeface(fontSource); FontSourcesByName.Add(glyphTypeface.Key, fontSource); GlyphTypefaceCache.AddGlyphTypeface(glyphTypeface); return fontSource; } finally { Lock.ExitFontFactory(); } } #endif /// /// Gets the bytes of a physical font with specified face name. /// public static XFontSource GetFontSourceByFontName(string fontName) { XFontSource fontSource; if (FontSourcesByName.TryGetValue(fontName, out fontSource)) return fontSource; Debug.Assert(false, string.Format("An XFontSource with the name '{0}' does not exists.", fontName)); return null; } /// /// Gets the bytes of a physical font with specified face name. /// public static XFontSource GetFontSourceByTypefaceKey(string typefaceKey) { XFontSource fontSource; if (FontSourcesByName.TryGetValue(typefaceKey, out fontSource)) return fontSource; Debug.Assert(false, string.Format("An XFontSource with the typeface key '{0}' does not exists.", typefaceKey)); return null; } public static bool TryGetFontSourceByKey(ulong key, out XFontSource fontSource) { return FontSourcesByKey.TryGetValue(key, out fontSource); } /// /// Gets a value indicating whether at least one font source was created. /// public static bool HasFontSources { get { return FontSourcesByName.Count > 0; } } public static bool TryGetFontResolverInfoByTypefaceKey(string typeFaceKey, out FontResolverInfo info) { return FontResolverInfosByName.TryGetValue(typeFaceKey, out info); } public static bool TryGetFontSourceByTypefaceKey(string typefaceKey, out XFontSource source) { return FontSourcesByName.TryGetValue(typefaceKey, out source); } //public static bool TryGetFontSourceByFaceName(string faceName, out XFontSource source) //{ // return FontSourcesByName.TryGetValue(faceName, out source); //} internal static void CacheFontResolverInfo(string typefaceKey, FontResolverInfo fontResolverInfo) { FontResolverInfo existingfFontResolverInfo; // Check whether identical font is already registered. if (FontResolverInfosByName.TryGetValue(typefaceKey, out existingfFontResolverInfo)) { // Should never come here. throw new InvalidOperationException(string.Format("A font file with different content already exists with the specified face name '{0}'.", typefaceKey)); } if (FontResolverInfosByName.TryGetValue(fontResolverInfo.Key, out existingfFontResolverInfo)) { // Should never come here. throw new InvalidOperationException(string.Format("A font resolver already exists with the specified key '{0}'.", fontResolverInfo.Key)); } // Add to both dictionaries. FontResolverInfosByName.Add(typefaceKey, fontResolverInfo); FontResolverInfosByName.Add(fontResolverInfo.Key, fontResolverInfo); } /// /// Caches a font source under its face name and its key. /// public static XFontSource CacheFontSource(XFontSource fontSource) { try { Lock.EnterFontFactory(); // Check whether an identical font source with a different face name already exists. XFontSource existingFontSource; if (FontSourcesByKey.TryGetValue(fontSource.Key, out existingFontSource)) { #if DEBUG // Fonts have same length and check sum. Now check byte by byte identity. int length = fontSource.Bytes.Length; for (int idx = 0; idx < length; idx++) { if (existingFontSource.Bytes[idx] != fontSource.Bytes[idx]) { //Debug.Assert(false,"Two fonts with identical checksum found."); break; //goto FontsAreNotIdentical; } } Debug.Assert(existingFontSource.Fontface != null); #endif return existingFontSource; //FontsAreNotIdentical: //// Incredible rare case: Two different fonts have the same size and check sum. //// Give the new one a new key until it do not clash with an existing one. //while (FontSourcesByKey.ContainsKey(fontSource.Key)) // fontSource.IncrementKey(); } OpenTypeFontface fontface = fontSource.Fontface; if (fontface == null) { // Create OpenType fontface for this font source. fontSource.Fontface = new OpenTypeFontface(fontSource); } FontSourcesByKey.Add(fontSource.Key, fontSource); FontSourcesByName.Add(fontSource.FontName, fontSource); return fontSource; } finally { Lock.ExitFontFactory(); } } /// /// Caches a font source under its face name and its key. /// public static XFontSource CacheNewFontSource(string typefaceKey, XFontSource fontSource) { // Debug.Assert(!FontSourcesByFaceName.ContainsKey(fontSource.FaceName)); // Check whether an identical font source with a different face name already exists. XFontSource existingFontSource; if (FontSourcesByKey.TryGetValue(fontSource.Key, out existingFontSource)) { //// Fonts have same length and check sum. Now check byte by byte identity. //int length = fontSource.Bytes.Length; //for (int idx = 0; idx < length; idx++) //{ // if (existingFontSource.Bytes[idx] != fontSource.Bytes[idx]) // { // goto FontsAreNotIdentical; // } //} return existingFontSource; ////// The bytes are really identical. Register font source again with the new face name ////// but return the existing one to save memory. ////FontSourcesByFaceName.Add(fontSource.FaceName, existingFontSource); ////return existingFontSource; //FontsAreNotIdentical: //// Incredible rare case: Two different fonts have the same size and check sum. //// Give the new one a new key until it do not clash with an existing one. //while (FontSourcesByKey.ContainsKey(fontSource.Key)) // fontSource.IncrementKey(); } OpenTypeFontface fontface = fontSource.Fontface; if (fontface == null) { fontface = new OpenTypeFontface(fontSource); fontSource.Fontface = fontface; // Also sets the font name in fontSource } FontSourcesByName.Add(typefaceKey, fontSource); FontSourcesByName.Add(fontSource.FontName, fontSource); FontSourcesByKey.Add(fontSource.Key, fontSource); return fontSource; } public static void CacheExistingFontSourceWithNewTypefaceKey(string typefaceKey, XFontSource fontSource) { try { Lock.EnterFontFactory(); FontSourcesByName.Add(typefaceKey, fontSource); } finally { Lock.ExitFontFactory(); } } internal static string GetFontCachesState() { StringBuilder state = new StringBuilder(); string[] keys; int count; // FontResolverInfo by name. state.Append("====================\n"); state.Append("Font resolver info by name\n"); Dictionary.KeyCollection keyCollection = FontResolverInfosByName.Keys; count = keyCollection.Count; keys = new string[count]; keyCollection.CopyTo(keys, 0); Array.Sort(keys, StringComparer.OrdinalIgnoreCase); foreach (string key in keys) state.AppendFormat(" {0}: {1}\n", key, FontResolverInfosByName[key].DebuggerDisplay); state.Append("\n"); // FontSource by key. state.Append("Font source by key and name\n"); Dictionary.KeyCollection fontSourceKeys = FontSourcesByKey.Keys; count = fontSourceKeys.Count; ulong[] ulKeys = new ulong[count]; fontSourceKeys.CopyTo(ulKeys, 0); Array.Sort(ulKeys, delegate (ulong x, ulong y) { return x == y ? 0 : (x > y ? 1 : -1); }); foreach (ulong ul in ulKeys) state.AppendFormat(" {0}: {1}\n", ul, FontSourcesByKey[ul].DebuggerDisplay); Dictionary.KeyCollection fontSourceNames = FontSourcesByName.Keys; count = fontSourceNames.Count; keys = new string[count]; fontSourceNames.CopyTo(keys, 0); Array.Sort(keys, StringComparer.OrdinalIgnoreCase); foreach (string key in keys) state.AppendFormat(" {0}: {1}\n", key, FontSourcesByName[key].DebuggerDisplay); state.Append("--------------------\n\n"); // FontFamilyInternal by name. state.Append(FontFamilyCache.GetCacheState()); // XGlyphTypeface by name. state.Append(GlyphTypefaceCache.GetCacheState()); // OpenTypeFontface by name. state.Append(OpenTypeFontfaceCache.GetCacheState()); return state.ToString(); } // TODO: Move to ctor /// /// Maps font typeface key to font resolver info. /// //static readonly Dictionary FontResolverInfosByTypefaceKey = new Dictionary(StringComparer.OrdinalIgnoreCase); static readonly Dictionary FontResolverInfosByName = new Dictionary(StringComparer.OrdinalIgnoreCase); ///// ///// Maps font resolver info key to font resolver info. ///// //static readonly Dictionary FontResolverInfosByKey = new Dictionary(); /// /// Maps typeface key or font name to font source. /// //static readonly Dictionary FontSourcesByTypefaceKey = new Dictionary(StringComparer.OrdinalIgnoreCase); static readonly Dictionary FontSourcesByName = new Dictionary(StringComparer.OrdinalIgnoreCase); ///// ///// Maps font name to font source. ///// //static readonly Dictionary FontSourcesByFontName = new Dictionary(); /// /// Maps font source key to font source. /// static readonly Dictionary FontSourcesByKey = new Dictionary(); } }