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