451 lines
20 KiB
C#
451 lines
20 KiB
C#
|
#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
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Provides functionality to map a fontface request to a physical font.
|
||
|
/// </summary>
|
||
|
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:";
|
||
|
|
||
|
/// <summary>
|
||
|
/// Converts specified information about a required typeface into a specific font.
|
||
|
/// </summary>
|
||
|
/// <param name="familyName">Name of the font family.</param>
|
||
|
/// <param name="fontResolvingOptions">The font resolving options.</param>
|
||
|
/// <param name="typefaceKey">Typeface key if already known by caller, null otherwise.</param>
|
||
|
/// <returns>
|
||
|
/// Information about the typeface, or null if no typeface can be found.
|
||
|
/// </returns>
|
||
|
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
|
||
|
/// <summary>
|
||
|
/// Registers the font face.
|
||
|
/// </summary>
|
||
|
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
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the bytes of a physical font with specified face name.
|
||
|
/// </summary>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the bytes of a physical font with specified face name.
|
||
|
/// </summary>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets a value indicating whether at least one font source was created.
|
||
|
/// </summary>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Caches a font source under its face name and its key.
|
||
|
/// </summary>
|
||
|
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(); }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Caches a font source under its face name and its key.
|
||
|
/// </summary>
|
||
|
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<string, FontResolverInfo>.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<ulong, XFontSource>.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<string, XFontSource>.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
|
||
|
|
||
|
/// <summary>
|
||
|
/// Maps font typeface key to font resolver info.
|
||
|
/// </summary>
|
||
|
//static readonly Dictionary<string, FontResolverInfo> FontResolverInfosByTypefaceKey = new Dictionary<string, FontResolverInfo>(StringComparer.OrdinalIgnoreCase);
|
||
|
static readonly Dictionary<string, FontResolverInfo> FontResolverInfosByName = new Dictionary<string, FontResolverInfo>(StringComparer.OrdinalIgnoreCase);
|
||
|
|
||
|
///// <summary>
|
||
|
///// Maps font resolver info key to font resolver info.
|
||
|
///// </summary>
|
||
|
//static readonly Dictionary<string, FontResolverInfo> FontResolverInfosByKey = new Dictionary<string, FontResolverInfo>();
|
||
|
|
||
|
/// <summary>
|
||
|
/// Maps typeface key or font name to font source.
|
||
|
/// </summary>
|
||
|
//static readonly Dictionary<string, XFontSource> FontSourcesByTypefaceKey = new Dictionary<string, XFontSource>(StringComparer.OrdinalIgnoreCase);
|
||
|
static readonly Dictionary<string, XFontSource> FontSourcesByName = new Dictionary<string, XFontSource>(StringComparer.OrdinalIgnoreCase);
|
||
|
|
||
|
///// <summary>
|
||
|
///// Maps font name to font source.
|
||
|
///// </summary>
|
||
|
//static readonly Dictionary<string, XFontSource> FontSourcesByFontName = new Dictionary<string, XFontSource>();
|
||
|
|
||
|
/// <summary>
|
||
|
/// Maps font source key to font source.
|
||
|
/// </summary>
|
||
|
static readonly Dictionary<ulong, XFontSource> FontSourcesByKey = new Dictionary<ulong, XFontSource>();
|
||
|
}
|
||
|
}
|