ASCU_ALL/PdfSharp/Drawing/XPrivateFontCollection.cs
2020-09-04 12:49:15 +05:00

442 lines
18 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using PdfSharp.Fonts;
#if CORE || GDI
using System.Drawing;
using System.Drawing.Drawing2D;
using GdiFontFamily = System.Drawing.FontFamily;
using GdiFont = System.Drawing.Font;
using GdiFontStyle = System.Drawing.FontStyle;
using GdiPrivateFontCollection = System.Drawing.Text.PrivateFontCollection;
#endif
#if WPF
using System.Windows.Markup;
using WpfFonts = System.Windows.Media.Fonts;
using WpfFontFamily = System.Windows.Media.FontFamily;
using WpfTypeface = System.Windows.Media.Typeface;
using WpfGlyphTypeface = System.Windows.Media.GlyphTypeface;
#endif
namespace PdfSharp.Drawing
{
#if true
///<summary>
/// Makes fonts that are not installed on the system available within the current application domain.<br/>
/// In Silverlight required for all fonts used in PDF documents.
/// </summary>
public sealed class XPrivateFontCollection
{
// This one is global and can only grow. It is not possible to remove fonts that have been added.
/// <summary>
/// Initializes a new instance of the <see cref="XPrivateFontCollection"/> class.
/// </summary>
XPrivateFontCollection()
{
// HACK: Use one global PrivateFontCollection in GDI+
}
#if GDI
//internal PrivateFontCollection PrivateFontCollection
//{
// get { return privateFontCollection; }
// set { privateFontCollection = value; }
//}
GdiPrivateFontCollection GetPrivateFontCollection()
{
// Create only if really needed.
if (_privateFontCollection == null)
_privateFontCollection = new GdiPrivateFontCollection();
return _privateFontCollection;
}
// PrivateFontCollection of GDI+
private GdiPrivateFontCollection _privateFontCollection;
#endif
/// <summary>
/// Gets the global font collection.
/// </summary>
internal static XPrivateFontCollection Singleton
{
get { return _singleton; }
}
internal static XPrivateFontCollection _singleton = new XPrivateFontCollection();
#if GDI
/// <summary>
/// Adds the font data to the font collections.
/// </summary>
[Obsolete("Use Add(Stream stream)")]
public void AddFont(byte[] data, string familyName)
{
if (String.IsNullOrEmpty(familyName))
throw new ArgumentNullException("familyName");
//if (glyphTypeface == null)
// throw new ArgumentNullException("glyphTypeface");
// Add to GDI+ PrivateFontCollection
int length = data.Length;
// Copy data without unsafe code
IntPtr ip = Marshal.AllocCoTaskMem(length);
Marshal.Copy(data, 0, ip, length);
GetPrivateFontCollection().AddMemoryFont(ip, length);
// Do not free the memory here, AddMemoryFont stores a pointer, not a copy!
//Marshal.FreeCoTaskMem(ip);
//privateFonts.Add(glyphTypeface);
}
#endif
/// <summary>
/// Adds the specified font data to the global PrivateFontCollection.
/// Family name and style are automatically retrieved from the font.
/// </summary>
#if GDI
[Obsolete("Use Add(Stream stream)")]
#else
[Obsolete("Use the GDI build of PDFsharp and use Add(Stream stream)")]
#endif
public static void AddFont(string filename)
{
throw new NotImplementedException();
//XGlyphTypeface glyphTypeface = new XGlyphTypeface(filename);
//Global.AddGlyphTypeface(glyphTypeface);
}
#if GDI
/// <summary>
/// Adds the specified font data to the global PrivateFontCollection.
/// Family name and style are automatically retrieved from the font.
/// </summary>
[Obsolete("Use Add(stream).")]
public static void AddFont(Stream stream)
{
Add(stream);
}
/// <summary>
/// Adds the specified font data to the global PrivateFontCollection.
/// Family name and style are automatically retrieved from the font.
/// </summary>
public static void Add(Stream stream)
{
int length = (int)stream.Length;
byte[] bytes = new byte[length];
stream.Read(bytes, 0, length);
Add(bytes);
}
/// <summary>
/// Adds the specified font data to the global PrivateFontCollection.
/// Family name and style are automatically retrieved from the font.
/// </summary>
public static void Add(byte[] font)
{
IntPtr unmanagedPointer = Marshal.AllocCoTaskMem(font.Length);
Marshal.Copy(font, 0, unmanagedPointer, font.Length);
Singleton.GetPrivateFontCollection().AddMemoryFont(unmanagedPointer, font.Length);
// Do not free the memory here, AddMemoryFont stores a pointer, not a copy!
//Marshal.FreeCoTaskMem(ip);
XFontSource fontSource = XFontSource.GetOrCreateFrom(font);
string familyName = fontSource.FontName;
if (familyName.EndsWith(" Regular", StringComparison.OrdinalIgnoreCase))
familyName = familyName.Substring(0, familyName.Length - 8);
bool bold = fontSource.Fontface.os2.IsBold;
bool italic = fontSource.Fontface.os2.IsItalic;
IncompetentlyMakeAHackToFixAProblemYouWoldNeverHaveIfYouUseAFontResolver(fontSource, ref familyName, ref bold, ref italic);
string key = MakeKey(familyName, bold, italic);
Singleton._fontSources.Add(key, fontSource);
string typefaceKey = XGlyphTypeface.ComputeKey(familyName, bold, italic);
FontFactory.CacheExistingFontSourceWithNewTypefaceKey(typefaceKey, fontSource);
}
static void IncompetentlyMakeAHackToFixAProblemYouWoldNeverHaveIfYouUseAFontResolver(XFontSource fontSource,
ref string familyName, ref bool bold, ref bool italic)
{
const string regularSuffix = " Regular";
const string boldSuffix = " Bold";
const string italicSuffix = " Italic";
const string boldItalicSuffix = " Bold Italic";
const string italicBoldSuffix = " Italic Bold";
if (familyName.EndsWith(regularSuffix, StringComparison.OrdinalIgnoreCase))
{
familyName = familyName.Substring(0, familyName.Length - regularSuffix.Length);
Debug.Assert(!bold && !italic);
bold = italic = false;
}
else if (familyName.EndsWith(boldItalicSuffix, StringComparison.OrdinalIgnoreCase) || familyName.EndsWith(italicBoldSuffix, StringComparison.OrdinalIgnoreCase))
{
familyName = familyName.Substring(0, familyName.Length - boldItalicSuffix.Length);
Debug.Assert(bold && italic);
bold = italic = true;
}
else if (familyName.EndsWith(boldSuffix, StringComparison.OrdinalIgnoreCase))
{
familyName = familyName.Substring(0, familyName.Length - boldSuffix.Length);
Debug.Assert(bold && !italic);
bold = true;
italic = false;
}
else if (familyName.EndsWith(italicSuffix, StringComparison.OrdinalIgnoreCase))
{
familyName = familyName.Substring(0, familyName.Length - italicSuffix.Length);
Debug.Assert(!bold && italic);
bold = false;
italic = true;
}
else
{
Debug.Assert(!bold && !italic);
bold = false;
italic = false;
}
}
#endif
/// <summary>
/// Adds the specified font data to the global PrivateFontCollection.
/// Family name and style are automatically retrieved from the font.
/// </summary>
#if GDI
[Obsolete("Use Add(Stream stream)")]
#else
[Obsolete("Use the GDI build of PDFsharp and use Add(Stream stream)")]
#endif
public static void AddFont(Stream stream, string facename)
{
throw new NotImplementedException();
//XGlyphTypeface glyphTypeface = new XGlyphTypeface(stream, facename);
//Global.AddGlyphTypeface(glyphTypeface);
}
// /// <summary>
// /// Adds XGlyphTypeface to internal collection.
// /// Family name and style are automatically retrieved from the font.
// /// </summary>
// void AddGlyphTypeface(XGlyphTypeface glyphTypeface)
// {
// string name = MakeName(glyphTypeface);
// if (_typefaces.ContainsKey(name))
// throw new InvalidOperationException(PSSR.FontAlreadyAdded(glyphTypeface.DisplayName));
// _typefaces.Add(name, glyphTypeface);
// //Debug.WriteLine("Font added: " + name);
//#if GDI
// // Add to GDI+ PrivateFontCollection singleton.
// byte[] data = glyphTypeface.Fontface.FontSource.Bytes;
// int length = data.Length;
// IntPtr ip = Marshal.AllocCoTaskMem(length);
// Marshal.Copy(data, 0, ip, length);
// _privateFontCollection.AddMemoryFont(ip, length);
// // Do not free the memory here, AddMemoryFont stores a pointer, not a copy!
// // Marshal.FreeCoTaskMem(ip);
//#endif
//#if WPF
//#endif
// }
#if WPF
/// <summary>
/// Initializes a new instance of the FontFamily class from the specified font family name and an optional base uniform resource identifier (URI) value.
/// Sample: Add(new Uri("pack://application:,,,/"), "./myFonts/#FontFamilyName");)
/// </summary>
/// <param name="baseUri">Specifies the base URI that is used to resolve familyName.</param>
/// <param name="familyName">The family name or names that comprise the new FontFamily. Multiple family names should be separated by commas.</param>
public static void Add(Uri baseUri, string familyName)
{
Uri uri = new Uri("pack://application:,,,/");
// TODO: What means 'Multiple family names should be separated by commas.'?
// does not work
if (String.IsNullOrEmpty(familyName))
throw new ArgumentNullException("familyName");
if (familyName.Contains(","))
throw new NotImplementedException("Only one family name is supported.");
// Family name starts right of '#'.
int idxHash = familyName.IndexOf('#');
if (idxHash < 0)
throw new ArgumentException("Family name must contain a '#'. Example './#MyFontFamilyName'", "familyName");
string key = familyName.Substring(idxHash + 1);
if (String.IsNullOrEmpty(key))
throw new ArgumentException("familyName has invalid format.");
if (Singleton._fontFamilies.ContainsKey(key))
throw new ArgumentException("An entry with the specified family name already exists.");
#if !SILVERLIGHT
#if DEBUG_
foreach (WpfFontFamily fontFamily1 in WpfFonts.GetFontFamilies(baseUri, familyName))
{
ICollection<WpfTypeface> wpfTypefaces = fontFamily1.GetTypefaces();
wpfTypefaces.GetType();
}
#endif
// Create WPF font family.
WpfFontFamily fontFamily = new WpfFontFamily(baseUri, familyName);
//System.Windows.Media.FontFamily x;
// Required for new Uri("pack://application:,,,/")
// ReSharper disable once ObjectCreationAsStatement
// new System.Windows.Application();
#else
System.Windows.Media.FontFamily fontFamily = new System.Windows.Media.FontFamily(familyName);
#endif
// Check whether font data really exists
#if DEBUG && !SILVERLIGHT
ICollection<WpfTypeface> list = fontFamily.GetTypefaces();
foreach (WpfTypeface typeFace in list)
{
Debug.WriteLine(String.Format("{0}, {1}, {2}, {3}, {4}", familyName, typeFace.FaceNames[FontHelper.XmlLanguageEnUs], typeFace.Style, typeFace.Weight, typeFace.Stretch));
WpfGlyphTypeface glyphTypeface;
if (!typeFace.TryGetGlyphTypeface(out glyphTypeface))
{
Debug.WriteLine(" Glyph typeface does not exists.");
//throw new ArgumentException("Font with the specified family name does not exist.");
}
}
#endif
Singleton._fontFamilies.Add(key, fontFamily);
}
#endif
//internal static XGlyphTypeface TryGetXGlyphTypeface(string familyName, XFontStyle style)
//{
// string name = MakeName(familyName, style);
// XGlyphTypeface typeface;
// _global._typefaces.TryGetValue(name, out typeface);
// return typeface;
//}
#if GDI
internal static GdiFont TryCreateFont(string name, double size, GdiFontStyle style, out XFontSource fontSource)
{
fontSource = null;
try
{
GdiPrivateFontCollection pfc = Singleton._privateFontCollection;
if (pfc == null)
return null;
#if true
string key = MakeKey(name, (XFontStyle)style);
if (Singleton._fontSources.TryGetValue(key, out fontSource))
{
GdiFont font = new GdiFont(name, (float)size, style, GraphicsUnit.World);
#if DEBUG_
Debug.Assert(StringComparer.OrdinalIgnoreCase.Compare(name, font.Name) == 0);
Debug.Assert(font.Bold == ((style & GdiFontStyle.Bold) != 0));
Debug.Assert(font.Italic == ((style & GdiFontStyle.Italic) != 0));
#endif
return font;
}
return null;
#else
foreach (GdiFontFamily family in pfc.Families)
{
if (string.Compare(family.Name, name, StringComparison.OrdinalIgnoreCase) == 0)
{
GdiFont font = new GdiFont(family, (float)size, style, GraphicsUnit.World);
if (string.Compare(font.Name, name, StringComparison.OrdinalIgnoreCase) != 0)
{
// Style simulation is not implemented in GDI+.
// Use WPF build.
}
return font;
}
}
#endif
}
catch (Exception ex)
{
// Ignore exception and return null.
Debug.WriteLine(ex.ToString());
}
return null;
}
#endif
#if WPF && !SILVERLIGHT
internal static WpfTypeface TryCreateTypeface(string name, XFontStyle style, out WpfFontFamily fontFamily)
{
if (Singleton._fontFamilies.TryGetValue(name, out fontFamily))
{
WpfTypeface typeface = FontHelper.CreateTypeface(fontFamily, style);
return typeface;
}
return null;
}
#endif
static string MakeKey(string familyName, XFontStyle style)
{
return MakeKey(familyName, (style & XFontStyle.Bold) != 0, (style & XFontStyle.Italic) != 0);
}
static string MakeKey(string familyName, bool bold, bool italic)
{
return familyName + "#" + (bold ? "b" : "") + (italic ? "i" : "");
}
readonly Dictionary<string, XGlyphTypeface> _typefaces = new Dictionary<string, XGlyphTypeface>();
#if GDI
//List<XGlyphTypeface> privateFonts = new List<XGlyphTypeface>();
readonly Dictionary<string, XFontSource> _fontSources = new Dictionary<string, XFontSource>(StringComparer.OrdinalIgnoreCase);
#endif
#if WPF
readonly Dictionary<string, WpfFontFamily> _fontFamilies = new Dictionary<string, WpfFontFamily>(StringComparer.OrdinalIgnoreCase);
#endif
}
#endif
}