// "Therefore those skilled at the unorthodox // are infinite as heaven and earth, // inexhaustible as the great rivers. // When they come to an end, // they begin again, // like the days and months; // they die and are reborn, // like the four seasons." // // - Sun Tsu, // "The Art of War" using System; using System.Collections.Generic; using TheArtOfDev.HtmlRenderer.Adapters; using TheArtOfDev.HtmlRenderer.Adapters.Entities; using TheArtOfDev.HtmlRenderer.Core.Utils; namespace TheArtOfDev.HtmlRenderer.Core.Handlers { /// /// Utilities for fonts and fonts families handling. /// internal sealed class FontsHandler { #region Fields and Consts /// /// /// private readonly RAdapter _adapter; /// /// Allow to map not installed fonts to different /// private readonly Dictionary _fontsMapping = new Dictionary(StringComparer.InvariantCultureIgnoreCase); /// /// collection of all installed and added font families to check if font exists /// private readonly Dictionary _existingFontFamilies = new Dictionary(StringComparer.InvariantCultureIgnoreCase); /// /// cache of all the font used not to create same font again and again /// private readonly Dictionary>> _fontsCache = new Dictionary>>(StringComparer.InvariantCultureIgnoreCase); #endregion /// /// Init. /// public FontsHandler(RAdapter adapter) { ArgChecker.AssertArgNotNull(adapter, "global"); _adapter = adapter; } /// /// Check if the given font family exists by name /// /// the font to check /// true - font exists by given family name, false - otherwise public bool IsFontExists(string family) { bool exists = _existingFontFamilies.ContainsKey(family); if (!exists) { string mappedFamily; if (_fontsMapping.TryGetValue(family, out mappedFamily)) { exists = _existingFontFamilies.ContainsKey(mappedFamily); } } return exists; } /// /// Adds a font family to be used. /// /// The font family to add. public void AddFontFamily(RFontFamily fontFamily) { ArgChecker.AssertArgNotNull(fontFamily, "family"); _existingFontFamilies[fontFamily.Name] = fontFamily; } /// /// Adds a font mapping from to iff the is not found.
/// When the font is used in rendered html and is not found in existing /// fonts (installed or added) it will be replaced by .
///
/// the font family to replace /// the font family to replace with public void AddFontFamilyMapping(string fromFamily, string toFamily) { ArgChecker.AssertArgNotNullOrEmpty(fromFamily, "fromFamily"); ArgChecker.AssertArgNotNullOrEmpty(toFamily, "toFamily"); _fontsMapping[fromFamily] = toFamily; } /// /// Get cached font instance for the given font properties.
/// Improve performance not to create same font multiple times. ///
/// cached font instance public RFont GetCachedFont(string family, double size, RFontStyle style) { var font = TryGetFont(family, size, style); if (font == null) { if (!_existingFontFamilies.ContainsKey(family)) { string mappedFamily; if (_fontsMapping.TryGetValue(family, out mappedFamily)) { font = TryGetFont(mappedFamily, size, style); if (font == null) { font = CreateFont(mappedFamily, size, style); _fontsCache[mappedFamily][size][style] = font; } } } if (font == null) { font = CreateFont(family, size, style); } _fontsCache[family][size][style] = font; } return font; } #region Private methods /// /// Get cached font if it exists in cache or null if it is not. /// private RFont TryGetFont(string family, double size, RFontStyle style) { RFont font = null; if (_fontsCache.ContainsKey(family)) { var a = _fontsCache[family]; if (a.ContainsKey(size)) { var b = a[size]; if (b.ContainsKey(style)) { font = b[style]; } } else { _fontsCache[family][size] = new Dictionary(); } } else { _fontsCache[family] = new Dictionary>(); _fontsCache[family][size] = new Dictionary(); } return font; } /// /// create font (try using existing font family to support custom fonts) /// private RFont CreateFont(string family, double size, RFontStyle style) { RFontFamily fontFamily; try { return _existingFontFamilies.TryGetValue(family, out fontFamily) ? _adapter.CreateFont(fontFamily, size, style) : _adapter.CreateFont(family, size, style); } catch { // handle possibility of no requested style exists for the font, use regular then return _existingFontFamilies.TryGetValue(family, out fontFamily) ? _adapter.CreateFont(fontFamily, size, RFontStyle.Regular) : _adapter.CreateFont(family, size, RFontStyle.Regular); } } #endregion } }