Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Core / CSharp / MS / Internal / FontCache / FontFaceLayoutInfo.cs / 1 / FontFaceLayoutInfo.cs
//----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Description: The FontFaceLayoutInfo class
//
// History:
// 07/23/2003 : mleonov - Big rewrite to change cache structure
//
//---------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Security;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Media;
using System.Runtime.InteropServices;
using MS.Win32;
using MS.Utility;
using MS.Internal;
using MS.Internal.FontFace;
using MS.Internal.Shaping;
using MS.Internal.PresentationCore;
namespace MS.Internal.FontCache
{
[FriendAccessAllowed]
internal sealed class FontFaceLayoutInfo : IFontCacheElement
{
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
#region Constructors
internal FontFaceLayoutInfo(FontSource fontSource, int faceIndex)
{
_fontSource = fontSource;
_faceIndex = faceIndex;
_timeStamp = _fontSource.SkipLastWriteTime();
}
///
/// Critical - Calls into the critical RetrieveKey method.
///
[SecurityCritical]
internal FontFaceLayoutInfo(CheckedPointer key)
{
RetrieveKey(key);
}
#endregion Constructors
// layout: struct | FileName | 4byte aligned CMAP (17 int pointers to planes)
// the structure size should be aligned on 2 byte boundary, because file name is stored
// immediately after the structure.
// As usual, individual structure members need to be properly aligned.
[StructLayout(LayoutKind.Explicit, Size = Layout.keyOffset + 8)]
private struct Layout
{
[FieldOffset(0)]
internal ushort designEmHeight;
[FieldOffset(2)]
internal ushort glyphCount;
[FieldOffset(4)]
internal ushort designCellAscent;
[FieldOffset(6)]
internal ushort designCellDescent;
[FieldOffset(8)]
internal short fontContrastAdjustment;
[FieldOffset(10)]
internal short xHeight;
[FieldOffset(12)]
internal short capsHeight;
[FieldOffset(14)]
internal ushort blankGlyph;
[FieldOffset(16)]
internal ushort invalidGlyph;
[FieldOffset(18)]
internal ushort underlineSize;
[FieldOffset(20)]
internal short underlinePosition;
[FieldOffset(22)]
internal ushort strikeThroughSize;
[FieldOffset(24)]
internal short strikeThroughPosition;
[FieldOffset(26)]
internal ushort symbol;
// RenderingHints is a ushort enum, so 2 bytes are enough.
[FieldOffset(28)]
internal RenderingHints renderingHints;
[FieldOffset(30)]
internal ushort embeddingRights;
[FieldOffset(32)]
internal long timeStamp;
[FieldOffset(40)]
internal long gsubScripts;
[FieldOffset(48)]
internal long gposScripts;
[FieldOffset(56)]
internal int offAdvanceWidths; // offset of the advance width array
[FieldOffset(60)]
internal int offGsub; // ditto for GSUB
[FieldOffset(64)]
internal int offGpos; // ditto for GPOS
[FieldOffset(68)]
internal int offGdef; // ditto for GDEF
[FieldOffset(72)]
internal int offJstf; // ditto for JSTF
[FieldOffset(76)]
internal int familyNames;
[FieldOffset(80)]
internal int win32FamilyNames;
[FieldOffset(84)]
internal int faceNames;
[FieldOffset(88)]
internal int win32faceNames;
[FieldOffset(92)]
internal int versionStrings;
[FieldOffset(96)]
internal int copyrights;
[FieldOffset(100)]
internal int manufacturerNames;
[FieldOffset(104)]
internal int trademarks;
[FieldOffset(108)]
internal int designerNames;
[FieldOffset(112)]
internal int descriptions;
[FieldOffset(116)]
internal int vendorUrls;
[FieldOffset(120)]
internal int designerUrls;
[FieldOffset(124)]
internal int licenseDescriptions;
[FieldOffset(128)]
internal int sampleTexts;
[FieldOffset(132)]
internal FontStyle style;
[FieldOffset(136)]
internal FontWeight weight;
[FieldOffset(140)]
internal FontStretch stretch;
[FieldOffset(144)]
internal double version;
[FieldOffset(152)]
internal int offGaspRanges;
[FieldOffset(156)]
internal TypographyAvailabilities typographyAvailabilities;
[FieldOffset(160)]
internal FontTechnology fontTechnology;
[FieldOffset(164)]
internal int offGsubCache;
[FieldOffset(168)]
internal int gsubCacheLength;
[FieldOffset(172)]
internal int offGposCache;
[FieldOffset(176)]
internal int gposCacheLength;
// family and face names adjusted by the font differentiator
[FieldOffset(180)]
internal int adjustedFaceNames;
// this should be equal to the offset immediately after the previous field
internal const int keyOffset = 184;
// key starts here
[FieldOffset(keyOffset)]
internal int faceIndex;
[FieldOffset(keyOffset + 4)]
internal int uriStringSizeInBytes;
internal static CheckedPointer GetUriString(CheckedPointer This)
{
unsafe
{
return This + sizeof(Layout);
}
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks
/// TreatAsSafe: It advances the pointer by the requisite number of bytes .
/// Calls to probe and '+ are bounds checked.
///
[SecurityCritical, SecurityTreatAsSafe]
internal static CheckedPointer GetCmap(CheckedPointer This)
{
unsafe
{
Layout* l = (Layout*)This.Probe(0, sizeof(Layout));
return GetUriString(This) + Util.Align4(l->uriStringSizeInBytes);
}
}
}
[StructLayout(LayoutKind.Explicit, Size = 12)]
private struct CachedName
{
[FieldOffset(0)]
internal int language;
[FieldOffset(4)]
internal int nameSize;
///
/// Offset to the name string
///
[FieldOffset(8)]
internal int nameString;
}
[StructLayout(LayoutKind.Explicit, Size = 4)]
private struct CachedNames
{
[FieldOffset(0)]
internal int numberOfNames;
// followed by array of CachedName structures
}
internal FontSource FontSource
{
get
{
return _fontSource;
}
}
internal int FaceIndex
{
get
{
return _faceIndex;
}
}
int IFontCacheElement.Type
{
get
{
return 3;
}
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks
/// TreatAsSafe: This stores data about the font into a pointer. This data is safe to
/// expose and the pointer operations are bounds checked.
///
[SecurityCritical, SecurityTreatAsSafe]
private void StoreKeyInternal(CheckedPointer d, out int realSize)
{
unsafe
{
int* k = (int*)d.Probe(0, sizeof(int));
*k = _faceIndex;
realSize = sizeof(int);
d += sizeof(int);
realSize += Util.StringAndLengthCopyToCheckedPointer(d, FontSource.GetUriString());
}
}
void IFontCacheElement.StoreKey(CheckedPointer d, out int realSize)
{
Debug.Assert(!_fontSource.IsAppSpecific);
StoreKeyInternal(d, out realSize);
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks.
/// Calls into the critical FontSource constructor.
///
[SecurityCritical]
public void RetrieveKey(CheckedPointer p)
{
unsafe
{
_faceIndex = *(int*)p.Probe(0, sizeof(int));
string fileName = Util.StringAndLengthCopyFromCheckedPointer(p + sizeof(int));
_fontSource = new FontSource(new Uri(fileName, UriKind.Absolute), false);
}
_timeStamp = _fontSource.SkipLastWriteTime();
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks
/// TreatAsSafe: The call to probe is bounds checked and also the pointers returned
/// are used locally and not passed around.
///
[SecurityCritical, SecurityTreatAsSafe]
bool IFontCacheElement.Match(CheckedPointer p)
{
unsafe
{
Layout* l = (Layout*)p.Probe(0, sizeof(Layout));
string fontUriString = FontSource.GetUriString();
if (Util.StringSize(fontUriString) != l->uriStringSizeInBytes)
return false;
if (_timeStamp != l->timeStamp)
return false;
if (_faceIndex != l->faceIndex)
return false;
return Util.StringEqualIgnoreCase(Layout.GetUriString(p), fontUriString);
}
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks. It
/// accesses _layout which is a pointer
/// TreatAsSafe: This data is ok to give out, also storing the checked pointer is
/// ok
///
[SecurityCritical, SecurityTreatAsSafe]
void IFontCacheElement.GetData(CheckedPointer block, ElementCacher cacher)
{
unsafe
{
_cacher = cacher;
_layout = (Layout*)block.Probe(0, sizeof(Layout));
_timeStamp = _layout->timeStamp;
_cmap = new IntMap(Layout.GetCmap(block), _cacher);
_cmap.SetGlyphCount(_layout->glyphCount);
}
}
///
/// Critical - As this function accesses FileName that contains absolute font path.
/// Safe - As this is only used to compute the cache element size.
///
int IFontCacheElement.Size
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe
{
return
sizeof(Layout) +
Util.Align4(Util.StringSize(FontSource.GetUriString())) +
IntMap.InitialSize();
}
}
}
bool IFontCacheElement.IsAppSpecific
{
get
{
return _fontSource.IsAppSpecific;
}
}
int IFontCacheElement.GetHashCode()
{
int hash = FontSource.GetHashCode();
hash = HashFn.HashMultiply(hash) + _faceIndex;
return HashFn.HashScramble(hash);
}
///
/// Critical - As this function obtains UnmanagedMemoryStream from FontSource.Pin()
/// which is Critical.
/// Safe - As this is only used to add font information to the cache which is safe.
///
[SecurityCritical, SecurityTreatAsSafe]
void IFontCacheElement.AddToCache(CheckedPointer newPointer, ElementCacher cacher)
{
_cacher = cacher;
int realSize;
StoreKeyInternal(newPointer + Layout.keyOffset, out realSize);
unsafe
{
_layout = (Layout*)newPointer.Probe(0, sizeof(Layout));
Debug.Assert(_timeStamp != 0);
_layout->timeStamp = _timeStamp;
// initialize optional table pointers
_layout->offGsub =
_layout->offGpos =
_layout->offGdef =
_layout->offJstf =
_layout->offGaspRanges =
Util.nullOffset;
_cmap = new IntMap(Layout.GetCmap(newPointer), _cacher);
}
_cmap.Init();
UnmanagedMemoryStream pinnedFontSource = _fontSource.GetUnmanagedStream();
try
{
TrueTypeFontDriver ttd = new TrueTypeFontDriver(pinnedFontSource, _fontSource.Uri);
ttd.SetFace(_faceIndex);
ttd.GetLayoutFontFaceInfo(this);
ttd.GetShapingFontFaceInfo(this);
}
catch (SEHException e)
{
throw Util.ConvertInPageException(_fontSource, e);
}
finally
{
pinnedFontSource.Close();
}
unsafe
{
_layout->offGsubCache = Util.nullOffset;
_layout->offGposCache = Util.nullOffset;
CreateOpenTypeLayoutCache();
_layout->typographyAvailabilities = ComputeTypographyAvailabilities();
}
}
///
/// Critical: Returns a writeable cmap.
///
internal IntMap CharacterMap
{
[SecurityCritical]
get
{
return _cmap;
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This code exposes DesignEmHeight which is ok to expose
/// The risk here is if _layout is null, but that is mitigated by probe.
///
internal ushort DesignEmHeight
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->designEmHeight; }
}
[SecurityCritical]
set
{
unsafe { _layout->designEmHeight = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal ushort DesignCellAscent
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->designCellAscent; }
}
[SecurityCritical]
set
{
unsafe { _layout->designCellAscent = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal ushort DesignCellDescent
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->designCellDescent; }
}
[SecurityCritical]
set
{
unsafe { _layout->designCellDescent = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal short xHeight
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->xHeight; }
}
[SecurityCritical]
set
{
unsafe { _layout->xHeight = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal short CapsHeight
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->capsHeight; }
}
[SecurityCritical]
set
{
unsafe { _layout->capsHeight = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal ushort BlankGlyph
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->blankGlyph; }
}
[SecurityCritical]
set
{
unsafe { _layout->blankGlyph = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
///
internal ushort InvalidGlyph
{
[SecurityCritical]
set
{
unsafe { _layout->invalidGlyph = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal ushort UnderlineThickness
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->underlineSize; }
}
[SecurityCritical]
set
{
unsafe { _layout->underlineSize = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal short UnderlinePosition
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->underlinePosition; }
}
[SecurityCritical]
set
{
unsafe { _layout->underlinePosition = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal ushort StrikethroughThickness
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->strikeThroughSize; }
}
[SecurityCritical]
set
{
unsafe { _layout->strikeThroughSize = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal short StrikethroughPosition
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->strikeThroughPosition; }
}
[SecurityCritical]
set
{
unsafe { _layout->strikeThroughPosition = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal bool Symbol
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->symbol != 0; }
}
[SecurityCritical]
set
{
unsafe { _layout->symbol = value ? (ushort)1 : (ushort)0; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal RenderingHints FontRenderingHints
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->renderingHints; }
}
[SecurityCritical]
set
{
unsafe { _layout->renderingHints = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing,
/// the data is sensitive and is not OK to expose to untrusted clients.
///
internal FontEmbeddingRight EmbeddingRights
{
[SecurityCritical]
get
{
unsafe { return (FontEmbeddingRight)_layout->embeddingRights; }
}
[SecurityCritical]
set
{
// Make sure no truncation happens when storing the enum value as a ushort.
Invariant.Assert(value == (FontEmbeddingRight)(short)value);
unsafe { _layout->embeddingRights = (ushort)value; }
}
}
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal FontTechnology FontTechnology
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->fontTechnology; }
}
[SecurityCritical]
set
{
unsafe { _layout->fontTechnology = value; }
}
}
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal short FontContrastAdjustment
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->fontContrastAdjustment; }
}
[SecurityCritical]
set
{
unsafe { _layout->fontContrastAdjustment = value; }
}
}
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal FontStyle Style
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->style; }
}
[SecurityCritical]
set
{
unsafe { _layout->style = value; }
}
}
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal FontWeight Weight
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->weight; }
}
[SecurityCritical]
set
{
unsafe { _layout->weight = value; }
}
}
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal FontStretch Stretch
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->stretch; }
}
[SecurityCritical]
set
{
unsafe { _layout->stretch = value; }
}
}
///
/// Critical: This calls into an unsafe code block and exposes font version,
/// which contains extra font information not strictly needed for rendering.
///
internal double Version
{
[SecurityCritical]
get
{
unsafe { return _layout->version; }
}
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable
///
internal TypographyAvailabilities TypographyAvailabilities
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->typographyAvailabilities; }
}
}
[Flags]
internal enum GaspFlags : ushort
{
GASP_GRIDFIT = 1,
GASP_DOGRAY = 2,
GASP_SYMMETRIC_GRIDFIT = 4,
GASP_SYMMETRIC_SMOOTHING = 8
}
internal struct GaspRange
{
public ushort ppem;
public GaspFlags flags;
};
///
/// Critical: This calls into an unsafe code block. This code
/// acceses cacher, allocates memory and manipulates pointers
/// TreatAsSafe: The pointer calls are bounds checked and
/// checked for validity in _cacher and ok to expose.
///
internal GaspRange[] GaspRanges
{
[SecurityCritical, SecurityTreatAsSafe]
set
{
unsafe
{
_layout->offGaspRanges = _cacher.Alloc(sizeof(ushort) + value.Length * 2 * sizeof(ushort));
ushort* gaspRanges = (ushort*)_cacher[_layout->offGaspRanges];
*gaspRanges++ = (ushort)value.Length;
foreach (GaspRange range in value)
{
*gaspRanges++ = range.ppem;
*gaspRanges++ = (ushort)range.flags;
}
}
}
}
// horizontal/vertical metrics are stored in this format:
// ushort number of glyphs
// followed by array of GlyphMetrics structures
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 14)]
internal struct GlyphMetrics
{
// horizontal metrics
internal ushort advanceWidth;
internal short lsb; // left sidebearing
internal short rsb; // right sidebearing
// vertical metrics
internal ushort advanceHeight;
internal short tsb; // top sidebearing
internal short bsb; // bottom sidebearing
internal short baseline; // distance to the bottom black pixel from base line Y
};
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing and memory overrun.
/// TreatAsSafe: This data is ok to expose. The
/// risk is in setting this variable since
/// it is used to index into unmanaged memory.
///
internal ushort GlyphCount
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe
{
return _layout->glyphCount;
}
}
[SecurityCritical]
set
{
unsafe
{
_layout->glyphCount = value;
}
}
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: Allocating memory and assigning it to layout
/// is ok.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void CreateAdvanceWidthsArray()
{
unsafe
{
_layout->offAdvanceWidths = _cacher.Alloc(GlyphCount * sizeof(GlyphMetrics));
}
}
///
/// Critical: Calls unsafe code blocks. It returns a pointer
///
[SecurityCritical]
internal unsafe GlyphMetrics* Metrics(ushort glyphIndex)
{
if (glyphIndex >= GlyphCount)
throw new ArgumentOutOfRangeException("glyphIndex", SR.Get(SRID.GlyphIndexOutOfRange, glyphIndex));
Invariant.Assert(_layout->offAdvanceWidths != Util.nullOffset && _layout->offAdvanceWidths != 0);
GlyphMetrics* metrics = (GlyphMetrics*)_cacher[_layout->offAdvanceWidths];
return metrics + glyphIndex;
}
// horizontal metrics
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal ushort GetAdvanceWidth(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->advanceWidth; }
}
///
/// Critical: Calls unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
internal void SetAdvanceWidth(ushort glyphIndex, ushort advanceWidth)
{
unsafe { Metrics(glyphIndex)->advanceWidth = advanceWidth; }
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal short GetLeftSidebearing(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->lsb; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetLeftSidebearing(ushort glyphIndex, short lsb)
{
unsafe { Metrics(glyphIndex)->lsb = lsb; }
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal short GetRightSidebearing(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->rsb; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetRightSidebearing(ushort glyphIndex, short rsb)
{
unsafe { Metrics(glyphIndex)->rsb = rsb; }
}
// vertical metrics
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal ushort GetAdvanceHeight(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->advanceHeight; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetAdvanceHeight(ushort glyphIndex, ushort advanceHeight)
{
unsafe { Metrics(glyphIndex)->advanceHeight = advanceHeight; }
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal short GetTopSidebearing(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->tsb; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetTopSidebearing(ushort glyphIndex, short tsb)
{
unsafe { Metrics(glyphIndex)->tsb = tsb; }
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal short GetBottomSidebearing(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->bsb; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetBottomSidebearing(ushort glyphIndex, short bsb)
{
unsafe { Metrics(glyphIndex)->bsb = bsb; }
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal short GetBaseline(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->baseline; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetBaseline(ushort glyphIndex, short baseline)
{
unsafe { Metrics(glyphIndex)->baseline = baseline; }
}
// OpenType support
///
/// Critical: Calls unsafe code blocks and returns a pointer
///
[SecurityCritical]
internal unsafe byte* Gsub()
{
if (_layout->offGsub == Util.nullOffset)
return null;
return _cacher[_layout->offGsub];
}
///
/// Critical: Calls unsafe code blocks and returns a pointer
///
[SecurityCritical]
internal unsafe byte* Gpos()
{
if (_layout->offGpos == Util.nullOffset)
return null;
return _cacher[_layout->offGpos];
}
///
/// Critical: Calls unsafe code blocks and returns a pointer
///
[SecurityCritical]
internal unsafe byte* Gdef()
{
if (_layout->offGdef == Util.nullOffset)
return null;
return _cacher[_layout->offGdef];
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
private int SetTable(CheckedPointer tablePointer)
{
unsafe
{
if (tablePointer.Size < sizeof(int))
return Util.nullOffset;
int layoutOffset = _cacher.Alloc(tablePointer.Size);
CheckedPointer table = _cacher.GetCheckedPointer(layoutOffset);
tablePointer.CopyTo(table);
// Table size is stored as the first integer in the cached table.
int* tableSize = (int*)table.Probe(0, sizeof(int));
*tableSize = tablePointer.Size;
return layoutOffset;
}
}
///
/// Critical: Calls unsafe code
///
[SecurityCritical]
internal unsafe CheckedPointer GetTableCache(OpenTypeTags tableTag)
{
switch (tableTag)
{
case OpenTypeTags.GSUB :
if (_layout->offGsubCache != Util.nullOffset)
{
return new CheckedPointer(_cacher[_layout->offGsubCache], _layout->gsubCacheLength);
}
break;
case OpenTypeTags.GPOS :
if (_layout->offGposCache != Util.nullOffset)
{
return new CheckedPointer(_cacher[_layout->offGposCache], _layout->gposCacheLength);
}
break;
default:
throw new NotSupportedException();
}
return new CheckedPointer();
}
///
/// Critical: Calls critical and unsafe code
///
[SecurityCritical]
internal unsafe CheckedPointer AllocateTableCache(OpenTypeTags tableTag, int size)
{
int layoutOffset = _cacher.Alloc(size);
switch (tableTag)
{
case OpenTypeTags.GSUB :
{
_layout->offGsubCache = layoutOffset;
_layout->gsubCacheLength = size;
break;
}
case OpenTypeTags.GPOS :
{
_layout->offGposCache = layoutOffset;
_layout->gposCacheLength = size;
break;
}
default:
{
throw new NotSupportedException();
}
}
if (layoutOffset == Util.nullOffset)
{
return new CheckedPointer();
}
//
return new CheckedPointer(_cacher[layoutOffset], size);
}
///
/// Compiles cache data needed for OpenType layout services
///
///
/// Critical - calls critical code (OpenType layout table access)
///
[SecurityCritical]
private void CreateOpenTypeLayoutCache()
{
int maxLayoutCacheSize = 0x4000; // 16K
GsubGposTables gsubGpos = new GsubGposTables(this);
//
OpenTypeLayout.CreateLayoutCache(gsubGpos, maxLayoutCacheSize);
}
///
/// Computes the typography availabilities.
/// It checks the presence of a set of required features in the font
/// for ranges of unicode code points and set the corresponding bits
/// in the TypographyAvailabilities enum. TypographyAvailabilities enum is
/// used to determind whether fast path can be used to format the input.
///
///
/// Critical - calls critical code (GetComplexLanguageList)
/// TreatAsSafe - returns safe information about the font
///
[SecurityCritical, SecurityTreatAsSafe]
private TypographyAvailabilities ComputeTypographyAvailabilities()
{
uint[] glyphBits = new uint[(GlyphCount + 31) >> 5];
ushort minGlyphId = 65535;
ushort maxGlyphId = 0;
WritingSystem[] complexScripts;
TypographyAvailabilities typography = TypographyAvailabilities.None;
GsubGposTables GsubGpos = new GsubGposTables(this);
// preparing the glyph bits. When the bit is set, it means the corresponding
// glyph needs to be checked against.
for (int i = 0; i < fastTextRanges.Length; i++)
{
int firstChar = fastTextRanges[i].firstChar;
int lastChar = fastTextRanges[i].lastChar;
for (int ch = firstChar; ch <= lastChar; ch++)
{
ushort glyphId;
if (CharacterMap.TryGetValue(ch, out glyphId))
{
glyphBits[glyphId >> 5] |= (uint)(1 << (glyphId % 32));
if (glyphId > maxGlyphId) maxGlyphId = glyphId;
if (glyphId < minGlyphId) minGlyphId = glyphId;
}
}
}
//
// Step 1: call OpenType layout engine to test presence of
// 'locl' feature. Based on the returned writing systems, set
// FastTextMajorLanguageLocalizedFormAvailable bit and
// FastTextExtraLanguageLocalizedFormAvailable bit
//
OpenTypeLayoutResult result;
unsafe
{
result = OpenTypeLayout.GetComplexLanguageList(
GsubGpos,
LoclFeature,
glyphBits,
minGlyphId,
maxGlyphId,
out complexScripts
);
}
if (result != OpenTypeLayoutResult.Success)
{
// The check failed. We abort and don't keep partial results that are not reliable
return TypographyAvailabilities.None;
}
else if (complexScripts != null)
{
// This is the bits for localized form we would want to set
// if both bits for localized form were set, we can end the loop earlier
TypographyAvailabilities loclBitsTest =
TypographyAvailabilities.FastTextMajorLanguageLocalizedFormAvailable
| TypographyAvailabilities.FastTextExtraLanguageLocalizedFormAvailable;
for (int i = 0; i < complexScripts.Length && typography != loclBitsTest; i++)
{
if (MajorLanguages.Contains((LanguageTags)complexScripts[i].langSysTag))
{
typography |= TypographyAvailabilities.FastTextMajorLanguageLocalizedFormAvailable;
}
else
{
typography |= TypographyAvailabilities.FastTextExtraLanguageLocalizedFormAvailable;
}
}
}
//
// step 2: continue to find out whether there is common features availabe
// in the font for the unicode ranges and set the FastTextTypographyAvailable bit
//
unsafe
{
result = OpenTypeLayout.GetComplexLanguageList(
GsubGpos,
RequiredTypographyFeatures,
glyphBits,
minGlyphId,
maxGlyphId,
out complexScripts
);
}
if (result != OpenTypeLayoutResult.Success)
{
// The check failed. We abort and don't keep partial results that are not reliable
return TypographyAvailabilities.None;
}
else if (complexScripts != null)
{
typography |= TypographyAvailabilities.FastTextTypographyAvailable;
}
//
// Step 3: call OpenType layout engine to find out if there is any feature present for
// ideographs. Because there are many ideographs to check for, an alternative is to
// check for all scripts with the required features in the font by setting all
// glyph bits to 1, then see whether CJKIdeograph is in the returned list.
//
for (int i = 0; i < glyphBits.Length; i++)
{
glyphBits[i] = 0xFFFFFFFF;
}
unsafe
{
result = OpenTypeLayout.GetComplexLanguageList(
GsubGpos,
RequiredFeatures,
glyphBits,
minGlyphId,
maxGlyphId,
out complexScripts
);
}
if (result != OpenTypeLayoutResult.Success)
{
// The check failed. We abort and don't keep partial results that are not reliable
return TypographyAvailabilities.None;
}
else if (complexScripts != null)
{
for (int i = 0; i < complexScripts.Length; i++)
{
if (complexScripts[i].scriptTag == (uint)ScriptTags.CJKIdeographic)
{
typography |= TypographyAvailabilities.IdeoTypographyAvailable;
}
else
{
typography |= TypographyAvailabilities.Available;
}
}
}
if (typography != TypographyAvailabilities.None)
{
// if any of the bits were set, set TypographyAvailabilities.Avaialble bit
// as well to indicate some lookup is available.
typography |= TypographyAvailabilities.Available;
}
return typography;
}
///
/// Critical: Calls unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
internal unsafe void SetGsub(CheckedPointer gsub)
{
_layout->offGsub = SetTable(gsub);
}
///
/// Critical: Calls unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
internal unsafe void SetGpos(CheckedPointer gpos)
{
_layout->offGpos = SetTable(gpos);
}
///
/// Critical: Calls unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
internal unsafe void SetGdef(CheckedPointer gdef)
{
_layout->offGdef = SetTable(gdef);
}
///
/// Critical: Calls unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
internal unsafe void SetJstf(CheckedPointer jstf)
{
_layout->offJstf = SetTable(jstf);
}
///
/// Critical: Calls unsafe code blocks to store a list of names into a filemapping object, can result in spoofing.
///
[SecurityCritical]
private int ConvertNames(LocalizedName[] names)
{
if (names == null)
return Util.nullOffset;
unsafe
{
int cacheOffset = _cacher.Alloc(sizeof(CachedNames) + names.Length * sizeof(CachedName));
CachedNames* cachedNames = (CachedNames*)_cacher[cacheOffset];
cachedNames->numberOfNames = names.Length;
CachedName* cachedName = (CachedName*)((byte*)cachedNames + sizeof(CachedNames));
for (int i = 0; i < names.Length; ++i)
{
// GetEquivalentCulture() should always succeed, because we started with information in a
// a TrueType file, which contained an LCID.
cachedName->language = names[i].OriginalLCID;
cachedName->nameSize = Util.StringSize(names[i].Name);
cachedName->nameString = _cacher.Alloc(cachedName->nameSize);
Util.StringCopyToCheckedPointer(_cacher.GetCheckedPointer(cachedName->nameString), names[i].Name);
++cachedName;
}
return cacheOffset;
}
}
///
/// Critical: Calls unsafe code blocks to set the various fields in _layout
/// TreatAsSafe: The pointers themselves are not exposed. Also the construction
/// of _layout is tracked to ensure that it cannot be hijacked.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void AddLocalizedNames(ref TrueTypeFontDriver.ParsedNameTable nameTable, bool skipFontDifferentiation)
{
unsafe
{
_layout->version = nameTable.version;
_layout->familyNames = ConvertNames(nameTable.familyNames);
_layout->win32FamilyNames = ConvertNames(nameTable.win32FamilyNames);
_layout->faceNames = ConvertNames(nameTable.faceNames);
_layout->win32faceNames = ConvertNames(nameTable.win32faceNames);
// Run font differentiator to adjust family and face names.
FontStyle adjustedStyle = _layout->style;
FontWeight adjustedWeight = _layout->weight;
FontStretch adjustedStretch = _layout->stretch;
LocalizedName[] oldFaceNames = nameTable.faceNames;
if (!skipFontDifferentiation)
{
FontDifferentiator.AdjustFamilyAndFaceInformation(ref nameTable, ref adjustedStyle, ref adjustedWeight, ref adjustedStretch);
}
// We store only adjusted face names in cache at this point, but if we decide to expose the rest,
// this is the right place to store this information in the cache.
if (nameTable.faceNames == null)
{
// We have only legacy face names.
_layout->adjustedFaceNames = _layout->win32faceNames;
}
else
{
if (oldFaceNames == nameTable.faceNames)
{
// Font differentiator didn't modify preferred face names.
_layout->adjustedFaceNames = _layout->faceNames;
}
else
{
// Font differentiator modified preferred face names.
_layout->adjustedFaceNames = ConvertNames(nameTable.faceNames);
}
}
_layout->versionStrings = ConvertNames(nameTable.versionStrings);
_layout->copyrights = ConvertNames(nameTable.copyrights);
_layout->manufacturerNames = ConvertNames(nameTable.manufacturerNames);
_layout->trademarks = ConvertNames(nameTable.trademarks);
_layout->designerNames = ConvertNames(nameTable.designerNames);
_layout->descriptions = ConvertNames(nameTable.descriptions);
_layout->vendorUrls = ConvertNames(nameTable.vendorUrls);
_layout->designerUrls = ConvertNames(nameTable.designerUrls);
_layout->licenseDescriptions = ConvertNames(nameTable.licenseDescriptions);
_layout->sampleTexts = ConvertNames(nameTable.sampleTexts);
}
}
///
/// Critical: This code yieds accesses unsafe method ElementCacher and uses _layout
/// TreatAsSafe: This code exposes and IDictionary object for Family Names which is ok
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe IDictionary GetFamilyNameDictionary()
{
return new LocalizedNameDictionary(
_cacher,
_layout->familyNames, // primary lookup
_layout->win32FamilyNames // fall back lookup
);
}
///
/// Critical: This calls into unsafe code to retrieve the family name
/// TreatAsSafe:This information is ok to give out and returns a dictionary
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe IDictionary GetWin32FamilyNameDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->win32FamilyNames);
}
///
/// Critical: This calls into unsafe code to retrieve the face name
/// TreatAsSafe:This information is ok to give out
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe IDictionary GetFaceNameDictionary()
{
return new LocalizedNameDictionary(
_cacher,
_layout->faceNames, // primary lookup
_layout->win32faceNames // fall back lookup
);
}
///
/// Critical: This calls into unsafe code to retrieve the face name
/// TreatAsSafe:This information is ok to give out
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe IDictionary GetWin32FaceNameDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->win32faceNames);
}
///
/// Critical: This calls into unsafe code to retrieve the adjusted family name
/// TreatAsSafe: This information is ok to give out.
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe IDictionary GetAdjustedFaceNameDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->adjustedFaceNames);
}
///
/// Critical: This calls into unsafe code to retrieve the version string,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetVersionStringDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->versionStrings);
}
///
/// Critical: This calls into unsafe code to retrieve copyright information,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetCopyrightDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->copyrights);
}
///
/// Critical: This calls into unsafe code to retrieve the manufacturer name,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetManufacturerNameDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->manufacturerNames);
}
///
/// Critical: This calls into unsafe code to retrieve trademark,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetTrademarkDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->trademarks);
}
///
/// Critical: This calls into unsafe code to retrieve the Designer Name,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetDesignerNameDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->designerNames);
}
///
/// Critical: This calls into unsafe code to retrieve the dscription,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetDescriptionDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->descriptions);
}
///
/// Critical: This calls into unsafe code to retrieve the Vendor Url,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetVendorUrlDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->vendorUrls);
}
///
/// Critical: This calls into unsafe code to retrieve the dsigner uri,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetDesignerUrlDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->designerUrls);
}
///
/// Critical: This calls into unsafe code to retrieve the Licensce,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetLicenseDescriptionDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->licenseDescriptions);
}
///
/// Critical: This calls into unsafe code to retrieve the sample text
///
[SecurityCritical]
internal unsafe IDictionary GetSampleTextDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->sampleTexts);
}
#region IntMap
///
/// IntMap represents mapping from UTF32 code points to glyph indices.
/// The IDictionary part is eventually returned from public APIs and is made read-only.
/// Internal methods are used by the font driver to create the cmap.
///
internal sealed class IntMap : IDictionary
{
internal static int InitialSize()
{
unsafe { return sizeof(int) + NumberOfPlanes * sizeof(int); }
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks, can be used to spoof cmap.
///
[SecurityCritical]
internal IntMap(CheckedPointer p, ElementCacher cacher)
{
unsafe { _data = (byte*)p.Probe(0, InitialSize()); }
_cacher = cacher;
}
///
/// Critical: This code accesses unsafe code blocks
/// TreatAsSafe: This information is ok to return for get but bad for set
///
internal unsafe int CharacterCount
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
return *(int*)_data;
}
[SecurityCritical]
set
{
*(int*)_data = value;
}
}
///
/// Critical: Calls into critical code which does an unsafe operation FillMemory.
/// TreatAsSafe: Ok to expose, since GetPlanePointer will return a valid value and number of planes is a const.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void Init()
{
CharacterCount = 0;
unsafe { Util.FillMemory(GetPlanePointer(0), NumberOfPlanes * sizeof(int), EmptyPlane); }
}
///
/// Glyph count is used for validating cmap contents.
/// If we discover that glyph index we are about to set or return is outside of glyph range,
/// we throw an exception.
///
///
/// Critical: Setting this from unauthorized functions increases the risk of failure in functions that
/// work with _glyphcount
///
[SecurityCritical]
internal void SetGlyphCount(ushort glyphCount)
{
_glyphCount = new SecurityCriticalDataForSet(glyphCount);
}
///
/// Critical: This code calls into unsafe code blocks and gets a pointer back. It uses
/// this to initialize a character entry. Can be used to spoof cmap.
///
[SecurityCritical]
internal void SetCharacterEntry(int i, ushort value)
{
// Some fonts have cmap entries that point to glyphs outside of the font.
// Just skip such entries.
if (value >= _glyphCount.Value)
return;
int plane = CreatePlane(i >> 16);
int page = CreatePage(plane, i >> 8 & 0xff);
unsafe
{
ushort* characterEntry = GetGlyphPointer(page, i & 0xff);
if (*characterEntry == 0 && value != 0)
++CharacterCount;
*characterEntry = value;
}
}
///
/// Critical: Calls into unsafe code block and returns a pointer
///
[SecurityCritical]
private unsafe int* GetPlanePointer(int i)
{
if (i < 0 || i >= NumberOfPlanes)
throw new ArgumentOutOfRangeException("c", SR.Get(SRID.CodePointOutOfRange, i));
return ((int*)_data) + i + 1;
}
///
/// Critical: Calls into unsafe code
/// TreatAsSafe: This code does not expose the pointer and is safe to call
///
[SecurityCritical, SecurityTreatAsSafe]
private unsafe int GetPlane(int i)
{
return *GetPlanePointer(i);
}
///
/// Critical: Calls into unsafe code block and exposes a pointer
///
[SecurityCritical]
private unsafe int* GetPagePointer(int plane, int i)
{
Invariant.Assert(0 <= i && i < NumberOfPages);
return ((int*)_cacher[plane]) + i;
}
///
/// Critical: Calls into unsafe code block
/// TreatAsSafe: This code does not expose the pointer and is safe to call
///
[SecurityCritical, SecurityTreatAsSafe]
private unsafe int GetPage(int plane, int i)
{
return *GetPagePointer(plane, i);
}
///
/// Critical: Calls into unsafe code block
///
[SecurityCritical]
private unsafe ushort* GetGlyphPointer(int page, int key)
{
Invariant.Assert(0 <= key && key < PageSize);
return (ushort*)_cacher[page] + key;
}
///
/// Critical: Calls into unsafe code block
/// TreatAsSafe: This code does not expose the pointer and is safe to call
///
[SecurityCritical, SecurityTreatAsSafe]
private unsafe ushort GetGlyph(int page, int key)
{
return *GetGlyphPointer(page, key);
}
///
/// Critical: Calls into unsafe code block
/// TreatAsSafe: Ok to expose this function. It uses _cacher which is
/// bounds checked.
///
[SecurityCritical, SecurityTreatAsSafe]
private int CreatePlane(int i)
{
unsafe
{
int* plane = GetPlanePointer(i);
if (*plane == EmptyPlane)
{
*plane = _cacher.Alloc(NumberOfPages * sizeof(int));
Util.FillMemory(_cacher[*plane], NumberOfPages * sizeof(int), EmptyPage);
}
return *plane;
}
}
///
/// Critical: Calls into unsafe code block
/// TreatAsSafe: Ok to expose, it used cacher which is bounds checked, it
/// uses GetPlanePointer which uses a const int and hence the index into
/// the array cannot be breached.
///
[SecurityCritical, SecurityTreatAsSafe]
private int CreatePage(int plane, int i)
{
unsafe
{
int* page = GetPagePointer(plane, i);
if (*page == EmptyPage)
{
*page = _cacher.Alloc(PageSize * sizeof(short));
Util.FillMemory(_cacher[*page], PageSize * sizeof(short), 0);
}
return *page;
}
}
#region IDictionary Members
public void Add(int key, ushort value)
{
throw new NotSupportedException();
}
public bool ContainsKey(int key)
{
ushort glyphIndex;
return TryGetValue(key, out glyphIndex);
}
public ICollection Keys
{
get
{
// This is inefficient, but Keys is not used too often to justify optimizing it.
int[] keys = new int[Count];
int i = 0;
foreach (KeyValuePair pair in this)
{
keys[i++] = pair.Key;
}
Debug.Assert(i == Count);
return keys;
}
}
public bool Remove(int key)
{
throw new NotSupportedException();
}
public bool TryGetValue(int key, out ushort value)
{
try
{
int plane = GetPlane(key >> 16);
if (plane != EmptyPlane)
{
int page = GetPage(plane, key >> 8 & 0xff);
if (page != EmptyPage)
{
ushort glyphIndex = GetGlyph(page, key & 0xff);
Debug.Assert(glyphIndex < _glyphCount.Value);
if (glyphIndex != 0)
{
value = glyphIndex;
return true;
}
}
}
}
catch (ArgumentOutOfRangeException)
{
// GetPlane() throws ArgumentOutOfRangeException if the key value is outside of the valid Unicode range.
// Since IDictionary semantics requre returning false for non-existent keys, we need to catch it.
}
value = new ushort();
return false;
}
public ICollection Values
{
get
{
// This is inefficient, but Values is not used too often to justify optimizing it.
ushort[] values = new ushort[Count];
int i = 0;
foreach (KeyValuePair pair in this)
{
values[i++] = pair.Value;
}
Debug.Assert(i == Count);
return values;
}
}
ushort IDictionary.this[int i]
{
get
{
ushort glyphIndex;
if (!TryGetValue(i, out glyphIndex))
throw new KeyNotFoundException();
return glyphIndex;
}
set
{
throw new NotSupportedException();
}
}
#endregion
#region ICollection> Members
public void Add(KeyValuePair item)
{
throw new NotSupportedException();
}
public void Clear()
{
throw new NotSupportedException();
}
public bool Contains(KeyValuePair item)
{
return ContainsKey(item.Key);
}
public void CopyTo(KeyValuePair[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException("array");
}
if (array.Rank != 1)
{
throw new ArgumentException(SR.Get(SRID.Collection_BadRank));
}
// The extra "arrayIndex >= array.Length" check in because even if _collection.Count
// is 0 the index is not allowed to be equal or greater than the length
// (from the MSDN ICollection docs)
if (arrayIndex < 0 || arrayIndex >= array.Length || (arrayIndex + Count) > array.Length)
{
throw new ArgumentOutOfRangeException("arrayIndex");
}
foreach (KeyValuePair pair in this)
array[arrayIndex++] = pair;
}
public int Count
{
get { return CharacterCount; }
}
public bool IsReadOnly
{
get { return true; }
}
public bool Remove(KeyValuePair item)
{
throw new NotSupportedException();
}
#endregion
#region IEnumerable> Members
public IEnumerator> GetEnumerator()
{
for (int i = 0; i < NumberOfPlanes; ++i)
{
int plane = GetPlane(i);
if (plane != EmptyPlane)
{
for (int j = 0; j < NumberOfPages; ++j)
{
int page = GetPage(plane, j);
if (page != EmptyPage)
{
for (int k = 0; k < PageSize; ++k)
{
ushort glyph = GetGlyph(page, k);
if (glyph != 0)
yield return new KeyValuePair(
(i << 16) | (j << 8) | k,
glyph
);
}
}
}
}
}
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable>)this).GetEnumerator();
}
#endregion
///
/// Critical:This code holds a pointer and is unsafe to let out
///
[SecurityCritical]
private unsafe byte* _data;
private ElementCacher _cacher;
///
/// Critical:This code is used to dereference a memory location and is hence critical
///
private SecurityCriticalDataForSet _glyphCount;
private const int NumberOfPlanes = 17;
private const int NumberOfPages = 256;
private const int PageSize = 256;
private const int EmptyPlane = Util.nullOffset;
private const int EmptyPage = Util.nullOffset;
}
internal enum RenderingHints : ushort
{
Regular = 0,
LegacyEastAsian = 1
};
#endregion
//------------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
private ElementCacher _cacher;
///
/// Critical: Holds reference to an unmanaged pointer
///
[SecurityCritical]
private unsafe Layout* _layout;
private IntMap _cmap;
private int _faceIndex;
private long _timeStamp;
private FontSource _fontSource;
// 'locl' feature which is language sensitive
private static readonly uint[] LoclFeature = new uint[]
{
(uint)OpenTypeTags.locl
};
// common features for fast text
// They are insensitive to languages
private static readonly uint[] RequiredTypographyFeatures = new uint[]
{
(uint)OpenTypeTags.ccmp,
(uint)OpenTypeTags.rlig,
(uint)OpenTypeTags.liga,
(uint)OpenTypeTags.clig,
(uint)OpenTypeTags.calt,
(uint)OpenTypeTags.kern,
(uint)OpenTypeTags.mark,
(uint)OpenTypeTags.mkmk
};
// All required features
private static readonly uint[] RequiredFeatures = new uint[]
{
(uint)OpenTypeTags.locl,
(uint)OpenTypeTags.ccmp,
(uint)OpenTypeTags.rlig,
(uint)OpenTypeTags.liga,
(uint)OpenTypeTags.clig,
(uint)OpenTypeTags.calt,
(uint)OpenTypeTags.kern,
(uint)OpenTypeTags.mark,
(uint)OpenTypeTags.mkmk
};
private static readonly UnicodeRange[] fastTextRanges = new UnicodeRange[]
{
new UnicodeRange(0x20 , 0x7e ), // basic latin
new UnicodeRange(0xA1 , 0xFF ), // latin-1 supplement,
new UnicodeRange(0x0100, 0x17F ), // latin extended-A
new UnicodeRange(0x0180, 0x024F), // latin extended-B
new UnicodeRange(0x1E00, 0x1EFF), // latin extended additional (Vietnamese precomposed)
new UnicodeRange(0x3040, 0x309F), // hiragana
new UnicodeRange(0x30A0, 0x30FF) // kana
};
#endregion Private Fields
#region Private Types
// LocalizedNameDictionary implements IDictionary in terms
// of one or two arrays of CachedName structures. Each array is assumed to be
// sorted by LCID.
//
// If two arrays are specified, we use the first array for primary lookup and
// the second array as a fallback. We use integer indexes internally to refer to
// culture/name pairs. We can tell from its value whether an index refers to the
// first or second array (see the GetCachedName method). An index must be less
// than the *sum* of the sizes of the two arrays, specified by the Limit property.
//
// If the fallback array contains keys which are also in the primary array, we
// don't want to enumerate them or include them in the Count. For this reason,
// Count may be less than Limit. We use the IsDuplicateKey method during enumeration
// to determine whether to skip an index, and we use GetCountWithoutDuplicates to
// calculate the count.
//
private unsafe struct LocalizedNameDictionary : IDictionary
{
private ElementCacher _cacher;
// primary table of strings
///
/// Critical:This holds reference to a pointer
///
[SecurityCritical]
private CachedName* _cachedName;
///
/// Critical:This holds data that is used to make decisions on dereferencing a pointer
///
private SecurityCriticalDataForSet _numberOfNames;
// secondary table of strings
///
/// Critical:This holds reference to a pointer
///
[SecurityCritical]
private CachedName* _cachedNameFallback;
///
/// Critical:This holds data that is used to make decisions on dereferencing a pointer
///
private SecurityCriticalDataForSet _numberOfNamesFallback;
// number of strings, excluding duplicates; may be computed lazily
private int _count;
///
/// Construct a dictionary using a single array of cached strings.
///
internal LocalizedNameDictionary(ElementCacher cacher, int cacheOffset)
:
this(cacher, cacheOffset, Util.nullOffset)
{
}
///
/// Construct a dictionary with a primary and fallback array of cached strings.
///
///
/// Critical:This accesses elementcacher and is unsafe
/// TreatAsSafe: Calling this is ok also it does not expose the pointers.
///
[SecurityCritical, SecurityTreatAsSafe]
internal LocalizedNameDictionary(ElementCacher cacher, int cacheOffset, int cacheOffsetFallback)
{
_cacher = cacher;
// It's quite common for a font to provide only a Win32-compatible table.
// If there's only one table we always want to make it the primary table
// for reasons of efficiency.
if (cacheOffset == Util.nullOffset)
{
cacheOffset = cacheOffsetFallback;
cacheOffsetFallback = Util.nullOffset;
}
// Initialize the primary table.
if (cacheOffset != Util.nullOffset)
{
CachedNames* cachedNames = (CachedNames*)_cacher[cacheOffset];
_cachedName = (CachedName*)((byte*)cachedNames + sizeof(CachedNames));
_numberOfNames = new SecurityCriticalDataForSet(cachedNames->numberOfNames);
}
else
{
_cachedName = null;
_numberOfNames = new SecurityCriticalDataForSet(0);
}
// Initialize the secondary table.
if (cacheOffsetFallback != Util.nullOffset)
{
CachedNames* cachedNames = (CachedNames*)_cacher[cacheOffsetFallback];
_cachedNameFallback = (CachedName*)((byte*)cachedNames + sizeof(CachedNames));
_numberOfNamesFallback = new SecurityCriticalDataForSet(cachedNames->numberOfNames);
}
else
{
_cachedNameFallback = null;
_numberOfNamesFallback = new SecurityCriticalDataForSet(0);
}
// Initialize count.
if (_numberOfNamesFallback.Value == 0 || _numberOfNames.Value == 0)
{
// If there's only one non-empty table then we don't need to check
// for duplicate keys.
_count = _numberOfNamesFallback.Value + _numberOfNames.Value;
}
else
{
// Compute count lazily because we need to check for duplicates.
_count = -1;
}
}
///
/// Critical:This accesses elementcacher and is unsafe it also returns a pointer
///
[SecurityCritical]
private CachedName* GetCachedName(int index)
{
// We should be checking bounds farther up the stack, e.g., in Enumerator class.
Invariant.Assert(index >= 0 && index < Limit);
// Look in primary or secondary table depending on value of index.
if (index < _numberOfNames.Value)
{
return _cachedName + index;
}
else
{
return _cachedNameFallback + (index - _numberOfNames.Value);
}
}
///
/// Critical:This accesses elementcacher which is unsafe
/// TreatAsSafe: This code accesses GetCachedName which returns a pointer ,returning this data is ok
///
[SecurityCritical, SecurityTreatAsSafe]
private int GetLCID(int index)
{
return GetCachedName(index)->language;
}
///
/// Critical:This accesses elementcacher and GetCachedName
/// TreatAsSafe: Returning this data is ok also the two functions
/// safeguard against invalid values
///
[SecurityCritical, SecurityTreatAsSafe]
private string GetName(int index)
{
CachedName* cachedName = GetCachedName(index);
return Util.StringCopyFromCheckedPointer(_cacher.GetCheckedPointer(cachedName->nameString), cachedName->nameSize);
}
///
/// Look up an LCID in the specified table.
///
/// LCID to look up.
/// Offset to add to returned index if found.
/// Array of CachedName structures.
/// Size of the array.
/// Index of match (plus offset) or -1 if not found.
///
/// Critical:This accesses elementcacher and is unsafe it also accepts a pointer
///
[SecurityCritical]
private int FindLCID(int lcid, int offset, CachedName* cachedName, int numberOfNames)
{
// Look up name based on LCID using binary search.
int min = 0, lim = numberOfNames;
while (min < lim)
{
int i = (min + lim) / 2;
int key = cachedName[i].language;
if (lcid < key)
lim = i;
else if (lcid > key)
min = i + 1;
else
return i + offset;
}
return -1;
}
///
/// Critical: This code calls accesses _cachedName
/// TreatAsSafe: The pointer is not exposed and LCID is ok to give out
///
[SecurityCritical, SecurityTreatAsSafe]
private int FindPrimaryLCID(int lcid)
{
return FindLCID(
lcid, // LCID to look for
0, // no offset
_cachedName, // look in primary array
_numberOfNames.Value);
}
///
/// Critical: This code calls accesses _cachedNameFallBack
/// TreatAsSafe: The pointer is not exposed and the call to FindLCID is safe
///
[SecurityCritical, SecurityTreatAsSafe]
private int FindSecondaryLCID(int lcid)
{
return FindLCID(
lcid, // LCID to look for
_numberOfNames.Value, // offset added to result if found
_cachedNameFallback, // look in secondary array
_numberOfNamesFallback.Value);
}
///
/// Look up an LCID in both tables.
///
private int FindLCID(int lcid)
{
// Look up in primary name table.
int i = FindPrimaryLCID(lcid);
// Fall back to secondary name table.
if (i < 0 && _numberOfNamesFallback.Value != 0)
{
i = FindSecondaryLCID(lcid);
}
return i;
}
///
/// Returns the name for the given LCID or null if not found.
///
internal string GetNameFromLCID(int lcid)
{
int i = FindLCID(lcid);
return (i >= 0) ? GetName(i) : null;
}
///
/// Determines whether the specified index is a duplicate key, which
/// should be skipped during enumeration.
///
///
/// Critical:This accesses elementcacher and is unsafe
/// TreatAsSafe: Calling this is ok since all it returns is whether a key is duped
///
[SecurityCritical, SecurityTreatAsSafe]
internal bool IsDuplicateKey(int index)
{
// We should be checking bounds higher up the stack, e.g., in Enumerator class.
Invariant.Assert(index >= 0 && index < Limit);
// Keys in the primary table are not duplicates by definition.
if (index < _numberOfNames.Value)
return false;
// If we find the LCID in the primary table then it's a duplicate.
int lcid = _cachedNameFallback[index - _numberOfNames.Value].language;
return FindPrimaryLCID(lcid) >= 0;
}
///
/// Computes the count (if not already known). The count excludes
/// indexes for which IsDuplicateKey is true.
///
///
/// Critical:This accesses elementcacher and is unsafe
/// TreatAsSafe: Calling this is ok does not return the critical data
///
[SecurityCritical, SecurityTreatAsSafe]
private int GetCountWithoutDuplicates()
{
if (_count < 0)
{
// include all keys in the primary table
_count = _numberOfNames.Value;
// iterate over the secondary table including only non-duplicates
for (int i = 0; i < _numberOfNamesFallback.Value; ++i)
{
int lcid = _cachedNameFallback[i].language;
if (FindPrimaryLCID(lcid) < 0)
{
// not a duplicate
_count++;
}
}
}
return _count;
}
///
/// Limit value for indexes. May differ from Count because the range
/// of indexes may include duplicate keys which are not enumerated or
/// included in the Count.
///
internal int Limit
{
get
{
return _numberOfNames.Value + _numberOfNamesFallback.Value;
}
}
#region IDictionary Members
bool IDictionary.Remove(CultureInfo key)
{
throw new NotSupportedException();
}
void IDictionary.Add(CultureInfo key, string value)
{
throw new NotSupportedException();
}
ICollection IDictionary.Keys
{
get
{
// OK, this is very slow, but semantically correct.
// Keys are not used that often anyway.
CultureInfo[] keys = new CultureInfo[GetCountWithoutDuplicates()];
// Enumerator will automatically skip duplicates.
int i = 0;
foreach (KeyValuePair pair in this)
{
keys[i++] = pair.Key;
}
// Debug check for enumeration logic.
Debug.Assert(i == keys.Length);
return keys;
}
}
string IDictionary.this[CultureInfo key]
{
get
{
return GetNameFromLCID(key.LCID);
}
set
{
throw new NotSupportedException();
}
}
bool IDictionary.TryGetValue(CultureInfo key, out string value)
{
int i = FindLCID(key.LCID);
if (i >= 0)
{
value = GetName(i);
return true;
}
else
{
value = null;
return false;
}
}
ICollection IDictionary.Values
{
get
{
// OK, this is very slow, but semantically correct.
// Values are not used that often anyway.
string[] values = new string[GetCountWithoutDuplicates()];
// Enumerator will automatically skip duplicates.
int i = 0;
foreach (KeyValuePair pair in this)
{
values[i++] = pair.Value;
}
// Debug check for enumeration logic.
Debug.Assert(i == values.Length);
return values;
}
}
bool IDictionary.ContainsKey(CultureInfo key)
{
return FindLCID(key.LCID) >= 0;
}
#endregion
#region ICollection> Members
bool ICollection>.Contains(KeyValuePair item)
{
int i = FindLCID(item.Key.LCID);
return (i >= 0 && String.Compare(GetName(i), item.Value, StringComparison.OrdinalIgnoreCase) == 0);
}
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 GetCountWithoutDuplicates();
}
}
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
//
// Enumerator class for LocalizedNameTable. Internally, we represent the current
// position as an integer index. We enumerate by incrementing the index, taking
// into account that some indexes are invalid (i.e., duplicate keys).
//
private class Enumerator : IEnumerator>
{
private int _currentIndex;
private LocalizedNameDictionary _parent;
public Enumerator(LocalizedNameDictionary parent)
{
_parent = parent;
_currentIndex = -1;
}
private void FailIfOutOfRange()
{
if (_currentIndex < 0)
throw new InvalidOperationException(SR.Get(SRID.Enumerator_NotStarted));
if (_currentIndex >= _parent.Limit)
throw new InvalidOperationException(SR.Get(SRID.Enumerator_ReachedEnd));
}
private CultureInfo GetCurrentCulture()
{
return CultureInfo.GetCultureInfo(_parent.GetLCID(_currentIndex));
}
private string GetCurrentName()
{
return _parent.GetName(_currentIndex);
}
#region IEnumerator> Members
public bool MoveNext()
{
// We may need to increment the index more than once to skip
// duplicate keys.
do
{
++_currentIndex;
// stop if we're at the end
if (_currentIndex >= _parent.Limit)
{
// prevent cycling
_currentIndex = _parent.Limit;
return false;
}
} while (_parent.IsDuplicateKey(_currentIndex));
return true;
}
KeyValuePair IEnumerator>.Current
{
get
{
FailIfOutOfRange();
return new KeyValuePair(GetCurrentCulture(), GetCurrentName());
}
}
object IEnumerator.Current
{
get
{
return ((IEnumerator>)this).Current;
}
}
public void Reset()
{
_currentIndex = -1;
}
#endregion
#region IDisposable Members
public void Dispose() { }
#endregion
}
}
#endregion
}
///
/// An implementation of IOpenTypeFont which only provides GSUB and GPOS tables
/// It is used by OTLS API to determine the optimizable script.
///
///
/// OTLS API always accepts IOpenTypeFont as input parameter. To be consistent, we
/// implement this IOpenTypeFont just for OpenTypeLayout.GetComplexLanguangeList(..) method.
///
internal sealed class GsubGposTables : IOpenTypeFont
{
///
/// Critical: Gsub() and Gpos() return pointers
///
[SecurityCritical]
internal GsubGposTables(FontFaceLayoutInfo layout)
{
_layout = layout;
unsafe
{
_gsubTable = new FontTable(_layout.Gsub());
_gposTable = new FontTable(_layout.Gpos());
}
}
///
/// Returns font table data
///
public FontTable GetFontTable(OpenTypeTags TableTag)
{
switch (TableTag)
{
case OpenTypeTags.GSUB:
{
return _gsubTable;
}
case OpenTypeTags.GPOS:
{
return _gposTable;
}
default:
{
throw new NotSupportedException();
}
}
}
///
/// Returns glyph coordinate
///
public LayoutOffset GetGlyphPointCoord(ushort Glyph, ushort PointIndex)
{
throw new NotSupportedException();
}
///
/// Returns cache for layout table. If cache not found, return null Checked pointer
///
///
/// Critical: Calls critical code
///
[SecurityCritical]
public CheckedPointer GetTableCache(OpenTypeTags tableTag)
{
return _layout.GetTableCache(tableTag);
}
///
/// Allocate space for layout table cache.
///
///
/// Critical: Calls critical code
///
[SecurityCritical]
public CheckedPointer AllocateTableCache(OpenTypeTags tableTag, int size)
{
return _layout.AllocateTableCache(tableTag, size);
}
private FontTable _gsubTable;
private FontTable _gposTable;
private FontFaceLayoutInfo _layout;
}
///
/// A unicode range identified by a pair of first and last unicode code point
///
internal struct UnicodeRange
{
internal UnicodeRange(int first, int last)
{
firstChar = first;
lastChar = last;
}
internal int firstChar;
internal int lastChar;
}
///
/// Major language targetted for optimization
///
internal static class MajorLanguages
{
///
/// check if input langSys is considered a major language.
///
/// true if it is a major language
internal static bool Contains(LanguageTags langSys)
{
if (langSys == LanguageTags.Default) return true;
for (int i = 0; i < majorLanguages.Length; i++)
{
if (majorLanguages[i].LangSys == langSys)
return true;
}
return false;
}
///
/// check if input culture is considered a major language.
///
/// true if it is a major language
internal static bool Contains(CultureInfo culture)
{
if (culture == null) return false;
// explicitly check for InvariantCulture. We don't need to check for its parent.
if (culture == CultureInfo.InvariantCulture) return true;
for (int i = 0; i < majorLanguages.Length; i++)
{
if (majorLanguages[i].Culture.Equals(culture)
|| majorLanguages[i].Culture.Equals(culture.Parent)
)
{
return true;
}
}
return false;
}
// major languages
private static readonly LangSysCulturePair[] majorLanguages = new LangSysCulturePair[]
{
new LangSysCulturePair(new CultureInfo("en"), LanguageTags.English), // English neutral culture
new LangSysCulturePair(new CultureInfo("de"), LanguageTags.German), // German neutral culture
new LangSysCulturePair(new CultureInfo("ja"), LanguageTags.Japanese) // Japanese neutral culture
};
private struct LangSysCulturePair
{
internal LangSysCulturePair(CultureInfo culture, LanguageTags langSys)
{
Culture = culture;
LangSys = langSys;
}
internal readonly CultureInfo Culture;
internal readonly LanguageTags LangSys;
}
}
///
/// An enum flag indicating the availabilities of various open type
/// look ups.
///
///
/// The enum is used to determine whether fast path is applicable.
/// Ideo refers to Ideographs
/// FastText refers to Other optimizable text
/// We keep a minimum set of flags here to allow us reliably optimize
/// the most common inputs. It is not to prevent under-optimization for
/// all cases.
///
[Flags]
internal enum TypographyAvailabilities
{
///
/// No required OpenType typography features is available
///
None = 0,
///
/// There are some lookup available for required typography
/// features
///
Available = 1,
///
/// There are some lookup available for required typography features
/// for Ideographic script.
///
IdeoTypographyAvailable = 2,
///
/// There are lookup available for required typography features
/// for fast text
///
FastTextTypographyAvailable = 4,
///
/// There are localized form available for major Ui lanaguages for fast text
///
/// MajorLanguages class
FastTextMajorLanguageLocalizedFormAvailable = 8,
///
/// There are localized form for non major Ui language available for fast text
///
FastTextExtraLanguageLocalizedFormAvailable = 16,
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Description: The FontFaceLayoutInfo class
//
// History:
// 07/23/2003 : mleonov - Big rewrite to change cache structure
//
//---------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Security;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Media;
using System.Runtime.InteropServices;
using MS.Win32;
using MS.Utility;
using MS.Internal;
using MS.Internal.FontFace;
using MS.Internal.Shaping;
using MS.Internal.PresentationCore;
namespace MS.Internal.FontCache
{
[FriendAccessAllowed]
internal sealed class FontFaceLayoutInfo : IFontCacheElement
{
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
#region Constructors
internal FontFaceLayoutInfo(FontSource fontSource, int faceIndex)
{
_fontSource = fontSource;
_faceIndex = faceIndex;
_timeStamp = _fontSource.SkipLastWriteTime();
}
///
/// Critical - Calls into the critical RetrieveKey method.
///
[SecurityCritical]
internal FontFaceLayoutInfo(CheckedPointer key)
{
RetrieveKey(key);
}
#endregion Constructors
// layout: struct | FileName | 4byte aligned CMAP (17 int pointers to planes)
// the structure size should be aligned on 2 byte boundary, because file name is stored
// immediately after the structure.
// As usual, individual structure members need to be properly aligned.
[StructLayout(LayoutKind.Explicit, Size = Layout.keyOffset + 8)]
private struct Layout
{
[FieldOffset(0)]
internal ushort designEmHeight;
[FieldOffset(2)]
internal ushort glyphCount;
[FieldOffset(4)]
internal ushort designCellAscent;
[FieldOffset(6)]
internal ushort designCellDescent;
[FieldOffset(8)]
internal short fontContrastAdjustment;
[FieldOffset(10)]
internal short xHeight;
[FieldOffset(12)]
internal short capsHeight;
[FieldOffset(14)]
internal ushort blankGlyph;
[FieldOffset(16)]
internal ushort invalidGlyph;
[FieldOffset(18)]
internal ushort underlineSize;
[FieldOffset(20)]
internal short underlinePosition;
[FieldOffset(22)]
internal ushort strikeThroughSize;
[FieldOffset(24)]
internal short strikeThroughPosition;
[FieldOffset(26)]
internal ushort symbol;
// RenderingHints is a ushort enum, so 2 bytes are enough.
[FieldOffset(28)]
internal RenderingHints renderingHints;
[FieldOffset(30)]
internal ushort embeddingRights;
[FieldOffset(32)]
internal long timeStamp;
[FieldOffset(40)]
internal long gsubScripts;
[FieldOffset(48)]
internal long gposScripts;
[FieldOffset(56)]
internal int offAdvanceWidths; // offset of the advance width array
[FieldOffset(60)]
internal int offGsub; // ditto for GSUB
[FieldOffset(64)]
internal int offGpos; // ditto for GPOS
[FieldOffset(68)]
internal int offGdef; // ditto for GDEF
[FieldOffset(72)]
internal int offJstf; // ditto for JSTF
[FieldOffset(76)]
internal int familyNames;
[FieldOffset(80)]
internal int win32FamilyNames;
[FieldOffset(84)]
internal int faceNames;
[FieldOffset(88)]
internal int win32faceNames;
[FieldOffset(92)]
internal int versionStrings;
[FieldOffset(96)]
internal int copyrights;
[FieldOffset(100)]
internal int manufacturerNames;
[FieldOffset(104)]
internal int trademarks;
[FieldOffset(108)]
internal int designerNames;
[FieldOffset(112)]
internal int descriptions;
[FieldOffset(116)]
internal int vendorUrls;
[FieldOffset(120)]
internal int designerUrls;
[FieldOffset(124)]
internal int licenseDescriptions;
[FieldOffset(128)]
internal int sampleTexts;
[FieldOffset(132)]
internal FontStyle style;
[FieldOffset(136)]
internal FontWeight weight;
[FieldOffset(140)]
internal FontStretch stretch;
[FieldOffset(144)]
internal double version;
[FieldOffset(152)]
internal int offGaspRanges;
[FieldOffset(156)]
internal TypographyAvailabilities typographyAvailabilities;
[FieldOffset(160)]
internal FontTechnology fontTechnology;
[FieldOffset(164)]
internal int offGsubCache;
[FieldOffset(168)]
internal int gsubCacheLength;
[FieldOffset(172)]
internal int offGposCache;
[FieldOffset(176)]
internal int gposCacheLength;
// family and face names adjusted by the font differentiator
[FieldOffset(180)]
internal int adjustedFaceNames;
// this should be equal to the offset immediately after the previous field
internal const int keyOffset = 184;
// key starts here
[FieldOffset(keyOffset)]
internal int faceIndex;
[FieldOffset(keyOffset + 4)]
internal int uriStringSizeInBytes;
internal static CheckedPointer GetUriString(CheckedPointer This)
{
unsafe
{
return This + sizeof(Layout);
}
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks
/// TreatAsSafe: It advances the pointer by the requisite number of bytes .
/// Calls to probe and '+ are bounds checked.
///
[SecurityCritical, SecurityTreatAsSafe]
internal static CheckedPointer GetCmap(CheckedPointer This)
{
unsafe
{
Layout* l = (Layout*)This.Probe(0, sizeof(Layout));
return GetUriString(This) + Util.Align4(l->uriStringSizeInBytes);
}
}
}
[StructLayout(LayoutKind.Explicit, Size = 12)]
private struct CachedName
{
[FieldOffset(0)]
internal int language;
[FieldOffset(4)]
internal int nameSize;
///
/// Offset to the name string
///
[FieldOffset(8)]
internal int nameString;
}
[StructLayout(LayoutKind.Explicit, Size = 4)]
private struct CachedNames
{
[FieldOffset(0)]
internal int numberOfNames;
// followed by array of CachedName structures
}
internal FontSource FontSource
{
get
{
return _fontSource;
}
}
internal int FaceIndex
{
get
{
return _faceIndex;
}
}
int IFontCacheElement.Type
{
get
{
return 3;
}
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks
/// TreatAsSafe: This stores data about the font into a pointer. This data is safe to
/// expose and the pointer operations are bounds checked.
///
[SecurityCritical, SecurityTreatAsSafe]
private void StoreKeyInternal(CheckedPointer d, out int realSize)
{
unsafe
{
int* k = (int*)d.Probe(0, sizeof(int));
*k = _faceIndex;
realSize = sizeof(int);
d += sizeof(int);
realSize += Util.StringAndLengthCopyToCheckedPointer(d, FontSource.GetUriString());
}
}
void IFontCacheElement.StoreKey(CheckedPointer d, out int realSize)
{
Debug.Assert(!_fontSource.IsAppSpecific);
StoreKeyInternal(d, out realSize);
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks.
/// Calls into the critical FontSource constructor.
///
[SecurityCritical]
public void RetrieveKey(CheckedPointer p)
{
unsafe
{
_faceIndex = *(int*)p.Probe(0, sizeof(int));
string fileName = Util.StringAndLengthCopyFromCheckedPointer(p + sizeof(int));
_fontSource = new FontSource(new Uri(fileName, UriKind.Absolute), false);
}
_timeStamp = _fontSource.SkipLastWriteTime();
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks
/// TreatAsSafe: The call to probe is bounds checked and also the pointers returned
/// are used locally and not passed around.
///
[SecurityCritical, SecurityTreatAsSafe]
bool IFontCacheElement.Match(CheckedPointer p)
{
unsafe
{
Layout* l = (Layout*)p.Probe(0, sizeof(Layout));
string fontUriString = FontSource.GetUriString();
if (Util.StringSize(fontUriString) != l->uriStringSizeInBytes)
return false;
if (_timeStamp != l->timeStamp)
return false;
if (_faceIndex != l->faceIndex)
return false;
return Util.StringEqualIgnoreCase(Layout.GetUriString(p), fontUriString);
}
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks. It
/// accesses _layout which is a pointer
/// TreatAsSafe: This data is ok to give out, also storing the checked pointer is
/// ok
///
[SecurityCritical, SecurityTreatAsSafe]
void IFontCacheElement.GetData(CheckedPointer block, ElementCacher cacher)
{
unsafe
{
_cacher = cacher;
_layout = (Layout*)block.Probe(0, sizeof(Layout));
_timeStamp = _layout->timeStamp;
_cmap = new IntMap(Layout.GetCmap(block), _cacher);
_cmap.SetGlyphCount(_layout->glyphCount);
}
}
///
/// Critical - As this function accesses FileName that contains absolute font path.
/// Safe - As this is only used to compute the cache element size.
///
int IFontCacheElement.Size
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe
{
return
sizeof(Layout) +
Util.Align4(Util.StringSize(FontSource.GetUriString())) +
IntMap.InitialSize();
}
}
}
bool IFontCacheElement.IsAppSpecific
{
get
{
return _fontSource.IsAppSpecific;
}
}
int IFontCacheElement.GetHashCode()
{
int hash = FontSource.GetHashCode();
hash = HashFn.HashMultiply(hash) + _faceIndex;
return HashFn.HashScramble(hash);
}
///
/// Critical - As this function obtains UnmanagedMemoryStream from FontSource.Pin()
/// which is Critical.
/// Safe - As this is only used to add font information to the cache which is safe.
///
[SecurityCritical, SecurityTreatAsSafe]
void IFontCacheElement.AddToCache(CheckedPointer newPointer, ElementCacher cacher)
{
_cacher = cacher;
int realSize;
StoreKeyInternal(newPointer + Layout.keyOffset, out realSize);
unsafe
{
_layout = (Layout*)newPointer.Probe(0, sizeof(Layout));
Debug.Assert(_timeStamp != 0);
_layout->timeStamp = _timeStamp;
// initialize optional table pointers
_layout->offGsub =
_layout->offGpos =
_layout->offGdef =
_layout->offJstf =
_layout->offGaspRanges =
Util.nullOffset;
_cmap = new IntMap(Layout.GetCmap(newPointer), _cacher);
}
_cmap.Init();
UnmanagedMemoryStream pinnedFontSource = _fontSource.GetUnmanagedStream();
try
{
TrueTypeFontDriver ttd = new TrueTypeFontDriver(pinnedFontSource, _fontSource.Uri);
ttd.SetFace(_faceIndex);
ttd.GetLayoutFontFaceInfo(this);
ttd.GetShapingFontFaceInfo(this);
}
catch (SEHException e)
{
throw Util.ConvertInPageException(_fontSource, e);
}
finally
{
pinnedFontSource.Close();
}
unsafe
{
_layout->offGsubCache = Util.nullOffset;
_layout->offGposCache = Util.nullOffset;
CreateOpenTypeLayoutCache();
_layout->typographyAvailabilities = ComputeTypographyAvailabilities();
}
}
///
/// Critical: Returns a writeable cmap.
///
internal IntMap CharacterMap
{
[SecurityCritical]
get
{
return _cmap;
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This code exposes DesignEmHeight which is ok to expose
/// The risk here is if _layout is null, but that is mitigated by probe.
///
internal ushort DesignEmHeight
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->designEmHeight; }
}
[SecurityCritical]
set
{
unsafe { _layout->designEmHeight = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal ushort DesignCellAscent
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->designCellAscent; }
}
[SecurityCritical]
set
{
unsafe { _layout->designCellAscent = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal ushort DesignCellDescent
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->designCellDescent; }
}
[SecurityCritical]
set
{
unsafe { _layout->designCellDescent = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal short xHeight
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->xHeight; }
}
[SecurityCritical]
set
{
unsafe { _layout->xHeight = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal short CapsHeight
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->capsHeight; }
}
[SecurityCritical]
set
{
unsafe { _layout->capsHeight = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal ushort BlankGlyph
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->blankGlyph; }
}
[SecurityCritical]
set
{
unsafe { _layout->blankGlyph = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
///
internal ushort InvalidGlyph
{
[SecurityCritical]
set
{
unsafe { _layout->invalidGlyph = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal ushort UnderlineThickness
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->underlineSize; }
}
[SecurityCritical]
set
{
unsafe { _layout->underlineSize = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal short UnderlinePosition
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->underlinePosition; }
}
[SecurityCritical]
set
{
unsafe { _layout->underlinePosition = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal ushort StrikethroughThickness
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->strikeThroughSize; }
}
[SecurityCritical]
set
{
unsafe { _layout->strikeThroughSize = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal short StrikethroughPosition
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->strikeThroughPosition; }
}
[SecurityCritical]
set
{
unsafe { _layout->strikeThroughPosition = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal bool Symbol
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->symbol != 0; }
}
[SecurityCritical]
set
{
unsafe { _layout->symbol = value ? (ushort)1 : (ushort)0; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal RenderingHints FontRenderingHints
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->renderingHints; }
}
[SecurityCritical]
set
{
unsafe { _layout->renderingHints = value; }
}
}
///
/// Critical: Calls unsafe code blocks, misusing the setter can result in spoofing,
/// the data is sensitive and is not OK to expose to untrusted clients.
///
internal FontEmbeddingRight EmbeddingRights
{
[SecurityCritical]
get
{
unsafe { return (FontEmbeddingRight)_layout->embeddingRights; }
}
[SecurityCritical]
set
{
// Make sure no truncation happens when storing the enum value as a ushort.
Invariant.Assert(value == (FontEmbeddingRight)(short)value);
unsafe { _layout->embeddingRights = (ushort)value; }
}
}
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal FontTechnology FontTechnology
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->fontTechnology; }
}
[SecurityCritical]
set
{
unsafe { _layout->fontTechnology = value; }
}
}
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal short FontContrastAdjustment
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->fontContrastAdjustment; }
}
[SecurityCritical]
set
{
unsafe { _layout->fontContrastAdjustment = value; }
}
}
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal FontStyle Style
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->style; }
}
[SecurityCritical]
set
{
unsafe { _layout->style = value; }
}
}
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal FontWeight Weight
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->weight; }
}
[SecurityCritical]
set
{
unsafe { _layout->weight = value; }
}
}
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing.
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// but that is mitigated by probe
///
internal FontStretch Stretch
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->stretch; }
}
[SecurityCritical]
set
{
unsafe { _layout->stretch = value; }
}
}
///
/// Critical: This calls into an unsafe code block and exposes font version,
/// which contains extra font information not strictly needed for rendering.
///
internal double Version
{
[SecurityCritical]
get
{
unsafe { return _layout->version; }
}
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable
///
internal TypographyAvailabilities TypographyAvailabilities
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe { return _layout->typographyAvailabilities; }
}
}
[Flags]
internal enum GaspFlags : ushort
{
GASP_GRIDFIT = 1,
GASP_DOGRAY = 2,
GASP_SYMMETRIC_GRIDFIT = 4,
GASP_SYMMETRIC_SMOOTHING = 8
}
internal struct GaspRange
{
public ushort ppem;
public GaspFlags flags;
};
///
/// Critical: This calls into an unsafe code block. This code
/// acceses cacher, allocates memory and manipulates pointers
/// TreatAsSafe: The pointer calls are bounds checked and
/// checked for validity in _cacher and ok to expose.
///
internal GaspRange[] GaspRanges
{
[SecurityCritical, SecurityTreatAsSafe]
set
{
unsafe
{
_layout->offGaspRanges = _cacher.Alloc(sizeof(ushort) + value.Length * 2 * sizeof(ushort));
ushort* gaspRanges = (ushort*)_cacher[_layout->offGaspRanges];
*gaspRanges++ = (ushort)value.Length;
foreach (GaspRange range in value)
{
*gaspRanges++ = range.ppem;
*gaspRanges++ = (ushort)range.flags;
}
}
}
}
// horizontal/vertical metrics are stored in this format:
// ushort number of glyphs
// followed by array of GlyphMetrics structures
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 14)]
internal struct GlyphMetrics
{
// horizontal metrics
internal ushort advanceWidth;
internal short lsb; // left sidebearing
internal short rsb; // right sidebearing
// vertical metrics
internal ushort advanceHeight;
internal short tsb; // top sidebearing
internal short bsb; // bottom sidebearing
internal short baseline; // distance to the bottom black pixel from base line Y
};
///
/// Critical: This calls into an unsafe code block, misusing the setter can result in spoofing and memory overrun.
/// TreatAsSafe: This data is ok to expose. The
/// risk is in setting this variable since
/// it is used to index into unmanaged memory.
///
internal ushort GlyphCount
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
unsafe
{
return _layout->glyphCount;
}
}
[SecurityCritical]
set
{
unsafe
{
_layout->glyphCount = value;
}
}
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: Allocating memory and assigning it to layout
/// is ok.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void CreateAdvanceWidthsArray()
{
unsafe
{
_layout->offAdvanceWidths = _cacher.Alloc(GlyphCount * sizeof(GlyphMetrics));
}
}
///
/// Critical: Calls unsafe code blocks. It returns a pointer
///
[SecurityCritical]
internal unsafe GlyphMetrics* Metrics(ushort glyphIndex)
{
if (glyphIndex >= GlyphCount)
throw new ArgumentOutOfRangeException("glyphIndex", SR.Get(SRID.GlyphIndexOutOfRange, glyphIndex));
Invariant.Assert(_layout->offAdvanceWidths != Util.nullOffset && _layout->offAdvanceWidths != 0);
GlyphMetrics* metrics = (GlyphMetrics*)_cacher[_layout->offAdvanceWidths];
return metrics + glyphIndex;
}
// horizontal metrics
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal ushort GetAdvanceWidth(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->advanceWidth; }
}
///
/// Critical: Calls unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
internal void SetAdvanceWidth(ushort glyphIndex, ushort advanceWidth)
{
unsafe { Metrics(glyphIndex)->advanceWidth = advanceWidth; }
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal short GetLeftSidebearing(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->lsb; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetLeftSidebearing(ushort glyphIndex, short lsb)
{
unsafe { Metrics(glyphIndex)->lsb = lsb; }
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal short GetRightSidebearing(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->rsb; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetRightSidebearing(ushort glyphIndex, short rsb)
{
unsafe { Metrics(glyphIndex)->rsb = rsb; }
}
// vertical metrics
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal ushort GetAdvanceHeight(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->advanceHeight; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetAdvanceHeight(ushort glyphIndex, ushort advanceHeight)
{
unsafe { Metrics(glyphIndex)->advanceHeight = advanceHeight; }
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal short GetTopSidebearing(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->tsb; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetTopSidebearing(ushort glyphIndex, short tsb)
{
unsafe { Metrics(glyphIndex)->tsb = tsb; }
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal short GetBottomSidebearing(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->bsb; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetBottomSidebearing(ushort glyphIndex, short bsb)
{
unsafe { Metrics(glyphIndex)->bsb = bsb; }
}
///
/// Critical: This calls into an unsafe code block
/// TreatAsSafe: This data is ok to expose. The
/// risk is if this pointer "_layout" is empty
/// which is mitigated by tracking all sets to this
/// variable. Also this code does not expose the pointer
/// it retrieves from Metrics
///
[SecurityCritical, SecurityTreatAsSafe]
internal short GetBaseline(ushort glyphIndex)
{
unsafe { return Metrics(glyphIndex)->baseline; }
}
///
/// Critical: This calls into an unsafe code block, can result in spoofing.
///
[SecurityCritical]
internal void SetBaseline(ushort glyphIndex, short baseline)
{
unsafe { Metrics(glyphIndex)->baseline = baseline; }
}
// OpenType support
///
/// Critical: Calls unsafe code blocks and returns a pointer
///
[SecurityCritical]
internal unsafe byte* Gsub()
{
if (_layout->offGsub == Util.nullOffset)
return null;
return _cacher[_layout->offGsub];
}
///
/// Critical: Calls unsafe code blocks and returns a pointer
///
[SecurityCritical]
internal unsafe byte* Gpos()
{
if (_layout->offGpos == Util.nullOffset)
return null;
return _cacher[_layout->offGpos];
}
///
/// Critical: Calls unsafe code blocks and returns a pointer
///
[SecurityCritical]
internal unsafe byte* Gdef()
{
if (_layout->offGdef == Util.nullOffset)
return null;
return _cacher[_layout->offGdef];
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
private int SetTable(CheckedPointer tablePointer)
{
unsafe
{
if (tablePointer.Size < sizeof(int))
return Util.nullOffset;
int layoutOffset = _cacher.Alloc(tablePointer.Size);
CheckedPointer table = _cacher.GetCheckedPointer(layoutOffset);
tablePointer.CopyTo(table);
// Table size is stored as the first integer in the cached table.
int* tableSize = (int*)table.Probe(0, sizeof(int));
*tableSize = tablePointer.Size;
return layoutOffset;
}
}
///
/// Critical: Calls unsafe code
///
[SecurityCritical]
internal unsafe CheckedPointer GetTableCache(OpenTypeTags tableTag)
{
switch (tableTag)
{
case OpenTypeTags.GSUB :
if (_layout->offGsubCache != Util.nullOffset)
{
return new CheckedPointer(_cacher[_layout->offGsubCache], _layout->gsubCacheLength);
}
break;
case OpenTypeTags.GPOS :
if (_layout->offGposCache != Util.nullOffset)
{
return new CheckedPointer(_cacher[_layout->offGposCache], _layout->gposCacheLength);
}
break;
default:
throw new NotSupportedException();
}
return new CheckedPointer();
}
///
/// Critical: Calls critical and unsafe code
///
[SecurityCritical]
internal unsafe CheckedPointer AllocateTableCache(OpenTypeTags tableTag, int size)
{
int layoutOffset = _cacher.Alloc(size);
switch (tableTag)
{
case OpenTypeTags.GSUB :
{
_layout->offGsubCache = layoutOffset;
_layout->gsubCacheLength = size;
break;
}
case OpenTypeTags.GPOS :
{
_layout->offGposCache = layoutOffset;
_layout->gposCacheLength = size;
break;
}
default:
{
throw new NotSupportedException();
}
}
if (layoutOffset == Util.nullOffset)
{
return new CheckedPointer();
}
//
return new CheckedPointer(_cacher[layoutOffset], size);
}
///
/// Compiles cache data needed for OpenType layout services
///
///
/// Critical - calls critical code (OpenType layout table access)
///
[SecurityCritical]
private void CreateOpenTypeLayoutCache()
{
int maxLayoutCacheSize = 0x4000; // 16K
GsubGposTables gsubGpos = new GsubGposTables(this);
//
OpenTypeLayout.CreateLayoutCache(gsubGpos, maxLayoutCacheSize);
}
///
/// Computes the typography availabilities.
/// It checks the presence of a set of required features in the font
/// for ranges of unicode code points and set the corresponding bits
/// in the TypographyAvailabilities enum. TypographyAvailabilities enum is
/// used to determind whether fast path can be used to format the input.
///
///
/// Critical - calls critical code (GetComplexLanguageList)
/// TreatAsSafe - returns safe information about the font
///
[SecurityCritical, SecurityTreatAsSafe]
private TypographyAvailabilities ComputeTypographyAvailabilities()
{
uint[] glyphBits = new uint[(GlyphCount + 31) >> 5];
ushort minGlyphId = 65535;
ushort maxGlyphId = 0;
WritingSystem[] complexScripts;
TypographyAvailabilities typography = TypographyAvailabilities.None;
GsubGposTables GsubGpos = new GsubGposTables(this);
// preparing the glyph bits. When the bit is set, it means the corresponding
// glyph needs to be checked against.
for (int i = 0; i < fastTextRanges.Length; i++)
{
int firstChar = fastTextRanges[i].firstChar;
int lastChar = fastTextRanges[i].lastChar;
for (int ch = firstChar; ch <= lastChar; ch++)
{
ushort glyphId;
if (CharacterMap.TryGetValue(ch, out glyphId))
{
glyphBits[glyphId >> 5] |= (uint)(1 << (glyphId % 32));
if (glyphId > maxGlyphId) maxGlyphId = glyphId;
if (glyphId < minGlyphId) minGlyphId = glyphId;
}
}
}
//
// Step 1: call OpenType layout engine to test presence of
// 'locl' feature. Based on the returned writing systems, set
// FastTextMajorLanguageLocalizedFormAvailable bit and
// FastTextExtraLanguageLocalizedFormAvailable bit
//
OpenTypeLayoutResult result;
unsafe
{
result = OpenTypeLayout.GetComplexLanguageList(
GsubGpos,
LoclFeature,
glyphBits,
minGlyphId,
maxGlyphId,
out complexScripts
);
}
if (result != OpenTypeLayoutResult.Success)
{
// The check failed. We abort and don't keep partial results that are not reliable
return TypographyAvailabilities.None;
}
else if (complexScripts != null)
{
// This is the bits for localized form we would want to set
// if both bits for localized form were set, we can end the loop earlier
TypographyAvailabilities loclBitsTest =
TypographyAvailabilities.FastTextMajorLanguageLocalizedFormAvailable
| TypographyAvailabilities.FastTextExtraLanguageLocalizedFormAvailable;
for (int i = 0; i < complexScripts.Length && typography != loclBitsTest; i++)
{
if (MajorLanguages.Contains((LanguageTags)complexScripts[i].langSysTag))
{
typography |= TypographyAvailabilities.FastTextMajorLanguageLocalizedFormAvailable;
}
else
{
typography |= TypographyAvailabilities.FastTextExtraLanguageLocalizedFormAvailable;
}
}
}
//
// step 2: continue to find out whether there is common features availabe
// in the font for the unicode ranges and set the FastTextTypographyAvailable bit
//
unsafe
{
result = OpenTypeLayout.GetComplexLanguageList(
GsubGpos,
RequiredTypographyFeatures,
glyphBits,
minGlyphId,
maxGlyphId,
out complexScripts
);
}
if (result != OpenTypeLayoutResult.Success)
{
// The check failed. We abort and don't keep partial results that are not reliable
return TypographyAvailabilities.None;
}
else if (complexScripts != null)
{
typography |= TypographyAvailabilities.FastTextTypographyAvailable;
}
//
// Step 3: call OpenType layout engine to find out if there is any feature present for
// ideographs. Because there are many ideographs to check for, an alternative is to
// check for all scripts with the required features in the font by setting all
// glyph bits to 1, then see whether CJKIdeograph is in the returned list.
//
for (int i = 0; i < glyphBits.Length; i++)
{
glyphBits[i] = 0xFFFFFFFF;
}
unsafe
{
result = OpenTypeLayout.GetComplexLanguageList(
GsubGpos,
RequiredFeatures,
glyphBits,
minGlyphId,
maxGlyphId,
out complexScripts
);
}
if (result != OpenTypeLayoutResult.Success)
{
// The check failed. We abort and don't keep partial results that are not reliable
return TypographyAvailabilities.None;
}
else if (complexScripts != null)
{
for (int i = 0; i < complexScripts.Length; i++)
{
if (complexScripts[i].scriptTag == (uint)ScriptTags.CJKIdeographic)
{
typography |= TypographyAvailabilities.IdeoTypographyAvailable;
}
else
{
typography |= TypographyAvailabilities.Available;
}
}
}
if (typography != TypographyAvailabilities.None)
{
// if any of the bits were set, set TypographyAvailabilities.Avaialble bit
// as well to indicate some lookup is available.
typography |= TypographyAvailabilities.Available;
}
return typography;
}
///
/// Critical: Calls unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
internal unsafe void SetGsub(CheckedPointer gsub)
{
_layout->offGsub = SetTable(gsub);
}
///
/// Critical: Calls unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
internal unsafe void SetGpos(CheckedPointer gpos)
{
_layout->offGpos = SetTable(gpos);
}
///
/// Critical: Calls unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
internal unsafe void SetGdef(CheckedPointer gdef)
{
_layout->offGdef = SetTable(gdef);
}
///
/// Critical: Calls unsafe code blocks, can result in spoofing.
///
[SecurityCritical]
internal unsafe void SetJstf(CheckedPointer jstf)
{
_layout->offJstf = SetTable(jstf);
}
///
/// Critical: Calls unsafe code blocks to store a list of names into a filemapping object, can result in spoofing.
///
[SecurityCritical]
private int ConvertNames(LocalizedName[] names)
{
if (names == null)
return Util.nullOffset;
unsafe
{
int cacheOffset = _cacher.Alloc(sizeof(CachedNames) + names.Length * sizeof(CachedName));
CachedNames* cachedNames = (CachedNames*)_cacher[cacheOffset];
cachedNames->numberOfNames = names.Length;
CachedName* cachedName = (CachedName*)((byte*)cachedNames + sizeof(CachedNames));
for (int i = 0; i < names.Length; ++i)
{
// GetEquivalentCulture() should always succeed, because we started with information in a
// a TrueType file, which contained an LCID.
cachedName->language = names[i].OriginalLCID;
cachedName->nameSize = Util.StringSize(names[i].Name);
cachedName->nameString = _cacher.Alloc(cachedName->nameSize);
Util.StringCopyToCheckedPointer(_cacher.GetCheckedPointer(cachedName->nameString), names[i].Name);
++cachedName;
}
return cacheOffset;
}
}
///
/// Critical: Calls unsafe code blocks to set the various fields in _layout
/// TreatAsSafe: The pointers themselves are not exposed. Also the construction
/// of _layout is tracked to ensure that it cannot be hijacked.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void AddLocalizedNames(ref TrueTypeFontDriver.ParsedNameTable nameTable, bool skipFontDifferentiation)
{
unsafe
{
_layout->version = nameTable.version;
_layout->familyNames = ConvertNames(nameTable.familyNames);
_layout->win32FamilyNames = ConvertNames(nameTable.win32FamilyNames);
_layout->faceNames = ConvertNames(nameTable.faceNames);
_layout->win32faceNames = ConvertNames(nameTable.win32faceNames);
// Run font differentiator to adjust family and face names.
FontStyle adjustedStyle = _layout->style;
FontWeight adjustedWeight = _layout->weight;
FontStretch adjustedStretch = _layout->stretch;
LocalizedName[] oldFaceNames = nameTable.faceNames;
if (!skipFontDifferentiation)
{
FontDifferentiator.AdjustFamilyAndFaceInformation(ref nameTable, ref adjustedStyle, ref adjustedWeight, ref adjustedStretch);
}
// We store only adjusted face names in cache at this point, but if we decide to expose the rest,
// this is the right place to store this information in the cache.
if (nameTable.faceNames == null)
{
// We have only legacy face names.
_layout->adjustedFaceNames = _layout->win32faceNames;
}
else
{
if (oldFaceNames == nameTable.faceNames)
{
// Font differentiator didn't modify preferred face names.
_layout->adjustedFaceNames = _layout->faceNames;
}
else
{
// Font differentiator modified preferred face names.
_layout->adjustedFaceNames = ConvertNames(nameTable.faceNames);
}
}
_layout->versionStrings = ConvertNames(nameTable.versionStrings);
_layout->copyrights = ConvertNames(nameTable.copyrights);
_layout->manufacturerNames = ConvertNames(nameTable.manufacturerNames);
_layout->trademarks = ConvertNames(nameTable.trademarks);
_layout->designerNames = ConvertNames(nameTable.designerNames);
_layout->descriptions = ConvertNames(nameTable.descriptions);
_layout->vendorUrls = ConvertNames(nameTable.vendorUrls);
_layout->designerUrls = ConvertNames(nameTable.designerUrls);
_layout->licenseDescriptions = ConvertNames(nameTable.licenseDescriptions);
_layout->sampleTexts = ConvertNames(nameTable.sampleTexts);
}
}
///
/// Critical: This code yieds accesses unsafe method ElementCacher and uses _layout
/// TreatAsSafe: This code exposes and IDictionary object for Family Names which is ok
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe IDictionary GetFamilyNameDictionary()
{
return new LocalizedNameDictionary(
_cacher,
_layout->familyNames, // primary lookup
_layout->win32FamilyNames // fall back lookup
);
}
///
/// Critical: This calls into unsafe code to retrieve the family name
/// TreatAsSafe:This information is ok to give out and returns a dictionary
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe IDictionary GetWin32FamilyNameDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->win32FamilyNames);
}
///
/// Critical: This calls into unsafe code to retrieve the face name
/// TreatAsSafe:This information is ok to give out
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe IDictionary GetFaceNameDictionary()
{
return new LocalizedNameDictionary(
_cacher,
_layout->faceNames, // primary lookup
_layout->win32faceNames // fall back lookup
);
}
///
/// Critical: This calls into unsafe code to retrieve the face name
/// TreatAsSafe:This information is ok to give out
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe IDictionary GetWin32FaceNameDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->win32faceNames);
}
///
/// Critical: This calls into unsafe code to retrieve the adjusted family name
/// TreatAsSafe: This information is ok to give out.
///
[SecurityCritical, SecurityTreatAsSafe]
internal unsafe IDictionary GetAdjustedFaceNameDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->adjustedFaceNames);
}
///
/// Critical: This calls into unsafe code to retrieve the version string,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetVersionStringDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->versionStrings);
}
///
/// Critical: This calls into unsafe code to retrieve copyright information,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetCopyrightDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->copyrights);
}
///
/// Critical: This calls into unsafe code to retrieve the manufacturer name,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetManufacturerNameDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->manufacturerNames);
}
///
/// Critical: This calls into unsafe code to retrieve trademark,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetTrademarkDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->trademarks);
}
///
/// Critical: This calls into unsafe code to retrieve the Designer Name,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetDesignerNameDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->designerNames);
}
///
/// Critical: This calls into unsafe code to retrieve the dscription,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetDescriptionDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->descriptions);
}
///
/// Critical: This calls into unsafe code to retrieve the Vendor Url,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetVendorUrlDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->vendorUrls);
}
///
/// Critical: This calls into unsafe code to retrieve the dsigner uri,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetDesignerUrlDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->designerUrls);
}
///
/// Critical: This calls into unsafe code to retrieve the Licensce,
/// and this exposes extra font information not needed for rendering.
///
[SecurityCritical]
internal unsafe IDictionary GetLicenseDescriptionDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->licenseDescriptions);
}
///
/// Critical: This calls into unsafe code to retrieve the sample text
///
[SecurityCritical]
internal unsafe IDictionary GetSampleTextDictionary()
{
return new LocalizedNameDictionary(_cacher, _layout->sampleTexts);
}
#region IntMap
///
/// IntMap represents mapping from UTF32 code points to glyph indices.
/// The IDictionary part is eventually returned from public APIs and is made read-only.
/// Internal methods are used by the font driver to create the cmap.
///
internal sealed class IntMap : IDictionary
{
internal static int InitialSize()
{
unsafe { return sizeof(int) + NumberOfPlanes * sizeof(int); }
}
///
/// Critical: Calls into probe which is critical and also has unsafe code blocks, can be used to spoof cmap.
///
[SecurityCritical]
internal IntMap(CheckedPointer p, ElementCacher cacher)
{
unsafe { _data = (byte*)p.Probe(0, InitialSize()); }
_cacher = cacher;
}
///
/// Critical: This code accesses unsafe code blocks
/// TreatAsSafe: This information is ok to return for get but bad for set
///
internal unsafe int CharacterCount
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
return *(int*)_data;
}
[SecurityCritical]
set
{
*(int*)_data = value;
}
}
///
/// Critical: Calls into critical code which does an unsafe operation FillMemory.
/// TreatAsSafe: Ok to expose, since GetPlanePointer will return a valid value and number of planes is a const.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void Init()
{
CharacterCount = 0;
unsafe { Util.FillMemory(GetPlanePointer(0), NumberOfPlanes * sizeof(int), EmptyPlane); }
}
///
/// Glyph count is used for validating cmap contents.
/// If we discover that glyph index we are about to set or return is outside of glyph range,
/// we throw an exception.
///
///
/// Critical: Setting this from unauthorized functions increases the risk of failure in functions that
/// work with _glyphcount
///
[SecurityCritical]
internal void SetGlyphCount(ushort glyphCount)
{
_glyphCount = new SecurityCriticalDataForSet(glyphCount);
}
///
/// Critical: This code calls into unsafe code blocks and gets a pointer back. It uses
/// this to initialize a character entry. Can be used to spoof cmap.
///
[SecurityCritical]
internal void SetCharacterEntry(int i, ushort value)
{
// Some fonts have cmap entries that point to glyphs outside of the font.
// Just skip such entries.
if (value >= _glyphCount.Value)
return;
int plane = CreatePlane(i >> 16);
int page = CreatePage(plane, i >> 8 & 0xff);
unsafe
{
ushort* characterEntry = GetGlyphPointer(page, i & 0xff);
if (*characterEntry == 0 && value != 0)
++CharacterCount;
*characterEntry = value;
}
}
///
/// Critical: Calls into unsafe code block and returns a pointer
///
[SecurityCritical]
private unsafe int* GetPlanePointer(int i)
{
if (i < 0 || i >= NumberOfPlanes)
throw new ArgumentOutOfRangeException("c", SR.Get(SRID.CodePointOutOfRange, i));
return ((int*)_data) + i + 1;
}
///
/// Critical: Calls into unsafe code
/// TreatAsSafe: This code does not expose the pointer and is safe to call
///
[SecurityCritical, SecurityTreatAsSafe]
private unsafe int GetPlane(int i)
{
return *GetPlanePointer(i);
}
///
/// Critical: Calls into unsafe code block and exposes a pointer
///
[SecurityCritical]
private unsafe int* GetPagePointer(int plane, int i)
{
Invariant.Assert(0 <= i && i < NumberOfPages);
return ((int*)_cacher[plane]) + i;
}
///
/// Critical: Calls into unsafe code block
/// TreatAsSafe: This code does not expose the pointer and is safe to call
///
[SecurityCritical, SecurityTreatAsSafe]
private unsafe int GetPage(int plane, int i)
{
return *GetPagePointer(plane, i);
}
///
/// Critical: Calls into unsafe code block
///
[SecurityCritical]
private unsafe ushort* GetGlyphPointer(int page, int key)
{
Invariant.Assert(0 <= key && key < PageSize);
return (ushort*)_cacher[page] + key;
}
///
/// Critical: Calls into unsafe code block
/// TreatAsSafe: This code does not expose the pointer and is safe to call
///
[SecurityCritical, SecurityTreatAsSafe]
private unsafe ushort GetGlyph(int page, int key)
{
return *GetGlyphPointer(page, key);
}
///
/// Critical: Calls into unsafe code block
/// TreatAsSafe: Ok to expose this function. It uses _cacher which is
/// bounds checked.
///
[SecurityCritical, SecurityTreatAsSafe]
private int CreatePlane(int i)
{
unsafe
{
int* plane = GetPlanePointer(i);
if (*plane == EmptyPlane)
{
*plane = _cacher.Alloc(NumberOfPages * sizeof(int));
Util.FillMemory(_cacher[*plane], NumberOfPages * sizeof(int), EmptyPage);
}
return *plane;
}
}
///
/// Critical: Calls into unsafe code block
/// TreatAsSafe: Ok to expose, it used cacher which is bounds checked, it
/// uses GetPlanePointer which uses a const int and hence the index into
/// the array cannot be breached.
///
[SecurityCritical, SecurityTreatAsSafe]
private int CreatePage(int plane, int i)
{
unsafe
{
int* page = GetPagePointer(plane, i);
if (*page == EmptyPage)
{
*page = _cacher.Alloc(PageSize * sizeof(short));
Util.FillMemory(_cacher[*page], PageSize * sizeof(short), 0);
}
return *page;
}
}
#region IDictionary Members
public void Add(int key, ushort value)
{
throw new NotSupportedException();
}
public bool ContainsKey(int key)
{
ushort glyphIndex;
return TryGetValue(key, out glyphIndex);
}
public ICollection Keys
{
get
{
// This is inefficient, but Keys is not used too often to justify optimizing it.
int[] keys = new int[Count];
int i = 0;
foreach (KeyValuePair pair in this)
{
keys[i++] = pair.Key;
}
Debug.Assert(i == Count);
return keys;
}
}
public bool Remove(int key)
{
throw new NotSupportedException();
}
public bool TryGetValue(int key, out ushort value)
{
try
{
int plane = GetPlane(key >> 16);
if (plane != EmptyPlane)
{
int page = GetPage(plane, key >> 8 & 0xff);
if (page != EmptyPage)
{
ushort glyphIndex = GetGlyph(page, key & 0xff);
Debug.Assert(glyphIndex < _glyphCount.Value);
if (glyphIndex != 0)
{
value = glyphIndex;
return true;
}
}
}
}
catch (ArgumentOutOfRangeException)
{
// GetPlane() throws ArgumentOutOfRangeException if the key value is outside of the valid Unicode range.
// Since IDictionary semantics requre returning false for non-existent keys, we need to catch it.
}
value = new ushort();
return false;
}
public ICollection Values
{
get
{
// This is inefficient, but Values is not used too often to justify optimizing it.
ushort[] values = new ushort[Count];
int i = 0;
foreach (KeyValuePair pair in this)
{
values[i++] = pair.Value;
}
Debug.Assert(i == Count);
return values;
}
}
ushort IDictionary.this[int i]
{
get
{
ushort glyphIndex;
if (!TryGetValue(i, out glyphIndex))
throw new KeyNotFoundException();
return glyphIndex;
}
set
{
throw new NotSupportedException();
}
}
#endregion
#region ICollection> Members
public void Add(KeyValuePair item)
{
throw new NotSupportedException();
}
public void Clear()
{
throw new NotSupportedException();
}
public bool Contains(KeyValuePair item)
{
return ContainsKey(item.Key);
}
public void CopyTo(KeyValuePair[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException("array");
}
if (array.Rank != 1)
{
throw new ArgumentException(SR.Get(SRID.Collection_BadRank));
}
// The extra "arrayIndex >= array.Length" check in because even if _collection.Count
// is 0 the index is not allowed to be equal or greater than the length
// (from the MSDN ICollection docs)
if (arrayIndex < 0 || arrayIndex >= array.Length || (arrayIndex + Count) > array.Length)
{
throw new ArgumentOutOfRangeException("arrayIndex");
}
foreach (KeyValuePair pair in this)
array[arrayIndex++] = pair;
}
public int Count
{
get { return CharacterCount; }
}
public bool IsReadOnly
{
get { return true; }
}
public bool Remove(KeyValuePair item)
{
throw new NotSupportedException();
}
#endregion
#region IEnumerable> Members
public IEnumerator> GetEnumerator()
{
for (int i = 0; i < NumberOfPlanes; ++i)
{
int plane = GetPlane(i);
if (plane != EmptyPlane)
{
for (int j = 0; j < NumberOfPages; ++j)
{
int page = GetPage(plane, j);
if (page != EmptyPage)
{
for (int k = 0; k < PageSize; ++k)
{
ushort glyph = GetGlyph(page, k);
if (glyph != 0)
yield return new KeyValuePair(
(i << 16) | (j << 8) | k,
glyph
);
}
}
}
}
}
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable>)this).GetEnumerator();
}
#endregion
///
/// Critical:This code holds a pointer and is unsafe to let out
///
[SecurityCritical]
private unsafe byte* _data;
private ElementCacher _cacher;
///
/// Critical:This code is used to dereference a memory location and is hence critical
///
private SecurityCriticalDataForSet _glyphCount;
private const int NumberOfPlanes = 17;
private const int NumberOfPages = 256;
private const int PageSize = 256;
private const int EmptyPlane = Util.nullOffset;
private const int EmptyPage = Util.nullOffset;
}
internal enum RenderingHints : ushort
{
Regular = 0,
LegacyEastAsian = 1
};
#endregion
//------------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
private ElementCacher _cacher;
///
/// Critical: Holds reference to an unmanaged pointer
///
[SecurityCritical]
private unsafe Layout* _layout;
private IntMap _cmap;
private int _faceIndex;
private long _timeStamp;
private FontSource _fontSource;
// 'locl' feature which is language sensitive
private static readonly uint[] LoclFeature = new uint[]
{
(uint)OpenTypeTags.locl
};
// common features for fast text
// They are insensitive to languages
private static readonly uint[] RequiredTypographyFeatures = new uint[]
{
(uint)OpenTypeTags.ccmp,
(uint)OpenTypeTags.rlig,
(uint)OpenTypeTags.liga,
(uint)OpenTypeTags.clig,
(uint)OpenTypeTags.calt,
(uint)OpenTypeTags.kern,
(uint)OpenTypeTags.mark,
(uint)OpenTypeTags.mkmk
};
// All required features
private static readonly uint[] RequiredFeatures = new uint[]
{
(uint)OpenTypeTags.locl,
(uint)OpenTypeTags.ccmp,
(uint)OpenTypeTags.rlig,
(uint)OpenTypeTags.liga,
(uint)OpenTypeTags.clig,
(uint)OpenTypeTags.calt,
(uint)OpenTypeTags.kern,
(uint)OpenTypeTags.mark,
(uint)OpenTypeTags.mkmk
};
private static readonly UnicodeRange[] fastTextRanges = new UnicodeRange[]
{
new UnicodeRange(0x20 , 0x7e ), // basic latin
new UnicodeRange(0xA1 , 0xFF ), // latin-1 supplement,
new UnicodeRange(0x0100, 0x17F ), // latin extended-A
new UnicodeRange(0x0180, 0x024F), // latin extended-B
new UnicodeRange(0x1E00, 0x1EFF), // latin extended additional (Vietnamese precomposed)
new UnicodeRange(0x3040, 0x309F), // hiragana
new UnicodeRange(0x30A0, 0x30FF) // kana
};
#endregion Private Fields
#region Private Types
// LocalizedNameDictionary implements IDictionary in terms
// of one or two arrays of CachedName structures. Each array is assumed to be
// sorted by LCID.
//
// If two arrays are specified, we use the first array for primary lookup and
// the second array as a fallback. We use integer indexes internally to refer to
// culture/name pairs. We can tell from its value whether an index refers to the
// first or second array (see the GetCachedName method). An index must be less
// than the *sum* of the sizes of the two arrays, specified by the Limit property.
//
// If the fallback array contains keys which are also in the primary array, we
// don't want to enumerate them or include them in the Count. For this reason,
// Count may be less than Limit. We use the IsDuplicateKey method during enumeration
// to determine whether to skip an index, and we use GetCountWithoutDuplicates to
// calculate the count.
//
private unsafe struct LocalizedNameDictionary : IDictionary
{
private ElementCacher _cacher;
// primary table of strings
///
/// Critical:This holds reference to a pointer
///
[SecurityCritical]
private CachedName* _cachedName;
///
/// Critical:This holds data that is used to make decisions on dereferencing a pointer
///
private SecurityCriticalDataForSet _numberOfNames;
// secondary table of strings
///
/// Critical:This holds reference to a pointer
///
[SecurityCritical]
private CachedName* _cachedNameFallback;
///
/// Critical:This holds data that is used to make decisions on dereferencing a pointer
///
private SecurityCriticalDataForSet _numberOfNamesFallback;
// number of strings, excluding duplicates; may be computed lazily
private int _count;
///
/// Construct a dictionary using a single array of cached strings.
///
internal LocalizedNameDictionary(ElementCacher cacher, int cacheOffset)
:
this(cacher, cacheOffset, Util.nullOffset)
{
}
///
/// Construct a dictionary with a primary and fallback array of cached strings.
///
///
/// Critical:This accesses elementcacher and is unsafe
/// TreatAsSafe: Calling this is ok also it does not expose the pointers.
///
[SecurityCritical, SecurityTreatAsSafe]
internal LocalizedNameDictionary(ElementCacher cacher, int cacheOffset, int cacheOffsetFallback)
{
_cacher = cacher;
// It's quite common for a font to provide only a Win32-compatible table.
// If there's only one table we always want to make it the primary table
// for reasons of efficiency.
if (cacheOffset == Util.nullOffset)
{
cacheOffset = cacheOffsetFallback;
cacheOffsetFallback = Util.nullOffset;
}
// Initialize the primary table.
if (cacheOffset != Util.nullOffset)
{
CachedNames* cachedNames = (CachedNames*)_cacher[cacheOffset];
_cachedName = (CachedName*)((byte*)cachedNames + sizeof(CachedNames));
_numberOfNames = new SecurityCriticalDataForSet(cachedNames->numberOfNames);
}
else
{
_cachedName = null;
_numberOfNames = new SecurityCriticalDataForSet(0);
}
// Initialize the secondary table.
if (cacheOffsetFallback != Util.nullOffset)
{
CachedNames* cachedNames = (CachedNames*)_cacher[cacheOffsetFallback];
_cachedNameFallback = (CachedName*)((byte*)cachedNames + sizeof(CachedNames));
_numberOfNamesFallback = new SecurityCriticalDataForSet(cachedNames->numberOfNames);
}
else
{
_cachedNameFallback = null;
_numberOfNamesFallback = new SecurityCriticalDataForSet(0);
}
// Initialize count.
if (_numberOfNamesFallback.Value == 0 || _numberOfNames.Value == 0)
{
// If there's only one non-empty table then we don't need to check
// for duplicate keys.
_count = _numberOfNamesFallback.Value + _numberOfNames.Value;
}
else
{
// Compute count lazily because we need to check for duplicates.
_count = -1;
}
}
///
/// Critical:This accesses elementcacher and is unsafe it also returns a pointer
///
[SecurityCritical]
private CachedName* GetCachedName(int index)
{
// We should be checking bounds farther up the stack, e.g., in Enumerator class.
Invariant.Assert(index >= 0 && index < Limit);
// Look in primary or secondary table depending on value of index.
if (index < _numberOfNames.Value)
{
return _cachedName + index;
}
else
{
return _cachedNameFallback + (index - _numberOfNames.Value);
}
}
///
/// Critical:This accesses elementcacher which is unsafe
/// TreatAsSafe: This code accesses GetCachedName which returns a pointer ,returning this data is ok
///
[SecurityCritical, SecurityTreatAsSafe]
private int GetLCID(int index)
{
return GetCachedName(index)->language;
}
///
/// Critical:This accesses elementcacher and GetCachedName
/// TreatAsSafe: Returning this data is ok also the two functions
/// safeguard against invalid values
///
[SecurityCritical, SecurityTreatAsSafe]
private string GetName(int index)
{
CachedName* cachedName = GetCachedName(index);
return Util.StringCopyFromCheckedPointer(_cacher.GetCheckedPointer(cachedName->nameString), cachedName->nameSize);
}
///
/// Look up an LCID in the specified table.
///
/// LCID to look up.
/// Offset to add to returned index if found.
/// Array of CachedName structures.
/// Size of the array.
/// Index of match (plus offset) or -1 if not found.
///
/// Critical:This accesses elementcacher and is unsafe it also accepts a pointer
///
[SecurityCritical]
private int FindLCID(int lcid, int offset, CachedName* cachedName, int numberOfNames)
{
// Look up name based on LCID using binary search.
int min = 0, lim = numberOfNames;
while (min < lim)
{
int i = (min + lim) / 2;
int key = cachedName[i].language;
if (lcid < key)
lim = i;
else if (lcid > key)
min = i + 1;
else
return i + offset;
}
return -1;
}
///
/// Critical: This code calls accesses _cachedName
/// TreatAsSafe: The pointer is not exposed and LCID is ok to give out
///
[SecurityCritical, SecurityTreatAsSafe]
private int FindPrimaryLCID(int lcid)
{
return FindLCID(
lcid, // LCID to look for
0, // no offset
_cachedName, // look in primary array
_numberOfNames.Value);
}
///
/// Critical: This code calls accesses _cachedNameFallBack
/// TreatAsSafe: The pointer is not exposed and the call to FindLCID is safe
///
[SecurityCritical, SecurityTreatAsSafe]
private int FindSecondaryLCID(int lcid)
{
return FindLCID(
lcid, // LCID to look for
_numberOfNames.Value, // offset added to result if found
_cachedNameFallback, // look in secondary array
_numberOfNamesFallback.Value);
}
///
/// Look up an LCID in both tables.
///
private int FindLCID(int lcid)
{
// Look up in primary name table.
int i = FindPrimaryLCID(lcid);
// Fall back to secondary name table.
if (i < 0 && _numberOfNamesFallback.Value != 0)
{
i = FindSecondaryLCID(lcid);
}
return i;
}
///
/// Returns the name for the given LCID or null if not found.
///
internal string GetNameFromLCID(int lcid)
{
int i = FindLCID(lcid);
return (i >= 0) ? GetName(i) : null;
}
///
/// Determines whether the specified index is a duplicate key, which
/// should be skipped during enumeration.
///
///
/// Critical:This accesses elementcacher and is unsafe
/// TreatAsSafe: Calling this is ok since all it returns is whether a key is duped
///
[SecurityCritical, SecurityTreatAsSafe]
internal bool IsDuplicateKey(int index)
{
// We should be checking bounds higher up the stack, e.g., in Enumerator class.
Invariant.Assert(index >= 0 && index < Limit);
// Keys in the primary table are not duplicates by definition.
if (index < _numberOfNames.Value)
return false;
// If we find the LCID in the primary table then it's a duplicate.
int lcid = _cachedNameFallback[index - _numberOfNames.Value].language;
return FindPrimaryLCID(lcid) >= 0;
}
///
/// Computes the count (if not already known). The count excludes
/// indexes for which IsDuplicateKey is true.
///
///
/// Critical:This accesses elementcacher and is unsafe
/// TreatAsSafe: Calling this is ok does not return the critical data
///
[SecurityCritical, SecurityTreatAsSafe]
private int GetCountWithoutDuplicates()
{
if (_count < 0)
{
// include all keys in the primary table
_count = _numberOfNames.Value;
// iterate over the secondary table including only non-duplicates
for (int i = 0; i < _numberOfNamesFallback.Value; ++i)
{
int lcid = _cachedNameFallback[i].language;
if (FindPrimaryLCID(lcid) < 0)
{
// not a duplicate
_count++;
}
}
}
return _count;
}
///
/// Limit value for indexes. May differ from Count because the range
/// of indexes may include duplicate keys which are not enumerated or
/// included in the Count.
///
internal int Limit
{
get
{
return _numberOfNames.Value + _numberOfNamesFallback.Value;
}
}
#region IDictionary Members
bool IDictionary.Remove(CultureInfo key)
{
throw new NotSupportedException();
}
void IDictionary.Add(CultureInfo key, string value)
{
throw new NotSupportedException();
}
ICollection IDictionary.Keys
{
get
{
// OK, this is very slow, but semantically correct.
// Keys are not used that often anyway.
CultureInfo[] keys = new CultureInfo[GetCountWithoutDuplicates()];
// Enumerator will automatically skip duplicates.
int i = 0;
foreach (KeyValuePair pair in this)
{
keys[i++] = pair.Key;
}
// Debug check for enumeration logic.
Debug.Assert(i == keys.Length);
return keys;
}
}
string IDictionary.this[CultureInfo key]
{
get
{
return GetNameFromLCID(key.LCID);
}
set
{
throw new NotSupportedException();
}
}
bool IDictionary.TryGetValue(CultureInfo key, out string value)
{
int i = FindLCID(key.LCID);
if (i >= 0)
{
value = GetName(i);
return true;
}
else
{
value = null;
return false;
}
}
ICollection IDictionary.Values
{
get
{
// OK, this is very slow, but semantically correct.
// Values are not used that often anyway.
string[] values = new string[GetCountWithoutDuplicates()];
// Enumerator will automatically skip duplicates.
int i = 0;
foreach (KeyValuePair pair in this)
{
values[i++] = pair.Value;
}
// Debug check for enumeration logic.
Debug.Assert(i == values.Length);
return values;
}
}
bool IDictionary.ContainsKey(CultureInfo key)
{
return FindLCID(key.LCID) >= 0;
}
#endregion
#region ICollection> Members
bool ICollection>.Contains(KeyValuePair item)
{
int i = FindLCID(item.Key.LCID);
return (i >= 0 && String.Compare(GetName(i), item.Value, StringComparison.OrdinalIgnoreCase) == 0);
}
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 GetCountWithoutDuplicates();
}
}
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
//
// Enumerator class for LocalizedNameTable. Internally, we represent the current
// position as an integer index. We enumerate by incrementing the index, taking
// into account that some indexes are invalid (i.e., duplicate keys).
//
private class Enumerator : IEnumerator>
{
private int _currentIndex;
private LocalizedNameDictionary _parent;
public Enumerator(LocalizedNameDictionary parent)
{
_parent = parent;
_currentIndex = -1;
}
private void FailIfOutOfRange()
{
if (_currentIndex < 0)
throw new InvalidOperationException(SR.Get(SRID.Enumerator_NotStarted));
if (_currentIndex >= _parent.Limit)
throw new InvalidOperationException(SR.Get(SRID.Enumerator_ReachedEnd));
}
private CultureInfo GetCurrentCulture()
{
return CultureInfo.GetCultureInfo(_parent.GetLCID(_currentIndex));
}
private string GetCurrentName()
{
return _parent.GetName(_currentIndex);
}
#region IEnumerator> Members
public bool MoveNext()
{
// We may need to increment the index more than once to skip
// duplicate keys.
do
{
++_currentIndex;
// stop if we're at the end
if (_currentIndex >= _parent.Limit)
{
// prevent cycling
_currentIndex = _parent.Limit;
return false;
}
} while (_parent.IsDuplicateKey(_currentIndex));
return true;
}
KeyValuePair IEnumerator>.Current
{
get
{
FailIfOutOfRange();
return new KeyValuePair(GetCurrentCulture(), GetCurrentName());
}
}
object IEnumerator.Current
{
get
{
return ((IEnumerator>)this).Current;
}
}
public void Reset()
{
_currentIndex = -1;
}
#endregion
#region IDisposable Members
public void Dispose() { }
#endregion
}
}
#endregion
}
///
/// An implementation of IOpenTypeFont which only provides GSUB and GPOS tables
/// It is used by OTLS API to determine the optimizable script.
///
///
/// OTLS API always accepts IOpenTypeFont as input parameter. To be consistent, we
/// implement this IOpenTypeFont just for OpenTypeLayout.GetComplexLanguangeList(..) method.
///
internal sealed class GsubGposTables : IOpenTypeFont
{
///
/// Critical: Gsub() and Gpos() return pointers
///
[SecurityCritical]
internal GsubGposTables(FontFaceLayoutInfo layout)
{
_layout = layout;
unsafe
{
_gsubTable = new FontTable(_layout.Gsub());
_gposTable = new FontTable(_layout.Gpos());
}
}
///
/// Returns font table data
///
public FontTable GetFontTable(OpenTypeTags TableTag)
{
switch (TableTag)
{
case OpenTypeTags.GSUB:
{
return _gsubTable;
}
case OpenTypeTags.GPOS:
{
return _gposTable;
}
default:
{
throw new NotSupportedException();
}
}
}
///
/// Returns glyph coordinate
///
public LayoutOffset GetGlyphPointCoord(ushort Glyph, ushort PointIndex)
{
throw new NotSupportedException();
}
///
/// Returns cache for layout table. If cache not found, return null Checked pointer
///
///
/// Critical: Calls critical code
///
[SecurityCritical]
public CheckedPointer GetTableCache(OpenTypeTags tableTag)
{
return _layout.GetTableCache(tableTag);
}
///
/// Allocate space for layout table cache.
///
///
/// Critical: Calls critical code
///
[SecurityCritical]
public CheckedPointer AllocateTableCache(OpenTypeTags tableTag, int size)
{
return _layout.AllocateTableCache(tableTag, size);
}
private FontTable _gsubTable;
private FontTable _gposTable;
private FontFaceLayoutInfo _layout;
}
///
/// A unicode range identified by a pair of first and last unicode code point
///
internal struct UnicodeRange
{
internal UnicodeRange(int first, int last)
{
firstChar = first;
lastChar = last;
}
internal int firstChar;
internal int lastChar;
}
///
/// Major language targetted for optimization
///
internal static class MajorLanguages
{
///
/// check if input langSys is considered a major language.
///
/// true if it is a major language
internal static bool Contains(LanguageTags langSys)
{
if (langSys == LanguageTags.Default) return true;
for (int i = 0; i < majorLanguages.Length; i++)
{
if (majorLanguages[i].LangSys == langSys)
return true;
}
return false;
}
///
/// check if input culture is considered a major language.
///
/// true if it is a major language
internal static bool Contains(CultureInfo culture)
{
if (culture == null) return false;
// explicitly check for InvariantCulture. We don't need to check for its parent.
if (culture == CultureInfo.InvariantCulture) return true;
for (int i = 0; i < majorLanguages.Length; i++)
{
if (majorLanguages[i].Culture.Equals(culture)
|| majorLanguages[i].Culture.Equals(culture.Parent)
)
{
return true;
}
}
return false;
}
// major languages
private static readonly LangSysCulturePair[] majorLanguages = new LangSysCulturePair[]
{
new LangSysCulturePair(new CultureInfo("en"), LanguageTags.English), // English neutral culture
new LangSysCulturePair(new CultureInfo("de"), LanguageTags.German), // German neutral culture
new LangSysCulturePair(new CultureInfo("ja"), LanguageTags.Japanese) // Japanese neutral culture
};
private struct LangSysCulturePair
{
internal LangSysCulturePair(CultureInfo culture, LanguageTags langSys)
{
Culture = culture;
LangSys = langSys;
}
internal readonly CultureInfo Culture;
internal readonly LanguageTags LangSys;
}
}
///
/// An enum flag indicating the availabilities of various open type
/// look ups.
///
///
/// The enum is used to determine whether fast path is applicable.
/// Ideo refers to Ideographs
/// FastText refers to Other optimizable text
/// We keep a minimum set of flags here to allow us reliably optimize
/// the most common inputs. It is not to prevent under-optimization for
/// all cases.
///
[Flags]
internal enum TypographyAvailabilities
{
///
/// No required OpenType typography features is available
///
None = 0,
///
/// There are some lookup available for required typography
/// features
///
Available = 1,
///
/// There are some lookup available for required typography features
/// for Ideographic script.
///
IdeoTypographyAvailable = 2,
///
/// There are lookup available for required typography features
/// for fast text
///
FastTextTypographyAvailable = 4,
///
/// There are localized form available for major Ui lanaguages for fast text
///
/// MajorLanguages class
FastTextMajorLanguageLocalizedFormAvailable = 8,
///
/// There are localized form for non major Ui language available for fast text
///
FastTextExtraLanguageLocalizedFormAvailable = 16,
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DefaultValueAttribute.cs
- ContentValidator.cs
- CodeNamespace.cs
- Label.cs
- Int16Animation.cs
- RoutedEventValueSerializer.cs
- Fonts.cs
- ListViewUpdateEventArgs.cs
- AspNetRouteServiceHttpHandler.cs
- XmlSchemaSimpleTypeUnion.cs
- Pkcs7Signer.cs
- SizeValueSerializer.cs
- BamlLocalizationDictionary.cs
- XmlMapping.cs
- AuditLog.cs
- DbProviderFactories.cs
- TableColumn.cs
- VerificationAttribute.cs
- AccessorTable.cs
- CurrentChangingEventManager.cs
- NameValueConfigurationCollection.cs
- AutomationAttributeInfo.cs
- PeerNameRegistration.cs
- SByteStorage.cs
- Crypto.cs
- EntityDataSourceConfigureObjectContext.cs
- DockPatternIdentifiers.cs
- ParameterRetriever.cs
- PersonalizationAdministration.cs
- GridViewAutoFormat.cs
- Variable.cs
- AttachmentService.cs
- WindowsListViewGroup.cs
- typedescriptorpermissionattribute.cs
- StrongNameIdentityPermission.cs
- ConfigXmlWhitespace.cs
- DeferredRunTextReference.cs
- AccessText.cs
- ContainerTracking.cs
- ConstraintConverter.cs
- AsyncContentLoadedEventArgs.cs
- ScriptControlDescriptor.cs
- FrameworkContentElement.cs
- BCLDebug.cs
- SymLanguageVendor.cs
- StringFreezingAttribute.cs
- SimpleFileLog.cs
- TableHeaderCell.cs
- PersianCalendar.cs
- DataKeyCollection.cs
- InvalidWMPVersionException.cs
- UrlAuthorizationModule.cs
- DivideByZeroException.cs
- ConfigurationPermission.cs
- MD5CryptoServiceProvider.cs
- WebPartActionVerb.cs
- Binding.cs
- TemplateBuilder.cs
- DropShadowBitmapEffect.cs
- StrokeNode.cs
- HiddenField.cs
- ProviderIncompatibleException.cs
- BidPrivateBase.cs
- ExpressionReplacer.cs
- KeysConverter.cs
- RelatedView.cs
- SingleKeyFrameCollection.cs
- ContentElementAutomationPeer.cs
- PackageRelationship.cs
- CollectionCodeDomSerializer.cs
- KeyValueConfigurationElement.cs
- StorageInfo.cs
- RightNameExpirationInfoPair.cs
- BitmapEffectState.cs
- FormViewInsertedEventArgs.cs
- ForceCopyBuildProvider.cs
- EncryptRequest.cs
- XPathEmptyIterator.cs
- ThousandthOfEmRealDoubles.cs
- SQLBoolean.cs
- TextModifierScope.cs
- SharedUtils.cs
- ObjectParameterCollection.cs
- PtsHelper.cs
- Validator.cs
- InstanceLockQueryResult.cs
- DbConnectionStringCommon.cs
- HttpWrapper.cs
- activationcontext.cs
- Literal.cs
- MultipartIdentifier.cs
- DatePicker.cs
- EntityDataSourceChangingEventArgs.cs
- ControlEvent.cs
- CompatibleIComparer.cs
- documentsequencetextpointer.cs
- MemberDescriptor.cs
- BinaryMethodMessage.cs
- TableLayoutPanelCodeDomSerializer.cs
- InterleavedZipPartStream.cs