442 lines
18 KiB
C#
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
|
|
}
|