Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / MS / Internal / FontCache / FamilyCollection.cs / 1 / FamilyCollection.cs
//----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Description: FamilyCollection font cache element class is responsible for
// storing the mapping between a folder and font families in it.
//
// History:
// 07/23/2003 : [....] - Big rewrite to change cache structure.
// 03/04/2004 : [....] - Cache layout and interface changes for font enumeration.
// 11/04/2005 : [....] - Refactoring to support font disambiguation.
//
//---------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Markup; // for XmlLanguage
using System.Windows.Media;
using MS.Win32;
using MS.Utility;
using MS.Internal;
using MS.Internal.FontFace;
using MS.Internal.PresentationCore;
// Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas.
#pragma warning disable 1634, 1691
namespace MS.Internal.FontCache
{
///
/// FamilyCollection font cache element class is responsible for
/// storing the mapping between a folder and font families in it
///
[FriendAccessAllowed]
internal class FamilyCollection : IFontCacheElement
{
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
#region Constructors
///
/// Creates a font family collection cache element from a canonical font family reference.
///
/// Absolute Uri of a folder
/// Indicates folderUri is constructed internally by WPF code
/// and specifies the Windows Fonts folder or a file in the Windows Fonts folder.
///
/// Critical - The ability to control the place fonts are loaded from is critical.
///
/// The folderUri parameter is critical as it may contain privileged information
/// (i.e., the location of Windows Fonts); it is passed to the FontSourceCollection
/// constructor which is declared critical and guarantees not to disclose the URI.
///
/// The isWindowsFonts parameter is critical for set as it is used to make a
/// security decision (i.e., whether to assert read access); it is passed to the
/// FontSourceCollection constructor and also assigned to the _isWindowsFonts
/// field which is declared critical.
///
/// Callers should only specify isWindowsFonts=true if the URI comes from internal
/// WPF code, NOT if it comes from the client. E.g., we want FontFamily="Arial" and
/// FontFamily="arial.ttf#Arial" to work in partial trust, therefore isWindowsFonts
/// is true so we assert read access. But FontFamily="file:///c:/windows/fonts/#Arial"
/// should NOT work in partial trust (even -- or especially -- if the URI is right),
/// as this would enable partial trust clients to guess the location of Windows Fonts
/// through trial and error.
///
[SecurityCritical]
internal FamilyCollection(Uri folderUri, bool isWindowsFonts)
{
_isWindowsFonts = isWindowsFonts;
_fontFolder = new FontSourceCollection(folderUri, isWindowsFonts);
_fontFolderUriString = _fontFolder.GetUriString();
}
///
/// This contructor is for shared cache reconstructing element from a key.
///
/// Key containing folder name.
///
/// Critical - Calls into the critical RetrieveKey method.
///
[SecurityCritical]
internal FamilyCollection(CheckedPointer key)
{
RetrieveKey(key);
Debug.Assert(_fontFolder != null);
}
#endregion Constructors
//------------------------------------------------------
//
// Internal Methods
//
//-----------------------------------------------------
#region Internal methods
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks
/// although functionally it simply does a string compare
/// TreatAsSafe: This code is safe to call functionally it
/// is ok to expose since it only compares two pointers also Probe does the pointer checks
///
[SecurityCritical,SecurityTreatAsSafe]
bool IFontCacheElement.Match(CheckedPointer p)
{
unsafe
{
Layout * l = (Layout *)p.Probe(0, sizeof(Layout));
if (Util.StringSize(_fontFolderUriString) != l->folderUriSize)
return false;
if (_fontFolder.TimeStamp != l->timeStamp)
return false;
if (_isWindowsFonts != l->isDefaultSystemFontsFolder)
return false;
return Util.StringEqualIgnoreCase(p + sizeof(Layout), _fontFolderUriString);
}
}
///
/// Critical: This code reads from the critical member _fontFolderUriString.
/// TreatAsSafe: This code is a part of the internal font cache code that sends miss reports
/// to the service process. The critical value is not exposed via public managed APIs.
///
[SecurityCritical,SecurityTreatAsSafe]
private void StoreKeyInternal(CheckedPointer d, out int realSize)
{
d.WriteBool(_isWindowsFonts);
d += (Layout.OffsetToFolderUriSize - Layout.OffsetToKey);
realSize = (Layout.OffsetToFolderUriSize - Layout.OffsetToKey) +
Util.StringAndLengthCopyToCheckedPointer(d, _fontFolderUriString);
}
public void StoreKey(CheckedPointer d, out int realSize)
{
Debug.Assert(!_fontFolder.IsAppSpecific);
StoreKeyInternal(d, out realSize);
}
///
/// Critical: Calls critical GetUriString method.
///
[SecurityCritical]
public void RetrieveKey(CheckedPointer p)
{
_isWindowsFonts = p.ReadBool();
p += (Layout.OffsetToFolderUriSize - Layout.OffsetToKey);
string folderName = Util.StringAndLengthCopyFromCheckedPointer(p);
_fontFolder = new FontSourceCollection(new Uri(folderName, UriKind.Absolute), _isWindowsFonts);
_fontFolderUriString = _fontFolder.GetUriString();
}
int IFontCacheElement.Type
{
get
{
return 2;
}
}
bool IFontCacheElement.IsAppSpecific
{
get
{
return _fontFolder.IsAppSpecific;
}
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks
/// TreatAsSafe: This code is safe to call functionally it
/// is ok to expose since it only compares two pointers also Probe does the pointer checks
///
[SecurityCritical,SecurityTreatAsSafe]
void IFontCacheElement.GetData(CheckedPointer block, ElementCacher cacher)
{
unsafe
{
_data = (Layout *)block.Probe(0, sizeof(Layout));
}
_cacher = cacher;
}
///
/// Critical: Accesses _fontFolderUriString critical field.
/// TreatAsSafe: The field is used only to compute its size.
///
int IFontCacheElement.Size
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe
{
return sizeof(Layout) + Util.StringSize(_fontFolderUriString);
}
}
}
int IFontCacheElement.GetHashCode()
{
return HashFn.HashScramble(_fontFolder.GetHashCode());
}
///
/// Critical:This code calls into unsafe code blocks.
/// TreatAsSafe: This code is ok to expose.Also the call to probe is boundary checked.
///
[SecurityCritical,SecurityTreatAsSafe]
void IFontCacheElement.AddToCache(CheckedPointer newPointer, ElementCacher cacher)
{
_cacher = cacher;
Invariant.Assert(!newPointer.IsNull);
unsafe
{
_data = (Layout*)newPointer.Probe(0, sizeof(Layout));
Debug.Assert(_fontFolder.TimeStamp != 0);
_data->timeStamp = _fontFolder.TimeStamp;
int keySize;
StoreKeyInternal(newPointer + Layout.OffsetToKey, out keySize);
// Make sure folderUriSize doesn't get out of sync with OffsetToKey.
// We use Debug.Assert instead of Invariant.Assert because
// this condition will result in an immediate crash on debug builds.
Debug.Assert(_data->folderUriSize == Util.StringSize(_fontFolderUriString));
// This list is needed to pass intermediate family collection from BuildFamilyList to AddFamiliesToCache.
// The keys are of type LocalizedName, the values are of type BaseFamily.
SortedDictionary familyNameList;
// This dictionary contains mappings from strings to cache offsets that point to them.
// The goal is to reduce cache size by having strings stored only once.
// This forces us to save all used strings on the 1st pass (BuildFamilyList),
// so that we have all names in the cache by the time we add individual families there.
// The strings come from 3 different places:
// 1. Family names (both composite and physical).
// 2. Physical face names.
// 3. Composite FontFamilyMap tables.
// PERF: The values in the dictionary are of type 'int', we use 'object' to avoid
// instantiating SortedList template with the value type int.
// We can move from object to int if CLR pre-instantiates SortedList of object, int in its own DLLs.
// We have to use sorted list instead of a regular dictionary because we want to be able to update values
// without reordering the list or creating new elements.
SortedList frequentStrings;
// List of physical and composite family objects.
List familyList;
BuildFamilyList(out familyList, out familyNameList, out frequentStrings);
if (familyList == null)
{
_data->offsetToCachedFamiliesAndNames = _cacher.Alloc(
CachedFamiliesAndNames.GetTotalSize(0, 0));
CachedFamiliesAndNames* cachedFamiliesAndNames = GetCachedFamiliesAndNames();
cachedFamiliesAndNames->numberOfFamilyNames = 0;
cachedFamiliesAndNames->numberOfFontFamilies = 0;
}
else
{
FontDifferentiator.ResolveFaceConflicts(familyList, frequentStrings, this);
_data->offsetToCachedFamiliesAndNames = _cacher.Alloc(
CachedFamiliesAndNames.GetTotalSize(familyList.Count, familyNameList.Count));
CachedFamiliesAndNames* cachedFamiliesAndNames = GetCachedFamiliesAndNames();
cachedFamiliesAndNames->numberOfFamilyNames = familyNameList.Count;
cachedFamiliesAndNames->numberOfFontFamilies = familyList.Count;
Debug.Assert(cachedFamiliesAndNames->numberOfFamilyNames >= cachedFamiliesAndNames->numberOfFontFamilies);
AddFamiliesToCache(
CachedFamiliesAndNames.GetCachedFamilyNames(cachedFamiliesAndNames),
CachedFamiliesAndNames.GetCachedFamilies(cachedFamiliesAndNames),
familyList.Count,
familyNameList,
frequentStrings);
}
}
}
///
/// Critical: This function accesses critical ElementCacher object.
/// TreatAsSafe: It uses the object only to obtain the obsolete flag.
///
internal bool IsObsolete
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
return _cacher.IsObsolete();
}
}
///
/// Critical:This function has unsafe code blocks and returns cached font families. It also accesses cacher.
/// TreatAsSafe: This information is ok to give out
///
[SecurityCritical,SecurityTreatAsSafe]
internal unsafe CachedFontFamily LookupFamily(
string familyName,
ref FontStyle fontStyle,
ref FontWeight fontWeight,
ref FontStretch fontStretch
)
{
if (familyName == null || familyName.Length == 0)
return new CachedFontFamily(this, null, 0);
CachedName * cachedFamilyNames;
int familyNameCount;
GetCachedFamilyNames(out cachedFamilyNames, out familyNameCount);
int familyNameIndex = 0;
int familyOffset = LookupLongestName(
familyName,
ref familyNameIndex,
cachedFamilyNames,
familyNameCount
);
if (familyOffset == Util.nullOffset)
return new CachedFontFamily(this, null, 0);
// The function guarantees that familyNameIndex is valid before calling LookupLongestName,
// and LookupLongestName guarantees that familyNameIndex is either within the string or at the end of it
// by tracking searchNameIndex and longestPartialMatch variables.
// This is a performance sensitive code path, and we perform redundant checks only in the debug version.
Debug.Assert(0 <= familyNameIndex && familyNameIndex <= familyName.Length);
CheckedPointer mapping = _cacher.Mapping;
CachedFamily* family = (CachedFamily*)mapping.Probe(familyOffset, sizeof(CachedFamily));
if (familyNameIndex == familyName.Length)
return new CachedFontFamily(this, family, mapping.Size - familyOffset);
// see if the input string contains style information
// The face name must be separated from family name by a single space.
// We set the same requirement as GDI.
if (familyName[familyNameIndex] == ' ' && family->familyType == FamilyCollection.FamilyType.Physical)
{
++familyNameIndex;
CachedPhysicalFamily* cachedPhysicalFamily = (CachedPhysicalFamily*)family;
CachedPhysicalFace* cachedFace = LookupFace(
familyName,
ref familyNameIndex,
cachedPhysicalFamily
);
// There should be no trailing characters after the style name.
if (cachedFace != null && familyNameIndex == familyName.Length)
{
fontStyle = cachedFace->style;
fontWeight = cachedFace->weight;
fontStretch = cachedFace->stretch;
return new CachedFontFamily(this, family, mapping.Size - familyOffset);
}
}
return new CachedFontFamily(this, null, 0);
}
///
/// Critical:This code accesses a pointer and passes it to a critical function
///
[SecurityCritical]
internal unsafe string GetFamilyName(CachedFamily * cachedFamily)
{
return new LocalizedNameDictionary(this, cachedFamily).OrdinaryName;
}
private unsafe struct FamilyEnumerator : IEnumerator, IEnumerable
{
private int _familyCount;
///
/// Critical: This code holds reference to a pointer
///
[SecurityCritical]
private unsafe int * _familyPointers;
private FamilyCollection _familyCollection;
private int _currentFamily;
///
/// Critical: This code stores reference to a pointer and accesses _Data which is critical
/// TreatAsSafe: The pointer is not exposed and the member that holds the pointer is critical
///
[SecurityCritical,SecurityTreatAsSafe]
internal FamilyEnumerator(FamilyCollection familyCollection)
{
_familyCollection = familyCollection;
_currentFamily = -1;
unsafe
{
familyCollection.GetCachedFamilies(out _familyPointers, out _familyCount);
}
}
#region IEnumerator Members
public bool MoveNext()
{
++_currentFamily;
if (_currentFamily >= _familyCount)
{
// prevent cycling
_currentFamily = _familyCount;
return false;
}
return true;
}
///
/// Critical: This function uses pointers and calls critical CachedFontFamily constructor
/// TreatAsSafe: This information is ok to expose
///
CachedFontFamily IEnumerator.Current
{
[SecurityCritical,SecurityTreatAsSafe]
get
{
// Disable "Property get methods should not throw exceptions" because IEnumerator<>.Current semantics
// are to throw an exception if enumerator is positioned before the first or after the last element.
#pragma warning disable 6503
if (_currentFamily < 0 || _currentFamily >= _familyCount)
{
throw new InvalidOperationException();
}
CheckedPointer familyPointer = _familyCollection._cacher.Mapping + _familyPointers[_currentFamily];
unsafe
{
return new CachedFontFamily(
_familyCollection,
(CachedFamily*)familyPointer.Probe(0, sizeof(CachedFamily)),
familyPointer.Size
);
}
#pragma warning restore 6503
}
}
#endregion
#region IEnumerator Members
object IEnumerator.Current
{
get
{
return ((IEnumerator)this).Current;
}
}
public void Reset()
{
_currentFamily = -1;
}
#endregion
#region IDisposable Members
public void Dispose() {}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return this as IEnumerator;
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)this).GetEnumerator();
}
#endregion
}
internal IEnumerable GetFontFamilies()
{
return new FamilyEnumerator(this);
}
///
/// Critical: This function has an unsafe code block.
/// SecurityTreatAsSafe: This function only returns the number of font families in a folder.
///
internal int FamilyCount
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe
{
return GetCachedFamiliesAndNames()->numberOfFontFamilies;
}
}
}
///
/// Critical:This function has unsafe code blocks and returns cached font face. It also accesses cacher.
/// It returns a pointer
///
[SecurityCritical]
private unsafe CachedPhysicalFace * LookupFace(
string styleName,
ref int styleNameIndex,
CachedPhysicalFamily * cachedPhysicalFamily
)
{
Debug.Assert(cachedPhysicalFamily->familyType == FamilyType.Physical);
int faceOffset = LookupLongestName(
styleName,
ref styleNameIndex,
(CachedName *)((byte *)cachedPhysicalFamily + sizeof(CachedPhysicalFamily)),
cachedPhysicalFamily->numberOfFaceNames
);
if (faceOffset == Util.nullOffset)
return null;
CachedPhysicalFace * face = (CachedPhysicalFace *)_cacher.Mapping.Probe(faceOffset,sizeof(CachedPhysicalFace));
return face;
}
///
/// Critical: This accesses a pointer and is unsafe because it returns a URI
///
[SecurityCritical]
internal unsafe Uri GetFontUri(CachedPhysicalFace * cachedFace)
{
string fontUri = Util.StringCopyFromUncheckedPointer(
(byte*)cachedFace + sizeof(CachedPhysicalFace),
cachedFace->fontUriSize
);
return Util.CombineUriWithFaceIndex(fontUri, cachedFace->faceIndex);
}
///
/// Critical:This function has unsafe code blocks and returns cached font face. It also accesses cacher.
/// TreatAsSafe: This infomation is ok to give out.
/// CachedFontFamily cannot be created with Null family. Which ensures validity of the pointer.
/// Taking that into account there is also the risk of dereferencing from elementcacher which is mitigated
/// by bounds checking in element cacher
///
[SecurityCritical,SecurityTreatAsSafe]
internal unsafe CachedFontFace GetCachedFace(CachedFontFamily cachedFamily, int faceIndex)
{
Invariant.Assert(faceIndex < cachedFamily.NumberOfFaces);
if (cachedFamily.IsPhysical)
{
CachedPhysicalFamily * cachedPhysicalFamily = cachedFamily.PhysicalFamily;
int * facePointers = (int *)((byte *)cachedPhysicalFamily +
sizeof(CachedPhysicalFamily) +
cachedPhysicalFamily->numberOfFaceNames * sizeof(CachedName)
);
CheckedPointer facePointer = _cacher.Mapping + facePointers[faceIndex];
return new CachedFontFace(
this,
(CachedFace*)facePointer.Probe(0, sizeof(CachedFace)),
facePointer.Size
);
}
Debug.Assert(cachedFamily.IsComposite);
CachedCompositeFamily * cachedCompositeFamily = cachedFamily.CompositeFamily;
int faceOffset = cachedCompositeFamily->OffsetToFamilyTypeface + (faceIndex * sizeof(CachedCompositeFace));
CheckedPointer cachedFace = cachedFamily.CheckedPointer + faceOffset;
return new CachedFontFace(
this,
(CachedFace*)cachedFace.Probe(0, sizeof(CachedFace)),
cachedFace.Size
);
}
///
/// Critical:This function has unsafe code blocks . It also accesses cacher and returns a pointer.
/// Functionally it looks up the CachedFamilyMap and retrieves the ranges that match the criteria.
/// The risk here is of being called with bogus pointers since there is minimal pointer checking while dereferencing.
/// The risk is substantially mitigated by the bounds checking employed in ElementCacher for the return value.
///
[SecurityCritical]
internal unsafe ushort* GetFamilyMapRanges(CachedCompositeFamily* cachedFamily, CultureInfo culture, out int lengthOfRanges)
{
CachedFamilyMapRangeList* rangeList = (CachedFamilyMapRangeList*)((byte*)cachedFamily + cachedFamily->OffsetToFamilyMapRangeLists);
CachedFamilyMapRangeList* defaultList = rangeList + (cachedFamily->numberOfFamilyMapRangeLists - 1);
// Look for the first range list with a matching culture, according to the usual matching rules.
// The lists are sorted such that the most specific cultures come first.
for (; rangeList != defaultList; ++rangeList)
{
XmlLanguage familyMapLanguage = GetFamilyMapLanguageFromOffset(rangeList->offsetToIetfLanguageTag);
if (familyMapLanguage.RangeIncludes(culture))
break;
}
// We either found a matching culture or rangeList == defaultList.
lengthOfRanges = rangeList->lengthOfRanges;
return (ushort*)((byte*)rangeList + rangeList->offsetToRanges);
}
///
/// Critical:This function has unsafe code blocks and returns a pointer
/// Functionally it returns that family map for a character. The risk is
/// in dereferencing the ranges and CachedFamilyPointers since they could be
/// bogus. The calls lower down to InRange are inherently safe.
///
[SecurityCritical]
internal unsafe CachedFamilyMap* GetFamilyMapOfChar(
CachedCompositeFamily* cachedFamily,
ushort* ranges,
int lengthOfRanges,
int ch)
{
CachedFamilyMap* familyMaps = (CachedFamilyMap*)((byte*)cachedFamily + cachedFamily->OffsetToFamilyMaps);
for (int i = 0; i < lengthOfRanges; i += 2)
{
int begin = ranges[i];
int end = ranges[i + 1];
for (int j = begin; j < end; ++j)
{
if (InRange(familyMaps + j, ch))
return familyMaps + j;
}
}
return null;
}
///
/// Critical:This code accesses a pointer and passes it to a critical function
///
[SecurityCritical]
internal unsafe IDictionary GetLocalizedNameDictionary(CachedFamily* cachedFamily)
{
return new LocalizedNameDictionary(this, cachedFamily);
}
#endregion Internal methods
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
///
/// Critical - as this calls FontSource.GetStream which can read files in Windows
/// fonts directory under an elevation.
/// Safe - as this just adds the data to the font cache.
///
[SecurityCritical, SecurityTreatAsSafe]
private void AddCompositeFamilyToList(FontSource fontSource, List familyList, SortedDictionary familyNameList, SortedList frequentStrings)
{
CompositeFontInfo fontInfo;
using (Stream fileStream = fontSource.GetStream())
{
fontInfo = CompositeFontParser.LoadXml(fileStream);
}
foreach (KeyValuePair familyEntry in fontInfo.FamilyNames)
{
LocalizedName familyName = new LocalizedName(familyEntry.Key, familyEntry.Value);
// If there is already a font with the same familyName, ignore the subsequent ones.
if (familyNameList.ContainsKey(familyName))
return;
}
CompositeFamily compositeFamily = new CompositeFamily();
familyList.Add(compositeFamily);
compositeFamily.compositeFontInfo = fontInfo;
// add family names to the list
foreach (KeyValuePair familyEntry in fontInfo.FamilyNames)
{
LocalizedName familyName = new LocalizedName(familyEntry.Key, familyEntry.Value);
familyNameList.Add(familyName, compositeFamily);
SaveLocalizedString(familyName, frequentStrings);
compositeFamily.familyNames.Add(familyName, null);
}
// add cmap table cultures to the culture list; FamilyMapLanguages may be
// null if all the family maps are culture-independent
if (fontInfo.FamilyMapLanguages != null)
{
foreach (XmlLanguage familyMapLanguage in fontInfo.FamilyMapLanguages)
{
SaveRegularString(familyMapLanguage.IetfLanguageTag, frequentStrings);
}
}
}
internal void SaveLocalizedString(LocalizedName name, SortedList frequentStrings)
{
SaveRegularString(name.Language.IetfLanguageTag, frequentStrings);
SaveRegularString(name.Name, frequentStrings);
}
private void SaveRegularString(string name, SortedList frequentStrings)
{
frequentStrings[name] = Util.nullOffset;
}
///
/// Critical - as this obtains UnmanagedMemoryStream from FontSource which is critical data.
/// Safe - as this just adds the data to the font cache.
///
[SecurityCritical,SecurityTreatAsSafe]
private void AddPhysicalFamilyToList(FontSource fontSource, List familyList, SortedDictionary familyNameList, SortedList frequentStrings)
{
// get all the faces out of this file and add them to the list
TrueTypeFontDriver.BasicFontFaceInfo basicFontFaceInfo = new TrueTypeFontDriver.BasicFontFaceInfo();
UnmanagedMemoryStream pinnedFontSource = fontSource.GetUnmanagedStream();
try
{
TrueTypeFontDriver ttd = new TrueTypeFontDriver(pinnedFontSource, fontSource.Uri);
for (int currentFace = 0; currentFace < ttd.NumFaces; ++currentFace)
{
ttd.SetFace(currentFace);
// figure out font family and style
bool skipFontDifferentiation;
ttd.GetBasicFontFaceInfo(ref basicFontFaceInfo, out skipFontDifferentiation);
// Name and style adjustment.
if (!skipFontDifferentiation)
{
FontDifferentiator.AdjustFamilyAndFaceInformation(
ref basicFontFaceInfo.nameTable,
ref basicFontFaceInfo.style,
ref basicFontFaceInfo.weight,
ref basicFontFaceInfo.stretch
);
}
LocalizedName[] familyNames = basicFontFaceInfo.nameTable.familyNames;
LocalizedName[] faceNames = basicFontFaceInfo.nameTable.faceNames;
if (familyNames == null)
{
// If a font doesn't have preferred family names,
// per OpenType spec we have to use legacy family names.
familyNames = basicFontFaceInfo.nameTable.win32FamilyNames;
if (familyNames == null)
{
// A valid font should contain at least one family name.
return;
}
}
if (faceNames == null)
{
// If a font doesn't have preferred face names,
// per OpenType spec we have to use legacy face names.
faceNames = basicFontFaceInfo.nameTable.win32faceNames;
if (faceNames == null)
{
// A valid font should contain at least one face name.
return;
}
}
// See if other faces from this font family were already present.
// If there was a physical face from this family, PhysicalFamily object
// should have already been created, and we can use it instead of creating a new one.
PhysicalFamily family = null;
{
// If there were any composite fonts using the same family name,
// remove such composite font families from the cache altogether,
// as we prefer physical to composite font families in case both are present.
List compositeFamiliesToRemove = null;
foreach (LocalizedName familyName in familyNames)
{
BaseFamily baseFamily;
if (familyNameList.TryGetValue(familyName, out baseFamily))
{
if (baseFamily is PhysicalFamily)
{
if (family == null)
family = (PhysicalFamily)baseFamily;
}
else
{
Debug.Assert(baseFamily is CompositeFamily);
CompositeFamily compositeFamily = (CompositeFamily)baseFamily;
if (compositeFamiliesToRemove == null)
compositeFamiliesToRemove = new List();
else
{
// Make sure we count each composite family only once.
foreach (CompositeFamily olderFamily in compositeFamiliesToRemove)
{
if (Object.ReferenceEquals(compositeFamily, olderFamily))
continue;
}
}
compositeFamiliesToRemove.Add(compositeFamily);
}
}
}
// Remove composite families that had the same family name.
if (compositeFamiliesToRemove != null)
{
foreach (CompositeFamily compositeFamily in compositeFamiliesToRemove)
{
foreach (LocalizedName compositeFamilyName in compositeFamily.familyNames.Keys)
{
familyNameList.Remove(compositeFamilyName);
}
bool removed = familyList.Remove(compositeFamily);
Debug.Assert(removed);
}
}
}
// this is the first time we encountered this family
// create a new object to hold it
if (family == null)
{
family = new PhysicalFamily();
familyList.Add(family);
}
// add new family names (if any) to the list
foreach (LocalizedName familyName in familyNames)
{
familyNameList[familyName] = family;
SaveLocalizedString(familyName, frequentStrings);
family.familyNames[familyName] = null;
}
// now, add a new face to the family
PhysicalFace face = new PhysicalFace();
face.fontUri = fontSource.GetUriString();
face.faceIndex = basicFontFaceInfo.faceIndex;
// The original face is not simulated, we'll add simulated faces if necessary after analyzing all faces in this family.
face.styleSimulations = StyleSimulations.None;
face.style = basicFontFaceInfo.style;
face.weight = basicFontFaceInfo.weight;
face.stretch = basicFontFaceInfo.stretch;
face.version = basicFontFaceInfo.nameTable.version;
face.timestamp = fontSource.GetLastWriteTimeUtc();
face.symbol = basicFontFaceInfo.symbol;
face.designEmHeight = basicFontFaceInfo.designEmHeight;
face.designCellAscent = basicFontFaceInfo.designCellAscent;
face.designCellDescent = basicFontFaceInfo.designCellDescent;
face.designLineSpacing = basicFontFaceInfo.designLineSpacing;
face.names = faceNames;
family.typefaces.Add(face);
}
}
catch (SEHException e)
{
throw Util.ConvertInPageException(fontSource, e);
}
finally
{
pinnedFontSource.Close();
}
}
///
/// Critical - as this call GetFiles which can read files in Windows
/// fonts directory under an elevation.
/// Safe - as this doesn't expose this information. This only works with
/// font files in the windows fonts directory and that information
/// is considered safe to expose.
///
[SecurityCritical, SecurityTreatAsSafe]
private void BuildFamilyList(
out List familyList,
out SortedDictionary familyNameList,
out SortedList frequentStrings)
{
familyNameList = null;
frequentStrings = null;
familyList = null;
foreach (FontSource fontSource in _fontFolder)
{
// verify that we're dealing with a supported font extension
string extension = Util.GetUriExtension(fontSource.Uri);
bool isComposite;
if (!Util.IsSupportedFontExtension(extension, out isComposite))
continue;
// PERF: We create these data structures on demand because application specific font collection is often empty.
if (familyNameList == null)
{
familyNameList = new SortedDictionary(LocalizedName.NameComparer);
frequentStrings = new SortedList(StringComparer.OrdinalIgnoreCase);
familyList = new List();
}
// Parse the file and add data from it to the family list.
// Note that for both physical and composite fonts
// we eat certain exceptions in order to ignore malformed fonts.
// Disable corresponding Presharp warning.
#pragma warning disable 6502
try
{
if (isComposite)
{
AddCompositeFamilyToList(fontSource, familyList, familyNameList, frequentStrings);
}
else
{
AddPhysicalFamilyToList(fontSource, familyList, familyNameList, frequentStrings);
}
}
// Skip malformed fonts for both local and shared cases.
catch (FileFormatException)
{
}
// Skip inaccessible fonts for local cache, re-throw for shared cache to make sure incomplete information
// is not shared to the clients.
catch (IOException)
{
if (_cacher.IsShared)
throw;
}
catch (UnauthorizedAccessException)
{
if (_cacher.IsShared)
throw;
}
catch (System.Net.WebException)
{
if (_cacher.IsShared)
throw;
}
#pragma warning restore 6502
}
}
///
/// Critical:This function has unsafe code blocks ,it accesses element cacher and returns
/// the next character in cache
///
[SecurityCritical]
private unsafe char GetCachedChar(
CachedName* cachedName,
int searchNameIndex)
{
byte* name = _cacher[cachedName->offsetToName];
int nameLength = *(int*)name;
if (nameLength <= searchNameIndex * sizeof(char))
return '\0';
char c = *((char*)(name + sizeof(int)) + searchNameIndex);
return Char.ToUpperInvariant(c);
}
///
/// Critical:This function has unsafe code blocks ,it accesses element cacher and returns
/// the next character in cache
///
[SecurityCritical]
private unsafe bool LookupNextCharacter(
string searchName,
int initialSearchNameIndex,
int searchNameIndex,
CachedName* cachedNames,
ref int startIndex,
ref int endIndex
)
{
char s = Char.ToUpperInvariant(searchName[searchNameIndex]);
for (; ; )
{
// we only have 1 or zero matches left
if (startIndex + 1 >= endIndex)
return false;
int median = (startIndex + endIndex) / 2;
char d = GetCachedChar(cachedNames + median, searchNameIndex - initialSearchNameIndex);
int res = s - d;
if (res < 0)
{
endIndex = median;
}
else if (res > 0)
{
startIndex = median + 1;
}
else
{
// we found a match, let's see if there are other matches
int i;
// move up to find a first non-match
i = median;
while (--i >= startIndex)
{
char c = GetCachedChar(cachedNames + i, searchNameIndex - initialSearchNameIndex);
Debug.Assert(c <= s);
if (c < s)
break;
}
startIndex = i + 1;
// move down to find a first non-match
i = median;
while (++i < endIndex)
{
char c = GetCachedChar(cachedNames + i, searchNameIndex - initialSearchNameIndex);
Debug.Assert(c >= s);
if (c > s)
break;
}
endIndex = i;
return true;
}
}
}
///
/// This function performs incremental binary search until we reach no more than one match.
///
///
/// The index of the current character in the search string.
///
///
/// Offset to the data found.
///
/// Critical:This function has unsafe code blocks ,it accesses element cacher
/// also it takes a pointer that it passes on without validating
///
[SecurityCritical]
private unsafe int LookupLongestName(
string searchName,
ref int searchNameIndex,
CachedName* cachedNames,
int numberOfCachedNames
)
{
Invariant.Assert(searchNameIndex >= 0);
// startIndex is inclusive
int startIndex = 0;
// endIndex is exclusive
int endIndex = numberOfCachedNames;
// remember the initial value of searchNameIndex
int initialSearchNameIndex = searchNameIndex;
int longestPartialMatchIndex = searchNameIndex;
int longestPartialMatchOffset = Util.nullOffset;
for (; ; )
{
// we ran out of characters in the search string
if (searchNameIndex == searchName.Length)
break;
if (!LookupNextCharacter(
searchName,
initialSearchNameIndex,
searchNameIndex,
cachedNames,
ref startIndex,
ref endIndex
))
break;
++searchNameIndex;
// see if the first match is null terminated
// in this case we already have a partial match
char nextCharacter = GetCachedChar(cachedNames + startIndex, searchNameIndex - initialSearchNameIndex);
if (nextCharacter == '\0')
{
longestPartialMatchIndex = searchNameIndex;
longestPartialMatchOffset = cachedNames[startIndex].data;
}
}
Debug.Assert(startIndex <= endIndex);
if (endIndex == startIndex)
{
searchNameIndex = longestPartialMatchIndex;
return longestPartialMatchOffset;
}
// only the item with startIndex can be exact match
// to prove it, there are 2 cases here:
// a) searchNameIndex == searchName.Length
// in this case all items are either of the same length as search string
// or longer. The item of the same length will be first in the list.
// b) searchNameIndex < searchName.Length
// in this case no more than 1 match is left
// Note that in both cases we must compare strings
// starting with searchNameIndex
for (; ; )
{
char d = GetCachedChar(cachedNames + startIndex, searchNameIndex - initialSearchNameIndex);
// see if we reached the end of the input string
if (searchNameIndex == searchName.Length)
{
// here we have an exact match only if we reached the end of the cached string as well
if (d == '\0')
return cachedNames[startIndex].data;
// Otherwise, we have no match.
searchNameIndex = longestPartialMatchIndex;
return longestPartialMatchOffset;
}
char s = Char.ToUpperInvariant(searchName[searchNameIndex]);
if (s != d)
{
// See if we reached the end of the cached string (partial match).
if (d == '\0')
return cachedNames[startIndex].data;
// Otherwise, we have no match.
searchNameIndex = longestPartialMatchIndex;
return longestPartialMatchOffset;
}
++searchNameIndex;
}
}
///
/// Critical:This code calls into Alloc and also yields unverifiable code. It also acceses
/// unsafe code to operate on pointers.
/// TreatAsSafe: Adding a physical family to cache is a safe operation.Also it uses primitives that
/// are bounds checked.
///
[SecurityCritical,SecurityTreatAsSafe]
private unsafe int AddPhysicalFamilyToCache(PhysicalFamily physicalFamily, SortedList frequentStrings)
{
int familyCacheOffset = _cacher.Alloc(
checked(
sizeof(CachedPhysicalFamily) +
physicalFamily.faceNames.Count * sizeof(CachedName) +
physicalFamily.typefaces.Count * sizeof(int)
)
);
CachedPhysicalFamily* cachedFamily = (CachedPhysicalFamily*)_cacher.Mapping.Probe(familyCacheOffset, sizeof(CachedPhysicalFamily));
cachedFamily->familyType = FamilyType.Physical;
CachedName* cachedFaceNames = (CachedName*)((byte*)cachedFamily + sizeof(CachedPhysicalFamily));
int* facePointers = (int*)(cachedFaceNames + physicalFamily.faceNames.Count);
cachedFamily->numberOfFaceNames = physicalFamily.faceNames.Count;
cachedFamily->numberOfTypefaces = 0;
bool symbol = false;
// the following 4 variables set up the state for regular face matching
PhysicalFace bestFace = null;
MatchingStyle regular = new MatchingStyle(FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
MatchingStyle bestMatch = new MatchingStyle();
// put all the faces to the cache
int j = 0;
foreach (KeyValuePair pair in physicalFamily.faceNames)
{
CachedName * cachedFaceName = cachedFaceNames + j;
LocalizedName faceName = pair.Key;
PhysicalFace face = pair.Value;
if (face.cacheOffset == Util.nullOffset)
{
int fontUriSize = Util.StringSize(face.fontUri);
face.cacheOffset = _cacher.Alloc(checked(sizeof(CachedPhysicalFace) + fontUriSize));
CheckedPointer cachedFacePointer = _cacher.GetCheckedPointer(face.cacheOffset);
CachedPhysicalFace* cachedFace = (CachedPhysicalFace*)cachedFacePointer.Probe(0, sizeof(CachedPhysicalFace));
cachedFace->fontUriSize = fontUriSize;
cachedFace->faceIndex = face.faceIndex;
cachedFace->style = face.style;
cachedFace->weight = face.weight;
cachedFace->stretch = face.stretch;
cachedFace->styleSimulations = face.styleSimulations;
Util.StringCopyToCheckedPointer(cachedFacePointer + sizeof(CachedPhysicalFace), face.fontUri);
// if any of faces is non-Unicode, the whole family is treated as non-Unicode
if (face.symbol)
symbol = true;
// add the face to the face list
facePointers[cachedFamily->numberOfTypefaces] = face.cacheOffset;
++cachedFamily->numberOfTypefaces;
// see if the face is a better match for regular face
MatchingStyle current = new MatchingStyle(face.style, face.weight, face.stretch);
if (MatchingStyle.IsBetterMatch(
regular,
bestMatch,
ref current
)
|| bestFace == null)
{
bestFace = face;
bestMatch = current;
}
}
// now that we ensured the face is constructed, fill out CachedName
cachedFaceName->offsetToIetfLanguageTag = GetOffsetToIetfLanguageTag(faceName.Language, frequentStrings);
cachedFaceName->offsetToName = GetOffsetToString(faceName.Name, frequentStrings);
cachedFaceName->data = face.cacheOffset;
++j;
}
cachedFamily->symbol = symbol;
// obtain baseline and height from regular face
Debug.Assert(bestFace != null);
Debug.Assert(cachedFamily->numberOfTypefaces <= physicalFamily.typefaces.Count);
double emsPerDesignUnit = 1.0 / bestFace.designEmHeight;
// The ascent is from the top of the cell. Per [....], we want baseline to be relative to the
// top of a logical line (represented by lineSpacing) in which the cell is vertically centered.
// Thus we want half the external leading to be above the cell. The external leading is equal
// to (lineSpacing - (ascent + descent)), giving us the following formula:
//
// baseline = ascent + (lineSpacing - (ascent + descent)) * 0.5
// = ascent + lineSpacing * 0.5 - ascent * 0.5 - descent * 0.5
// = ascent * 0.5 + lineSpacing * 0.5 - descent * 0.5
// = (ascent + lineSpacing - descent) * 0.5
//
cachedFamily->baseline =
(bestFace.designCellAscent + bestFace.designLineSpacing - bestFace.designCellDescent)
* 0.5 * emsPerDesignUnit;
cachedFamily->lineSpacing = bestFace.designLineSpacing * emsPerDesignUnit;
AddFamilyNames((CachedFamily*)cachedFamily, physicalFamily, frequentStrings);
return familyCacheOffset;
}
///
/// Critical:This code calls into Alloc and also yields unverifiable code
/// TreatAsSafe: Adding a composite family to cache is a safe operation.Also it uses primitives that
/// are bounds checked.
///
[SecurityCritical, SecurityTreatAsSafe]
private unsafe int AddCompositeFamilyToCache(CompositeFamily compositeFamily, SortedList frequentStrings)
{
int familyCacheOffset;
CachedCompositeFamily* cachedFamily = new CompositeFamilyCacher().Allocate(
compositeFamily.compositeFontInfo,
this,
frequentStrings,
out familyCacheOffset
);
AddFamilyNames((CachedFamily*)cachedFamily, compositeFamily, frequentStrings);
return familyCacheOffset;
}
///
/// Comparer for sorting XmlLanguages in decreasing order of specificity.
///
private class LanguageComparer : IComparer
{
internal static readonly LanguageComparer Comparer = new LanguageComparer();
public int Compare(object x, object y)
{
int dx = ((XmlLanguage) x).GetSpecificity();
int dy = ((XmlLanguage) y).GetSpecificity();
return
(dx > dy) ? -1 : // dx is more specific -> sort before
(dx < dy) ? 1 : // dx is less specific -> sort after
string.CompareOrdinal(x.ToString(), y.ToString()); // no need for case-insensitivity; only requirement is stability
}
}
///
/// Add a list of family names sorted by culture to a family.
///
/// Cached representation of a font family.
/// Temporary in-memory representation of a font family.
/// String cache structure to optimize cache space.
///
/// Critical:This code calls dereferences pointers without any validation.
///
[SecurityCritical]
private unsafe void AddFamilyNames(CachedFamily* cachedFamily, BaseFamily family, SortedList frequentStrings)
{
cachedFamily->numberOfFamilyNames = family.familyNames.Count;
Invariant.Assert(cachedFamily->numberOfFamilyNames >= 1);
cachedFamily->offsetToFamilyNames = _cacher.Alloc(2 * sizeof(int) * cachedFamily->numberOfFamilyNames);
int* familyNamePointers = (int*)_cacher[cachedFamily->offsetToFamilyNames];
foreach (LocalizedName familyName in family.familyNames.Keys)
{
familyNamePointers[0] = (int)frequentStrings[familyName.Language.IetfLanguageTag];
familyNamePointers[1] = (int)frequentStrings[familyName.Name];
Invariant.Assert(familyNamePointers[0] != Util.nullOffset && familyNamePointers[0] != 0);
Invariant.Assert(familyNamePointers[1] != Util.nullOffset && familyNamePointers[1] != 0);
familyNamePointers += 2;
}
Invariant.Assert(familyNamePointers - (int*)_cacher[cachedFamily->offsetToFamilyNames] == 2 * cachedFamily->numberOfFamilyNames);
}
///
/// Fills in family list and family name list, constructing cache family object as needed.
///
/// Family name list.
/// Family list.
/// Number of elements in the family list.
/// Mapping from family names to family objects.
/// String cache structure to optimize cache space.
///
/// Critical:This code calls into AddPhysicalFamilyToCache and also yields unverifiable code
///
[SecurityCritical]
private unsafe void AddFamiliesToCache(
CachedName* cachedNames,
int* cachedFamilies,
int familyCount,
SortedDictionary familyNameList,
SortedList frequentStrings)
{
int i;
// Store frequently used strings only once, and use offsets to refer to them.
for (i = 0; i < frequentStrings.Count; ++i)
{
string name = frequentStrings.Keys[i];
frequentStrings[name] = StoreString(name);
}
int numberOfConstructedFamilies = 0;
i = 0;
foreach (KeyValuePair pair in familyNameList)
{
CachedName * cachedFamilyName = cachedNames + i;
LocalizedName familyName = pair.Key;
BaseFamily family = pair.Value;
try
{
if (family.cacheOffset == Util.nullOffset)
{
// create family structure in case it doesn't exist
PhysicalFamily physicalFamily = family as PhysicalFamily;
if (physicalFamily != null)
{
family.cacheOffset = AddPhysicalFamilyToCache(physicalFamily, frequentStrings);
}
else
{
family.cacheOffset = AddCompositeFamilyToCache((CompositeFamily)family, frequentStrings);
}
Invariant.Assert(numberOfConstructedFamilies < familyCount);
cachedFamilies[numberOfConstructedFamilies] = family.cacheOffset;
++numberOfConstructedFamilies;
}
// now that we ensured the family is constructed, fill out CachedName
cachedFamilyName->offsetToIetfLanguageTag = GetOffsetToIetfLanguageTag(familyName.Language, frequentStrings);
cachedFamilyName->offsetToName = GetOffsetToString(familyName.Name, frequentStrings);
cachedFamilyName->data = family.cacheOffset;
}
catch (OverflowException)
{
// Extreme numbers in the font 'name' table can cause arithmetic overflows in
// AddPhysicalFamilyToCache.
// We want to ignore such fonts and continue creating entries for well formed fonts.
}
++i;
}
}
///
/// Critical: This code calls into cacher. and results in unverifiable code
/// TreatAsSafe: This code is safe to call since the cacher function is bounds checked.,
///
[SecurityCritical,SecurityTreatAsSafe]
private int StoreString(string name)
{
unsafe
{
int newOffset = _cacher.Alloc(sizeof(int) + Util.StringSize(name));
Util.StringAndLengthCopyToCheckedPointer(_cacher.GetCheckedPointer(newOffset), name);
return newOffset;
}
}
private int GetOffsetToString(string name, SortedList frequentStrings)
{
int offset = (int)frequentStrings[name];
Debug.Assert(offset != Util.nullOffset && offset != 0);
return offset;
}
private int GetOffsetToIetfLanguageTag(XmlLanguage language, SortedList frequentStrings)
{
return GetOffsetToString(language.IetfLanguageTag, frequentStrings);
}
private string GetStringFromOffset(int offsetToName)
{
return Util.StringAndLengthCopyFromCheckedPointer(_cacher.GetCheckedPointer(offsetToName));
}
private XmlLanguage GetFamilyMapLanguageFromOffset(int offsetToIetfLanguageTag)
{
return XmlLanguage.GetLanguage(GetStringFromOffset(offsetToIetfLanguageTag));
}
///
/// Critical: This function acceses _cacher[], it derefernces a pointer without any checking
///
[SecurityCritical]
private unsafe bool InRange(CachedFamilyMap* cmap, int ch)
{
CachedCharacterRange* range = (CachedCharacterRange*)((byte*)cmap + cmap->offsetToRanges);
for (int i = 0; i < cmap->numberOfRanges; i++)
{
// clever code from Word meaning: "ch >= _first && ch <= _last",
// this is done with one test and branch.
if ((uint)(ch - range->first) <= range->delta)
return true;
++range;
}
return false;
}
///
/// Critical: This code calls into unsafe code blocks and exposes a pointer.
///
[SecurityCritical]
private unsafe CachedFamiliesAndNames* GetCachedFamiliesAndNames()
{
return (CachedFamiliesAndNames*)_cacher[_data->offsetToCachedFamiliesAndNames];
}
///
/// Critical: This code calls into unsafe code blocks and exposes a pointer.
///
[SecurityCritical]
private unsafe void GetCachedFamilyNames(out CachedName* cachedFamilyNames, out int familyNameCount)
{
CachedFamiliesAndNames* cachedFamiliesAndNames = GetCachedFamiliesAndNames();
familyNameCount = cachedFamiliesAndNames->numberOfFamilyNames;
cachedFamilyNames = CachedFamiliesAndNames.GetCachedFamilyNames(cachedFamiliesAndNames);
}
///
/// Critical: This code calls into unsafe code blocks and exposes a pointer.
///
[SecurityCritical]
private unsafe void GetCachedFamilies(out int* cachedFamilies, out int familyCount)
{
CachedFamiliesAndNames* cachedFamiliesAndNames = GetCachedFamiliesAndNames();
familyCount = cachedFamiliesAndNames->numberOfFontFamilies;
cachedFamilies = CachedFamiliesAndNames.GetCachedFamilies(cachedFamiliesAndNames);
}
#endregion Private Methods
//-----------------------------------------------------
//
// Private Types
//
//------------------------------------------------------
#region Private Types
///
/// Memory layout: Layout struct | folder name string.
///
[StructLayout(LayoutKind.Explicit, Size = 20)]
internal struct Layout
{
[FieldOffset(0)]
internal long timeStamp;
[FieldOffset(8)]
internal int offsetToCachedFamiliesAndNames;
///
/// isDefaultSystemFontsFolder and folderUriSize should be the last members,
/// as they are stored along with the name string as a key for cache miss reports.
///
[FieldOffset(OffsetToKey)]
internal bool isDefaultSystemFontsFolder;
[FieldOffset(OffsetToFolderUriSize)]
internal int folderUriSize;
internal const int OffsetToKey = 12;
internal const int OffsetToFolderUriSize = 16;
}
///
/// Memory layout: CachedFamiliesAndNames |
/// unsorted array [numberOfFontFamilies] of font family pointers |
/// sorted array [numberOfFamilyNames] of CachedName family entries
/// Unsorted array of font family pointers is used for font family enumeration.
/// Sorted array of font family names is used for font family lookup by name.
/// The structure should be aligned on a 4 byte bounary.
/// This guarantees that arrays are also aligned on 4 byte boundaries.
///
///
/// Critical - as this class contains sizes and offsets which are used to allocate
/// unamanged memory and perform pointer arithmetic; font cache corruption
/// could result if any of the calculations are wrong.
///
/// The entire class is declared critical because *all* of its members are
/// critical, and because declaring the critical members individually would
/// not result in any more transparent code.
///
[SecurityCritical(SecurityCriticalScope.Everything)]
[StructLayout(LayoutKind.Explicit, Size = 8)]
private struct CachedFamiliesAndNames
{
[FieldOffset(0)]
internal int numberOfFontFamilies;
[FieldOffset(4)]
internal int numberOfFamilyNames;
// We cannot use sizeof to assign to a const variable.
public const int OffsetToCachedFamilies = 8;
public unsafe static int* GetCachedFamilies(CachedFamiliesAndNames* This)
{
return (int*)((byte*)This + OffsetToCachedFamilies);
}
public static int GetTotalSize(int familyCount, int familyNameCount)
{
unsafe
{
return GetOffsetToCachedFamilyNames(familyCount) + sizeof(CachedName) * familyNameCount;
}
}
public static int GetOffsetToCachedFamilyNames(int familyCount)
{
unsafe
{
return OffsetToCachedFamilies + sizeof(int) * familyCount;
}
}
public unsafe static CachedName* GetCachedFamilyNames(CachedFamiliesAndNames* This)
{
return (CachedName*)((byte*)This + GetOffsetToCachedFamilyNames(This->numberOfFontFamilies));
}
}
///
/// The structure should be aligned on 4 byte boundary.
///
[StructLayout(LayoutKind.Explicit, Size = 12)]
private struct CachedName
{
///
/// Offset to the name string culture
///
[FieldOffset(0)]
internal int offsetToIetfLanguageTag;
///
/// Offset to the actual name string
///
[FieldOffset(4)]
internal int offsetToName;
///
/// Offset to PhysicalFace or Family
///
[FieldOffset(8)]
internal int data;
};
internal enum FamilyType
{
Composite = 0,
Physical = 1
};
///
/// Base structure for CachedPhysicalFamily and CachedCompositeFamily.
/// Since we cannot use inheritance here, we simply repeat the fields in derivees.
///
[StructLayout(LayoutKind.Explicit, Size = 32)]
internal struct CachedFamily
{
///
/// Composite or physical
///
[FieldOffset(0)]
internal FamilyType familyType;
///
/// Number of typefaces in this family.
///
[FieldOffset(4)]
internal int numberOfTypefaces;
[FieldOffset(8)]
internal double baseline;
[FieldOffset(16)]
internal double lineSpacing;
///
/// Number of localized names for this family.
///
[FieldOffset(24)]
internal int numberOfFamilyNames;
///
/// Pointer to the CachedName[numberOfFamilyNames] array of localized names for this family,
/// sorted by culture info string.
///
[FieldOffset(28)]
internal int offsetToFamilyNames;
};
/// Allocates and initializes a CachedCompositeFamily structure and related structures. The
/// memory layout is as follows.
///
/// 1. CachedCompositeFamily struct
///
/// 2. at CachedCompositeFamily.OffsetToFamilyMaps:
/// array [numberOfFamilyMaps] of CachedFamilyMap in lookup order.
///
/// 3. at CachedCompositeFamily.OffsetToFamilyMapRangeLists:
/// array [numberOfFamilyMapRangeLists] of CachedFamilyMapRangeList in decreasing
/// order of cultural specificity.
///
/// 4. at CachedCompositeFamily.OffsetToFamilyTypeface:
/// array[numberOfTypefaces] of CachedCompositeFace.
///
/// 5. at CachedFamily.OffsetToTargetFamilyNameStrings
/// one or more size-prefixed strings, each with the following layout:
/// int representing the size of the string in bytes
/// char[] containing characters of the string
/// optional padding for integer alignment
/// Note: The first string is always the target of the first FontFamilyMap. Subsequent
/// strings may be in any order. If there are no FamilyMaps then a single string
/// of length zero is added.
///
/// 6. at _offsetToFamilyMapRanges:
/// array[] of CachedCharacterRange.
///
/// 7. at _offsetToRangeListArrays:
/// array[] of ushort, referenced by CachedFamilyMapRangeList.
///
/// 8. at _offsetToCachedDeviceFonts:
/// zero or more of the following:
/// CachedDeviceFont structure
/// at CachedDeviceFont.OffsetToLengthPrefixedName: length prefixed string
/// at CachedDeviceFont.OffsetToCharacterMap: array[] of int
///
/// 9. at _offsetToCharacterMetricsArray:
/// array[] of CachedCharacterMetrics
///
///
/// Critical - as this class contains sizes and offsets which are used to allocate
/// unamanged memory and perform pointer arithmetic; font cache corruption
/// could result if any of the calculations are wrong.
///
/// The entire class is declared critical because *all* of its members are
/// critical, and because declaring the critical members individually would
/// not result in any more transparent code.
///
[SecurityCritical(SecurityCriticalScope.Everything)]
private struct CompositeFamilyCacher
{
private CompositeFontInfo _compositeFontInfo;
private CachedCompositeFamily _cachedFamily;
// Family map information.
// AnalyzeFamilyMaps initializes these fields.
private Hashtable _targetFamilyNamesTable;
private int _sizeOfTargetFamilyNameStrings;
private int _sizeOfFamilyMapRanges;
private string _firstTargetFamilyName;
// Family map language / range lists information.
// AnalyzeFamilyMapLanguages initializes these fields.
private XmlLanguage[] _familyMapLanguages;
private ushort[][] _familyMapRangeLists;
private int _sizeOfRangeListArrays;
// Device font information.
// AnalyzeDeviceFonts initializes these fields.
private Hashtable _characterMetricsTable;
private int _sizeOfCachedDeviceFonts;
private int _sizeOfCharacterMetrics;
// Other offsets, based on the above sizes.
private int _offsetToFamilyMapRanges;
private int _offsetToRangeListArrays;
private int _offsetToCachedDeviceFonts;
private int _offsetToCharacterMetricsArray;
private int _allocationSize;
internal unsafe CachedCompositeFamily* Allocate(
CompositeFontInfo compositeFontInfo,
FamilyCollection collection,
SortedList frequentStrings,
out int familyCacheOffset)
{
AnalyzeCompositeFontInfo(compositeFontInfo);
// allocate memory for the CachedCompositeFamily and subsequent data
familyCacheOffset = collection._cacher.Alloc(_allocationSize);
// create a checked pointer for bounds-checked access using the allocation size
CheckedPointer cachedFamily = collection._cacher.Mapping.CheckedProbe(
familyCacheOffset,
_allocationSize
);
// fill in basic information
CachedCompositeFamily* familyPointer = (CachedCompositeFamily*)(cachedFamily.Probe(0, sizeof(CachedCompositeFamily)));
*familyPointer = _cachedFamily;
// fill in family maps
WriteFamilyMaps(collection, cachedFamily, frequentStrings);
WriteFamilyTypefaces(cachedFamily);
return familyPointer;
}
private void AnalyzeCompositeFontInfo(CompositeFontInfo compositeFontInfo)
{
_compositeFontInfo = compositeFontInfo;
_cachedFamily = new CachedCompositeFamily();
// Partially initialize _cachedFamily.
_cachedFamily.familyType = FamilyType.Composite;
_cachedFamily.baseline = compositeFontInfo.Baseline;
_cachedFamily.lineSpacing = compositeFontInfo.LineSpacing;
// Calculate other sizes and offsets, including some _cachedFamily fields.
AnalyzeFamilyMaps();
AnalyzeFamilyTypefaces();
// We've now initialized _cachedFamily to the point where we can rely on the values
// returned by its OffsetToXXX properties. Calculate additional offsets based on the
// previously computed sizes.
// array of CachedCharacterRange; 8-byte alignment required
_offsetToFamilyMapRanges = Util.Align8(
_cachedFamily.OffsetToTargetFamilyNameStrings + _sizeOfTargetFamilyNameStrings
);
// array of ushort; ushort alignment is sufficient
_offsetToRangeListArrays = _offsetToFamilyMapRanges + _sizeOfFamilyMapRanges;
// zero or more of ( CachedDeviceFont + array of char + array of int )
_offsetToCachedDeviceFonts = Util.Align8(
_offsetToRangeListArrays + _sizeOfRangeListArrays
);
// array of CachedCharacterMetrics; 8-byte alignment is required
_offsetToCharacterMetricsArray = Util.Align8(
_offsetToCachedDeviceFonts + _sizeOfCachedDeviceFonts
);
_allocationSize = Util.Align8(
_offsetToCharacterMetricsArray + _sizeOfCharacterMetrics
);
}
private void AnalyzeFamilyMaps()
{
unsafe
{
FontFamilyMapCollection familyMaps = _compositeFontInfo.FamilyMaps;
if (familyMaps != null && familyMaps.Count != 0)
{
// Remember the number of family maps.
_cachedFamily.numberOfFamilyMaps = _compositeFontInfo.FamilyMaps.Count;
// We're going to create a mapping of target family names to unique indexes
// and also remember the first target family name.
_targetFamilyNamesTable = new Hashtable(_cachedFamily.numberOfFamilyMaps);
_firstTargetFamilyName = familyMaps[0].Target;
foreach (FontFamilyMap map in familyMaps)
{
// Is this the first occurrence of this family name?
if (!_targetFamilyNamesTable.ContainsKey(map.Target))
{
// Associated each unique string with a unique index.
_targetFamilyNamesTable.Add(map.Target, _targetFamilyNamesTable.Count);
// Reserve room for length prefix + characters + any padding.
_sizeOfTargetFamilyNameStrings += GetPrefixedStringSize(map.Target);
}
_sizeOfFamilyMapRanges += map.Ranges.Length * sizeof(CachedCharacterRange);
}
// Analyze the family map cultures and the corresponding range lists.
AnalyzeFamilyMapLanguages();
}
// If there are no target family names we'll add a zero length string so that
// CachedCompositeFamily.OffsetToTargetFamilyNameStrings always points to a
// valid length-prefixed string.
if (_sizeOfTargetFamilyNameStrings == 0)
{
// All we need is the length prefix with value zero.
_sizeOfTargetFamilyNameStrings = GetPrefixedStringSize(String.Empty);
}
}
}
private static int GetPrefixedStringSize(string s)
{
unsafe
{
return Util.Align4(sizeof(int) + Util.StringSize(s));
}
}
private unsafe void WriteFamilyMaps(FamilyCollection collection, CheckedPointer cachedFamily, SortedList frequentStrings)
{
CheckedPointer[] namePointers = null;
CheckedPointer nameStrings = cachedFamily + _cachedFamily.OffsetToTargetFamilyNameStrings;
CheckedPointer nameStringsEnd = nameStrings;
// fill in target family names
if (_targetFamilyNamesTable != null)
{
namePointers = new CheckedPointer[_targetFamilyNamesTable.Count];
// The first target family name must always come first.
namePointers[0] = nameStringsEnd;
Util.StringAndLengthCopyToCheckedPointer(nameStringsEnd, _firstTargetFamilyName);
nameStringsEnd += GetPrefixedStringSize(_firstTargetFamilyName);
// The other target family names can be added in whatever order the
// hashtable enumerates them.
foreach (DictionaryEntry entry in _targetFamilyNamesTable)
{
// We've already added the first target family name so skip this
// entry if nameIndex is zero.
int nameIndex = (int)entry.Value;
if (nameIndex != 0)
{
namePointers[nameIndex] = nameStringsEnd;
string familyName = (string)entry.Key;
Util.StringAndLengthCopyToCheckedPointer(nameStringsEnd, familyName);
nameStringsEnd += GetPrefixedStringSize(familyName);
}
}
}
else
{
// No family names. Store an empty string so that CachedCompositeFamily.OffsetToTargetFamilyNameStrings
// always is the offset of a valid length-prefixed string.
Util.StringAndLengthCopyToCheckedPointer(nameStringsEnd, String.Empty);
nameStringsEnd += GetPrefixedStringSize(String.Empty);
}
// Make sure the calculated size agrees with the actual size.
Invariant.Assert(nameStringsEnd.PointerEquals(nameStrings + _sizeOfTargetFamilyNameStrings));
// fill in the CachedFamilyMap and CachedCharacterRange arrays
if (_cachedFamily.numberOfFamilyMaps != 0)
{
CheckedPointer cachedFamilyMaps = cachedFamily + _cachedFamily.OffsetToFamilyMaps;
CheckedPointer cachedRanges = cachedFamily + _offsetToFamilyMapRanges;
CheckedPointer cachedRangesEnd = cachedRanges;
foreach (FontFamilyMap cmap in _compositeFontInfo.FamilyMaps)
{
CachedFamilyMap* familyMap = (CachedFamilyMap*)cachedFamilyMaps.Probe(0, sizeof(CachedFamilyMap));
familyMap->scaleInEm = cmap.Scale;
CheckedPointer namePointer = namePointers[(int)_targetFamilyNamesTable[cmap.Target]];
familyMap->targetFamilyNameOffset = cachedFamilyMaps.OffsetOf(namePointer);
familyMap->numberOfRanges = cmap.Ranges.Length;
familyMap->offsetToRanges = cachedFamilyMaps.OffsetOf(cachedRangesEnd);
foreach (FontFamilyMap.Range range in cmap.Ranges)
{
CachedCharacterRange* cachedRange = (CachedCharacterRange*)cachedRangesEnd.Probe(0, sizeof(CachedCharacterRange));
cachedRange->first = range.First;
cachedRange->delta = range.Delta;
cachedRangesEnd += sizeof(CachedCharacterRange);
}
cachedFamilyMaps += sizeof(CachedFamilyMap);
}
// Make sure the calculated size agrees with the actual size.
Invariant.Assert(cachedRangesEnd.PointerEquals(cachedRanges + _sizeOfFamilyMapRanges));
// Write the family map cultures and their associated range lists.
WriteFamilyMapLanguages(collection, cachedFamily, frequentStrings);
}
}
private void AnalyzeFamilyMapLanguages()
{
unsafe
{
int languageCount = _compositeFontInfo.FamilyMapLanguages != null ?
_compositeFontInfo.FamilyMapLanguages.Count :
0;
// We create one range list per culture, plus one for "any language", i.e., null.
_cachedFamily.numberOfFamilyMapRangeLists = languageCount + 1;
// Create the array of cultures (including "any") and sort in decreasing order of specificity.
_familyMapLanguages = new XmlLanguage[_cachedFamily.numberOfFamilyMapRangeLists];
if (languageCount > 0)
{
_compositeFontInfo.FamilyMapLanguages.CopyTo(_familyMapLanguages, 0);
Array.Sort(_familyMapLanguages, 0, languageCount, LanguageComparer.Comparer);
}
// Get the range list for each culture (including "any") and calculate their total size.
_familyMapRangeLists = new ushort[_cachedFamily.numberOfFamilyMapRangeLists][];
for (int i = 0; i < _familyMapLanguages.Length; ++i)
{
ushort[] ranges = _compositeFontInfo.GetFamilyMapsOfLanguage(_familyMapLanguages[i]);
_familyMapRangeLists[i] = ranges;
_sizeOfRangeListArrays += (ranges.Length - CompositeFontInfo.FirstFamilyMapRange) * sizeof(ushort);
}
}
}
private unsafe void WriteFamilyMapLanguages(FamilyCollection collection, CheckedPointer cachedFamily, SortedList frequentStrings)
{
// fill in the family map range lists
CheckedPointer rangeLists = cachedFamily + _cachedFamily.OffsetToFamilyMapRangeLists;
CheckedPointer rangeListArrays = cachedFamily + _offsetToRangeListArrays;
CheckedPointer rangeListArraysEnd = rangeListArrays;
for (int i = 0; i < _familyMapLanguages.Length; ++i)
{
XmlLanguage language = _familyMapLanguages[i];
ushort[] ranges = _familyMapRangeLists[i];
CachedFamilyMapRangeList* rangeList = (CachedFamilyMapRangeList*)rangeLists.Probe(0, sizeof(CachedFamilyMapRangeList));
rangeList->offsetToIetfLanguageTag = (language != null) ? collection.GetOffsetToIetfLanguageTag(language, frequentStrings) : 0;
rangeList->lengthOfRanges = ranges.Length - CompositeFontInfo.FirstFamilyMapRange;
rangeList->offsetToRanges = rangeLists.OffsetOf(rangeListArraysEnd);
int arraySizeInBytes = Util.ArrayCopyToCheckedPointer(
rangeListArraysEnd,
ranges,
CompositeFontInfo.FirstFamilyMapRange,
rangeList->lengthOfRanges
);
rangeListArraysEnd += arraySizeInBytes;
rangeLists += sizeof(CachedFamilyMapRangeList);
}
// Make sure the calculated size agrees with the actual size.
Invariant.Assert(rangeListArrays.OffsetOf(rangeListArraysEnd) == _sizeOfRangeListArrays);
}
private void AnalyzeFamilyTypefaces()
{
FamilyTypefaceCollection familyTypefaces = _compositeFontInfo.FamilyTypefaces;
if (familyTypefaces == null)
return;
_cachedFamily.numberOfTypefaces = familyTypefaces.Count;
AnalyzeDeviceFonts();
}
private unsafe void WriteFamilyTypefaces(CheckedPointer cachedFamily)
{
if (_cachedFamily.numberOfTypefaces == 0)
return;
// fill in the CachedCompositeFace array
CachedCompositeFace* cachedFamilyTypeface = (CachedCompositeFace*)cachedFamily.Probe(
_cachedFamily.OffsetToFamilyTypeface,
_cachedFamily.numberOfTypefaces * sizeof(CachedCompositeFace)
);
foreach (FamilyTypeface familyTypeface in _compositeFontInfo.FamilyTypefaces)
{
cachedFamilyTypeface->capsHeight = familyTypeface.CapsHeight;
cachedFamilyTypeface->fontStyle = familyTypeface.Style;
cachedFamilyTypeface->fontWeight = familyTypeface.Weight;
cachedFamilyTypeface->fontStretch = familyTypeface.Stretch;
cachedFamilyTypeface->strikeThroughPosition = familyTypeface.StrikethroughPosition;
cachedFamilyTypeface->strikeThroughThickness = familyTypeface.StrikethroughThickness;
cachedFamilyTypeface->underlinePosition = familyTypeface.UnderlinePosition;
cachedFamilyTypeface->underlineThickness = familyTypeface.UnderlineThickness;
cachedFamilyTypeface->xHeight = familyTypeface.XHeight;
++cachedFamilyTypeface;
}
// fill in device font information
WriteDeviceFonts(cachedFamily);
}
private void AnalyzeDeviceFonts()
{
unsafe
{
for (int i = 0; i < _cachedFamily.numberOfTypefaces; ++i)
{
// Does this FamilyTypeface have device font information?
FamilyTypeface face = _compositeFontInfo.FamilyTypefaces[i];
if (face.DeviceFontName != null && face.DeviceFontCharacterMetrics.Count != 0)
{
// We'll store only one copy of each unique CharacterMetrics so use a hash table
// to map character metrics (the key) to integers (indexes).
if (_characterMetricsTable == null)
{
_characterMetricsTable = new Hashtable(_cachedFamily.numberOfTypefaces);
}
// Determine the number of "pages" of integers after the CachedDeviceFont structure.
CharacterMetricsDictionary metricsDictionary = face.DeviceFontCharacterMetrics;
int pageCount = 0;
for (int j = 0; j < CharacterMetricsDictionary.PageCount; ++j)
{
// Iterate over pages, where j is the page index
CharacterMetrics[] page = metricsDictionary.GetPage(j);
if (page != null)
{
// The page exists so increment the page count
++pageCount;
// Add each unique CharacterMetrics object to the hash table
for (int k = 0; k < page.Length; ++k)
{
if (page[k] != null && !_characterMetricsTable.ContainsKey(page[k]))
{
_characterMetricsTable.Add(page[k], _characterMetricsTable.Count);
}
}
}
}
// Add the total size of this device font including the CachedDeviceFont structure itself
// plus the string and character metrics array.
_sizeOfCachedDeviceFonts += new CachedDeviceFont(face.DeviceFontName, pageCount).SizeOfCachedDeviceFont;
}
}
// Add room for the array of character metrics.
if (_characterMetricsTable != null)
{
_sizeOfCharacterMetrics = _characterMetricsTable.Count * sizeof(CachedCharacterMetrics);
}
}
}
private unsafe void WriteDeviceFonts(CheckedPointer cachedFamily)
{
if (_characterMetricsTable == null)
return;
// fill in the CachedCharacterMetrics array
CheckedPointer cachedCharacterMetricsArray = cachedFamily.CheckedProbe(
_offsetToCharacterMetricsArray,
_characterMetricsTable.Count * sizeof(CachedCharacterMetrics)
);
foreach (DictionaryEntry entry in _characterMetricsTable)
{
CharacterMetrics metrics = (CharacterMetrics)(entry.Key);
CachedCharacterMetrics* cachedMetrics = (CachedCharacterMetrics*)cachedCharacterMetricsArray.Probe(
(int)(entry.Value) * sizeof(CachedCharacterMetrics),
sizeof(CachedCharacterMetrics)
);
cachedMetrics->blackBoxWidth = metrics.BlackBoxWidth;
cachedMetrics->blackBoxHeight = metrics.BlackBoxHeight;
cachedMetrics->baseline = metrics.Baseline;
cachedMetrics->leftSideBearing = metrics.LeftSideBearing;
cachedMetrics->rightSideBearing = metrics.RightSideBearing;
cachedMetrics->topSideBearing = metrics.TopSideBearing;
cachedMetrics->bottomSideBearing = metrics.BottomSideBearing;
}
// iterate over the typefaces to fill in device fonts
CheckedPointer cachedDeviceFonts = cachedFamily + _offsetToCachedDeviceFonts;
CheckedPointer cachedDeviceFontsEnd = cachedDeviceFonts;
CheckedPointer currentFamilyTypeface = cachedFamily + _cachedFamily.OffsetToFamilyTypeface;
foreach (FamilyTypeface familyTypeface in _compositeFontInfo.FamilyTypefaces)
{
// Does this typeface have device font information?
string deviceFontName = familyTypeface.DeviceFontName;
if (deviceFontName != null && familyTypeface.DeviceFontCharacterMetrics.Count != 0)
{
// Initialize the offset from the typeface to the corresponding device font.
CachedCompositeFace* typefacePtr = (CachedCompositeFace*)currentFamilyTypeface.Probe(0, sizeof(CachedCompositeFace));
typefacePtr->offsetToDeviceFont = currentFamilyTypeface.OffsetOf(cachedDeviceFontsEnd);
// Count the non-empty pages in the character metrics dictionary.
CharacterMetricsDictionary metricsDictionary = familyTypeface.DeviceFontCharacterMetrics;
int pageCount = 0;
for (int i = 0; i < CharacterMetricsDictionary.PageCount; ++i)
{
if (metricsDictionary.GetPage(i) != null)
++pageCount;
}
// Point to the device font structure and fill in the page count and name.
CachedDeviceFont* deviceFontPtr = (CachedDeviceFont*)cachedDeviceFontsEnd.Probe(0, sizeof(CachedDeviceFont));
deviceFontPtr->numberOfPages = pageCount;
Util.StringAndLengthCopyToCheckedPointer(
cachedDeviceFontsEnd + CachedDeviceFont.OffsetToLengthPrefixedName,
deviceFontName
);
// Create a checked pointer for accessing the character map; this ensures we won't
// overwrite the memory we reserved for this device font.
int characterMapOffset = deviceFontPtr->OffsetToCharacterMap;
int deviceFontSize = deviceFontPtr->SizeOfCachedDeviceFont;
CheckedPointer characterMap = cachedDeviceFontsEnd.CheckedProbe(
characterMapOffset,
deviceFontSize - characterMapOffset
);
// Point to the page table.
int* cachedPageTable = (int*)characterMap.Probe(0, CharacterMetricsDictionary.PageCount * sizeof(int));
// Integer index of current page from start of page table.
int currentPageIndex = CharacterMetricsDictionary.PageCount;
// Iterate over the pages in the CharacterMetricsDictionary.
for (int i = 0; i < CharacterMetricsDictionary.PageCount; ++i)
{
// Is there a page?
CharacterMetrics[] page = metricsDictionary.GetPage(i);
if (page != null)
{
// Point to the page.
int* cachedPage = (int*)characterMap.Probe(
currentPageIndex * sizeof(int),
CharacterMetricsDictionary.PageSize * sizeof(int)
);
// Set the page table entry to the index of the beginning of the page.
cachedPageTable[i] = currentPageIndex;
// Initialize the page contents.
for (int j = 0; j < CharacterMetricsDictionary.PageSize; ++j)
{
if (page[j] != null)
{
// Use the hash table to find the array index of the CachedCharacterMetrics object.
int metricsIndex = (int)_characterMetricsTable[page[j]];
// Set the page entry to the offset of the CachedCharacterMetrics object.
cachedPage[j] = cachedDeviceFontsEnd.OffsetOf(
cachedCharacterMetricsArray + (metricsIndex * sizeof(CachedCharacterMetrics))
);
}
}
// Update the current page index.
currentPageIndex += CharacterMetricsDictionary.PageSize;
}
}
// Point to where the next CachedDeviceFont will go (if any).
cachedDeviceFontsEnd += deviceFontSize;
}
currentFamilyTypeface += sizeof(CachedCompositeFace);
}
// Make sure the total memory we filled in for all device fonts matches the calculated size.
Invariant.Assert(cachedDeviceFonts.OffsetOf(cachedDeviceFontsEnd) == _sizeOfCachedDeviceFonts);
}
}
/// See comment for struct CompositeFamilyCacher for memory layout.
/// We never store this in a sequential array so we don't have to 8 byte align its size.
[StructLayout(LayoutKind.Explicit, Size = 40)]
internal struct CachedCompositeFamily
{
///
/// Composite or physical
///
[FieldOffset(0)]
internal FamilyType familyType;
///
/// Number of typefaces in this family.
///
[FieldOffset(4)]
internal int numberOfTypefaces;
[FieldOffset(8)]
internal double baseline;
[FieldOffset(16)]
internal double lineSpacing;
///
/// Number of localized names for this family.
///
[FieldOffset(24)]
internal int numberOfFamilyNames;
///
/// Pointer to the CachedName[numberOfFamilyNames] array of localized names for this family,
/// sorted by culture info string.
///
[FieldOffset(28)]
internal int offsetToFamilyNames;
///
/// Number of family maps in the family maps list.
///
[FieldOffset(32)]
internal int numberOfFamilyMaps;
///
/// Number of per-culture family map ranges lists. The range lists should be sorted in
/// order of decreasing cultural-specificity, with the default range list (for "any"
/// culture) coming last.
///
[FieldOffset(36)]
internal int numberOfFamilyMapRangeLists;
// numberOfFamilyMaps structures of type CachedFamilyMap
internal int OffsetToFamilyMaps
{
get
{
unsafe
{
return sizeof(CachedCompositeFamily);
}
}
}
// numberOfFamilyMapRangeLists structures of type CachedFamilyMapRangeList
internal int OffsetToFamilyMapRangeLists
{
get
{
unsafe
{
return OffsetToFamilyMaps + numberOfFamilyMaps * sizeof(CachedFamilyMap);
}
}
}
// [8 byte align barrier] numberOfTypefaces structures of type CachedCompositeFace
internal int OffsetToFamilyTypeface
{
get
{
unsafe
{
return Util.Align8(
OffsetToFamilyMapRangeLists +
numberOfFamilyMapRangeLists * sizeof(CachedFamilyMapRangeList)
);
}
}
}
// variable-length array of char containing null-terminated family names
internal int OffsetToTargetFamilyNameStrings
{
get
{
unsafe
{
return OffsetToFamilyTypeface + numberOfTypefaces * sizeof(CachedCompositeFace);
}
}
}
}
///
/// CachedFace is a base for CachedPhysicalFace and CachedCompositeFace
/// We can't use inheritance here, so we much keep the three structures in sync.
///
[StructLayout(LayoutKind.Explicit, Size = 12)]
internal struct CachedFace
{
[FieldOffset(0)]
internal FontStyle style;
[FieldOffset(4)]
internal FontWeight weight;
[FieldOffset(8)]
internal FontStretch stretch;
}
///
/// Memory layout: PhysicalFace struct | font Uri string
///
[StructLayout(LayoutKind.Explicit, Size = 24)]
internal struct CachedPhysicalFace
{
[FieldOffset(0)]
internal FontStyle style;
[FieldOffset(4)]
internal FontWeight weight;
[FieldOffset(8)]
internal FontStretch stretch;
// the Uri of the font face
[FieldOffset(12)]
internal int fontUriSize;
[FieldOffset(16)]
internal int faceIndex;
[FieldOffset(20)]
internal StyleSimulations styleSimulations;
}
// since we have doubles in this structure,
// always make its size aligned on 8 byte boundary
[StructLayout(LayoutKind.Explicit, Size = 64)]
internal struct CachedCompositeFace
{
[FieldOffset(0)]
internal FontStyle fontStyle;
[FieldOffset(4)]
internal FontWeight fontWeight;
[FieldOffset(8)]
internal FontStretch fontStretch;
[FieldOffset(12)]
internal int offsetToDeviceFont;
[FieldOffset(16)]
internal double underlinePosition;
[FieldOffset(24)]
internal double underlineThickness;
[FieldOffset(32)]
internal double strikeThroughPosition;
[FieldOffset(40)]
internal double strikeThroughThickness;
[FieldOffset(48)]
internal double capsHeight;
[FieldOffset(56)]
internal double xHeight;
};
// Memory layout: CachedDeviceFont | string | array of int. The size of
// the string and the integer array is variable, but we never store this structure in an
// array; its offset from the corresponding CachedCompositeFace is specified by the latter
// structure's offsetToDeviceFont field. 8-byte alignment is required.
[StructLayout(LayoutKind.Explicit, Size = 8)]
internal struct CachedDeviceFont
{
internal CachedDeviceFont(string name, int pageCount)
{
numberOfPages = pageCount;
sizeOfDeviceFontName = Util.StringSize(name);
}
// Number of pages of integers in the character map.
[FieldOffset(0)]
internal int numberOfPages;
// Size of the name in bytes; must be the last field in the structure
// as the device font name immediately follows
[FieldOffset(4)]
internal int sizeOfDeviceFontName;
// Offset of the sizeOfDeviceFontName field, which is the last field in
// this structure and is immediately followed by character data
internal const int OffsetToLengthPrefixedName = 4;
// Offset to an array of integers. The first CharacterMetricsDictionary.PageCount
// integers compose the "page table". It is followed by one or more "pages" each
// CharacterMetricsDictionary.PageSize integers.
//
// The high-order byte of a Unicode scalar value is an index into the page table,
// and the integer at that index is either zero (if the character is not mapped)
// or the index of the first element of the correct page for that range of characters.
// The low-order byte of the Unicode value is an index into the page, and the integer
// at that index is either zero (if the character is not mapped) or the byte offset
// of a CachedCharacterMetrics structure relative to the CachedDeviceFont.
internal int OffsetToCharacterMap
{
get
{
unsafe
{
return Util.Align4(
sizeof(CachedDeviceFont) + sizeOfDeviceFontName
);
}
}
}
// Gets the total size of the cached device font, including the device font name
// and character map, padded to an 8-byte boundary. The padding is so the the
// return value can be used as the offset to a subsequent device font.
internal int SizeOfCachedDeviceFont
{
get
{
unsafe
{
// The character map consist of a "page table" of PageCount elements
// followed by a variable number of "pages" of PageSize elements.
int characterMapLength =
CharacterMetricsDictionary.PageCount
+ (numberOfPages * CharacterMetricsDictionary.PageSize);
return Util.Align8(OffsetToCharacterMap + (characterMapLength * sizeof(int)));
}
}
}
}
// since we have doubles in this structure,
// always make its size aligned on 8 byte boundary
[StructLayout(LayoutKind.Explicit, Size = 56)]
internal struct CachedCharacterMetrics
{
[FieldOffset(0)]
internal double blackBoxWidth;
[FieldOffset(8)]
internal double blackBoxHeight;
[FieldOffset(16)]
internal double baseline;
[FieldOffset(24)]
internal double leftSideBearing;
[FieldOffset(32)]
internal double rightSideBearing;
[FieldOffset(40)]
internal double topSideBearing;
[FieldOffset(48)]
internal double bottomSideBearing;
}
// since we have doubles in this structure,
// always make its size aligned on 8 byte boundary
[StructLayout(LayoutKind.Explicit, Size = 24)]
internal struct CachedFamilyMap
{
[FieldOffset(0)]
internal double scaleInEm;
[FieldOffset(8)]
internal int targetFamilyNameOffset;
[FieldOffset(12)]
internal int numberOfRanges;
[FieldOffset(16)]
internal int offsetToRanges;
};
[StructLayout(LayoutKind.Explicit, Size = 8)]
internal struct CachedCharacterRange
{
[FieldOffset(0)]
internal int first;
[FieldOffset(4)]
internal uint delta;
};
[StructLayout(LayoutKind.Explicit, Size = 12)]
internal struct CachedFamilyMapRangeList
{
[FieldOffset(0)]
internal int offsetToIetfLanguageTag;
[FieldOffset(4)]
internal int lengthOfRanges;
[FieldOffset(8)]
internal int offsetToRanges;
};
[StructLayout(LayoutKind.Explicit, Size = 40)]
internal struct CachedPhysicalFamily
{
///
/// Composite or physical
///
[FieldOffset(0)]
internal FamilyType familyType;
///
/// Number of typefaces in this family.
///
[FieldOffset(4)]
internal int numberOfTypefaces;
[FieldOffset(8)]
internal double baseline;
[FieldOffset(16)]
internal double lineSpacing;
///
/// Number of localized names for this family.
///
[FieldOffset(24)]
internal int numberOfFamilyNames;
///
/// Pointer to the CachedName[numberOfFamilyNames] array of localized names for this family,
/// sorted by culture info string.
///
[FieldOffset(28)]
internal int offsetToFamilyNames;
[FieldOffset(32)]
internal int numberOfFaceNames;
[FieldOffset(36)]
internal bool symbol;
// numberOfFaceNames face names of type CachedName follow
// this sorted list is used for lookup by name
// numberOfTypefaces face pointers of type int follow
// this list is used for face enumeration
};
internal class BaseFamily
{
///
/// cacheOffset is used to reference the cached representation of the family
///
internal int cacheOffset = Util.nullOffset;
///
/// A sorted list of LocalizedStrings containing all names for this family.
/// Values don't matter.
///
internal SortedDictionary familyNames = new SortedDictionary(LocalizedName.LanguageComparer);
}
internal class PhysicalFamily : BaseFamily
{
// PhysicalFamily contains less members than CachedPhysicalFamily
// because they can be computed only when we have the full list of families and faces
internal SortedDictionary faceNames;
internal List typefaces = new List(1);
}
private class CompositeFamily : BaseFamily
{
internal CompositeFontInfo compositeFontInfo;
}
internal class PhysicalFace
{
///
/// cacheOffset is used to reference the cached representation of the face
///
internal int cacheOffset = Util.nullOffset;
///
/// Critical - fontUri can contain information about local file system.
///
[SecurityCritical]
internal string fontUri;
internal int faceIndex;
internal StyleSimulations styleSimulations;
internal FontStyle style;
internal FontWeight weight;
internal FontStretch stretch;
internal double version;
internal DateTime timestamp;
internal bool symbol;
internal ushort designEmHeight;
internal ushort designCellAscent;
internal ushort designCellDescent;
internal int designLineSpacing;
internal LocalizedName[] names;
///
/// Critical: accesses critical fontUri.
/// TreatAsSafe: fontUri is copied into another critical member.
///
[SecurityCritical, SecurityTreatAsSafe]
internal PhysicalFace Clone()
{
PhysicalFace clone = new PhysicalFace();
clone.fontUri = this.fontUri;
clone.faceIndex = this.faceIndex;
clone.styleSimulations = this.styleSimulations;
clone.style = this.style;
clone.weight = this.weight;
clone.stretch = this.stretch;
clone.version = this.version;
clone.timestamp = this.timestamp;
clone.symbol = this.symbol;
clone.designEmHeight = this.designEmHeight;
clone.designCellAscent = this.designCellAscent;
clone.designCellDescent = this.designCellDescent;
clone.designLineSpacing = this.designLineSpacing;
clone.names = this.names;
return clone;
}
}
private unsafe struct LocalizedNameDictionary : IDictionary
{
private FamilyCollection _familyCollection;
private int _numberOfFamilyNames;
///
/// Critical: This holds reference to a pointer variable
///
[SecurityCritical]
private int* _offsetToFamilyNames;
///
/// Critical: This function acceses _cacher[] and dereferences a pointer
///
[SecurityCritical]
internal LocalizedNameDictionary(FamilyCollection familyCollection, CachedFamily* cachedFamily)
{
Invariant.Assert(cachedFamily!=null);
_numberOfFamilyNames = cachedFamily->numberOfFamilyNames;
_familyCollection = familyCollection;
_offsetToFamilyNames = (int*)_familyCollection._cacher[cachedFamily->offsetToFamilyNames];
Invariant.Assert(_numberOfFamilyNames >= 1);
Invariant.Assert(*_offsetToFamilyNames != 0 && *_offsetToFamilyNames != Util.nullOffset);
}
///
/// Returns a name that uniquely identifies this family.
/// The name culture doesn't matter, as the name is supposed to be used only
/// for FontFamily construction.
///
///
///
/// Critical:This code acceses _offsetToFamilyName
/// TreatAsSafe: This information is ok to expose and the pointer it accesses is critical
/// and all creation is tracked
///
internal string OrdinaryName
{
[SecurityCritical,SecurityTreatAsSafe]
get
{
return _familyCollection.GetStringFromOffset(*(_offsetToFamilyNames + 1));
}
}
#region IDictionary Members
bool IDictionary.Remove(XmlLanguage key)
{
throw new NotSupportedException();
}
void IDictionary.Add(XmlLanguage key, string value)
{
throw new NotSupportedException();
}
ICollection IDictionary.Keys
{
get
{
// Disable PreSharp warning "Property get methods should not throw exceptions."
// Presumably PreSharp is reporting this because the getter allocates memory, but
// it is not unreasable for a getter that returns a collection to allocate a new
// object.
#pragma warning disable 6503
// OK, this is very slow, but semantically correct.
// Keys are not used that often anyway.
XmlLanguage[] keys = new XmlLanguage[_numberOfFamilyNames];
int i = 0;
foreach (KeyValuePair pair in this)
{
keys[i++] = pair.Key;
}
return keys;
#pragma warning restore 6503
}
}
string IDictionary.this[XmlLanguage key]
{
get
{
// the number of family name localizations is usually small
// if we find important families that behave otherwise
// we can always change the linear algorithm below to binary search
// since the list of family names is already sorted by culture name
foreach (KeyValuePair pair in this)
{
if (pair.Key.Equals(key))
return pair.Value;
}
return null;
}
set
{
throw new NotSupportedException();
}
}
bool IDictionary.TryGetValue(XmlLanguage key, out string value)
{
// the number of family name localizations is usually small
// if we find important families that behave otherwise
// we can always change the linear algorithm below to binary search
// since the list of family names is already sorted by culture name
foreach (KeyValuePair pair in this)
{
if (pair.Key.Equals(key))
{
value = pair.Value;
return true;
}
}
value = null;
return false;
}
ICollection IDictionary.Values
{
get
{
// Disable PreSharp warning "Property get methods should not throw exceptions."
// Presumably PreSharp is reporting this because the getter allocates memory, but
// it is not unreasable for a getter that returns a collection to allocate a new
// object.
#pragma warning disable 6503
// OK, this is very slow, but semantically correct.
// Values are not used that often anyway.
string[] values = new string[_numberOfFamilyNames];
int i = 0;
foreach (KeyValuePair pair in this)
{
values[i++] = pair.Value;
}
return values;
#pragma warning restore 6503
}
}
bool IDictionary.ContainsKey(XmlLanguage key)
{
// the number of family name localizations is usually small
// if we find important families that behave otherwise
// we can always change the linear algorithm below to binary search
// since the list of family names is already sorted by culture name
foreach (KeyValuePair pair in this)
{
if (pair.Key.Equals(key))
return true;
}
return false;
}
#endregion
#region ICollection> Members
bool ICollection>.Contains(KeyValuePair item)
{
// the number of family name localizations is usually small
// if we find important families that behave otherwise
// we can always change the linear algorithm below to binary search
// since the list of family names is already sorted by culture name
string value = item.Value;
foreach (KeyValuePair pair in this)
{
// We don't use KeyValuePair.Equals because it is inherited from ValueType.Equals
// and involves reflection.
if (pair.Key.Equals(item.Key) && String.Compare(pair.Value, value, StringComparison.OrdinalIgnoreCase) == 0)
return true;
}
return false;
}
void ICollection>.Add(KeyValuePair item)
{
throw new NotSupportedException();
}
bool ICollection>.Remove(KeyValuePair item)
{
throw new NotSupportedException();
}
bool ICollection>.IsReadOnly
{
get
{
return true;
}
}
void ICollection>.Clear()
{
throw new NotSupportedException();
}
int ICollection>.Count
{
get
{
return _numberOfFamilyNames;
}
}
void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
{
int index = arrayIndex;
foreach (KeyValuePair pair in this)
{
array[index] = pair;
++index;
}
}
#endregion
#region IEnumerable> Members
IEnumerator> IEnumerable>.GetEnumerator()
{
return new Enumerator(this);
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return new Enumerator(this);
}
#endregion
private unsafe struct Enumerator : IEnumerator>
{
private int _currentFamilyName;
private LocalizedNameDictionary _parent;
public Enumerator(LocalizedNameDictionary parent)
{
_parent = parent;
_currentFamilyName = -1;
}
private void FailIfOutOfRange()
{
if (_currentFamilyName < 0)
throw new InvalidOperationException(SR.Get(SRID.Enumerator_NotStarted));
if (_currentFamilyName >= _parent._numberOfFamilyNames)
throw new InvalidOperationException(SR.Get(SRID.Enumerator_ReachedEnd));
}
///
/// Critical:This code acceses _offsetToFamilyNames
/// TreatAsSafe: This information is ok to expose and the pointer it accesses is critical
/// and all creation is tracked
///
[SecurityCritical,SecurityTreatAsSafe]
private XmlLanguage GetCurrentLanguage()
{
string languageName = _parent._familyCollection.GetStringFromOffset(*(_parent._offsetToFamilyNames + 2 * _currentFamilyName));
return XmlLanguage.GetLanguage(languageName);
}
///
/// Critical:This code acceses _offsetToFamilyNames
/// TreatAsSafe: This information is ok to expose and the pointer it accesses is critical
/// and all creation is tracked
///
[SecurityCritical, SecurityTreatAsSafe]
private string GetCurrentName()
{
return _parent._familyCollection.GetStringFromOffset(*(_parent._offsetToFamilyNames + 2 * _currentFamilyName + 1));
}
#region IEnumerator> Members
public bool MoveNext()
{
++_currentFamilyName;
if (_currentFamilyName >= _parent._numberOfFamilyNames)
{
// prevent cycling
_currentFamilyName = _parent._numberOfFamilyNames;
return false;
}
return true;
}
///
/// Critical: This function acceses _cacher[]
/// TreatAsSafe: This information is ok to expose
///
KeyValuePair IEnumerator>.Current
{
[SecurityCritical,SecurityTreatAsSafe]
get
{
FailIfOutOfRange();
return new KeyValuePair(GetCurrentLanguage(), GetCurrentName());
}
}
object IEnumerator.Current
{
get
{
return ((IEnumerator>)this).Current;
}
}
public void Reset()
{
_currentFamilyName = -1;
}
#endregion
#region IDisposable Members
public void Dispose() { }
#endregion
}
}
#endregion Private Types
//-----------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
private FontSourceCollection _fontFolder;
///
/// Critical - this value is used to make security decisions (i.e., whether FontSourceCollection
/// does an assert) so it can only be set by critical code.
///
[SecurityCritical]
private bool _isWindowsFonts;
///
/// Cached result of calling _fontFolder.GetUriString().
/// This string is expensive to recompute and is used very frequently.
///
///
/// Critical: This function stores the absolute font Uri, which is critical data.
///
[SecurityCritical]
private string _fontFolderUriString;
///
/// Critical: This holds reference to a pointer object
///
[SecurityCritical]
private unsafe Layout* _data;
private ElementCacher _cacher;
#endregion Private Fields
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- HyperLink.cs
- ExpressionNode.cs
- ResourceProperty.cs
- ToolZone.cs
- XmlStrings.cs
- QueryOperationResponseOfT.cs
- XmlEventCache.cs
- MsmqUri.cs
- DataObjectFieldAttribute.cs
- KeyConverter.cs
- ObjectFactoryCodeDomTreeGenerator.cs
- DES.cs
- ListSortDescriptionCollection.cs
- PathHelper.cs
- UnauthorizedWebPart.cs
- ApplicationDirectory.cs
- ReadWriteObjectLock.cs
- ProjectionCamera.cs
- CanExecuteRoutedEventArgs.cs
- SqlCommandSet.cs
- RouteParser.cs
- FigureParagraph.cs
- StorageEntitySetMapping.cs
- UnhandledExceptionEventArgs.cs
- SourceFilter.cs
- ScrollProperties.cs
- SqlDataSourceWizardForm.cs
- ReflectEventDescriptor.cs
- XmlDocumentSchema.cs
- FixedSOMImage.cs
- ObsoleteAttribute.cs
- Attachment.cs
- FontFamily.cs
- CodePageEncoding.cs
- PrinterResolution.cs
- StreamUpgradeBindingElement.cs
- MulticastNotSupportedException.cs
- UpdatePanel.cs
- ChameleonKey.cs
- ExtensionMethods.cs
- XmlSchemaSubstitutionGroup.cs
- AspNetSynchronizationContext.cs
- CallbackHandler.cs
- IteratorFilter.cs
- Triangle.cs
- ADMembershipProvider.cs
- ObjectSecurity.cs
- XmlSchemaSimpleContentExtension.cs
- BamlLocalizerErrorNotifyEventArgs.cs
- ListViewGroupItemCollection.cs
- SqlWriter.cs
- BaseServiceProvider.cs
- BatchStream.cs
- XmlAutoDetectWriter.cs
- TableAdapterManagerHelper.cs
- ToolStripMenuItemDesigner.cs
- ConfigXmlWhitespace.cs
- GridViewSortEventArgs.cs
- StringValidatorAttribute.cs
- Context.cs
- DataSetUtil.cs
- DeploymentExceptionMapper.cs
- EventDrivenDesigner.cs
- QueryCorrelationInitializer.cs
- SQLStringStorage.cs
- ColorBlend.cs
- GradientStop.cs
- SchemaObjectWriter.cs
- DesignerForm.cs
- XslNumber.cs
- BamlResourceContent.cs
- WCFServiceClientProxyGenerator.cs
- CompositeKey.cs
- ExternalException.cs
- PageRequestManager.cs
- RequestCache.cs
- ChannelDispatcherCollection.cs
- XpsFontSerializationService.cs
- DeclarativeCatalogPart.cs
- MailHeaderInfo.cs
- ResourceExpression.cs
- ReadOnlyPropertyMetadata.cs
- CellQuery.cs
- Int32AnimationUsingKeyFrames.cs
- BufferedReceiveElement.cs
- Int16Storage.cs
- ColumnMapProcessor.cs
- FocusChangedEventArgs.cs
- FolderNameEditor.cs
- XmlSchemaAnyAttribute.cs
- ACL.cs
- GridEntry.cs
- ControlAdapter.cs
- TextProperties.cs
- MailSettingsSection.cs
- TraceEventCache.cs
- CompositeKey.cs
- LineInfo.cs
- SecureStringHasher.cs
- PrivilegeNotHeldException.cs