Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / 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 IDictionaryGetFamilyNameDictionary() { 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 IDictionaryGetWin32FamilyNameDictionary() { 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 IDictionaryGetFaceNameDictionary() { 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 IDictionaryGetWin32FaceNameDictionary() { 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 IDictionaryGetAdjustedFaceNameDictionary() { 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 IDictionaryGetVersionStringDictionary() { 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 IDictionaryGetCopyrightDictionary() { 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 IDictionaryGetManufacturerNameDictionary() { 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 IDictionaryGetTrademarkDictionary() { 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 IDictionaryGetDesignerNameDictionary() { 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 IDictionaryGetDescriptionDictionary() { 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 IDictionaryGetVendorUrlDictionary() { 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 IDictionaryGetDesignerUrlDictionary() { 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 IDictionaryGetLicenseDescriptionDictionary() { return new LocalizedNameDictionary(_cacher, _layout->licenseDescriptions); } /// /// Critical: This calls into unsafe code to retrieve the sample text /// [SecurityCritical] internal unsafe IDictionaryGetSampleTextDictionary() { 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 IDictionaryMembers 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 IDictionaryin 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 IDictionaryMembers 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 IDictionaryGetFamilyNameDictionary() { 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 IDictionaryGetWin32FamilyNameDictionary() { 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 IDictionaryGetFaceNameDictionary() { 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 IDictionaryGetWin32FaceNameDictionary() { 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 IDictionaryGetAdjustedFaceNameDictionary() { 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 IDictionaryGetVersionStringDictionary() { 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 IDictionaryGetCopyrightDictionary() { 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 IDictionaryGetManufacturerNameDictionary() { 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 IDictionaryGetTrademarkDictionary() { 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 IDictionaryGetDesignerNameDictionary() { 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 IDictionaryGetDescriptionDictionary() { 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 IDictionaryGetVendorUrlDictionary() { 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 IDictionaryGetDesignerUrlDictionary() { 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 IDictionaryGetLicenseDescriptionDictionary() { return new LocalizedNameDictionary(_cacher, _layout->licenseDescriptions); } /// /// Critical: This calls into unsafe code to retrieve the sample text /// [SecurityCritical] internal unsafe IDictionaryGetSampleTextDictionary() { 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 IDictionaryMembers 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 IDictionaryin 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 IDictionaryMembers 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
- DrawingAttributesDefaultValueFactory.cs
- HtmlAnchor.cs
- AspNetSynchronizationContext.cs
- FtpCachePolicyElement.cs
- RealizationContext.cs
- Merger.cs
- PrintDialog.cs
- SQLGuid.cs
- MatrixAnimationBase.cs
- VerificationException.cs
- ByteAnimation.cs
- DataControlField.cs
- HttpListenerRequest.cs
- PreDigestedSignedInfo.cs
- XmlSerializerAssemblyAttribute.cs
- TextProperties.cs
- UdpDiscoveryMessageFilter.cs
- HierarchicalDataBoundControlAdapter.cs
- DataGridItem.cs
- PhysicalFontFamily.cs
- DefaultObjectMappingItemCollection.cs
- DataFormat.cs
- SQLSingle.cs
- _TLSstream.cs
- RelationshipNavigation.cs
- RangeValidator.cs
- KeyEventArgs.cs
- SHA384Managed.cs
- UpdatePanel.cs
- VerticalAlignConverter.cs
- GB18030Encoding.cs
- DataGridViewCheckBoxColumn.cs
- MsmqOutputChannel.cs
- WorkflowQueueInfo.cs
- ThreadInterruptedException.cs
- Int32RectValueSerializer.cs
- PathTooLongException.cs
- DataGridItem.cs
- FrameSecurityDescriptor.cs
- Itemizer.cs
- CacheSection.cs
- ComAdminInterfaces.cs
- TriggerBase.cs
- FontCacheLogic.cs
- AppDomainFactory.cs
- TopClause.cs
- PaperSize.cs
- XPathParser.cs
- HTMLTagNameToTypeMapper.cs
- CompositionAdorner.cs
- BinaryObjectInfo.cs
- ButtonBaseAdapter.cs
- DataServiceRequestException.cs
- PrefixQName.cs
- ExpressionBuilder.cs
- OleDbCommandBuilder.cs
- HttpRawResponse.cs
- WindowsGrip.cs
- ControlDesigner.cs
- XPathScanner.cs
- __Error.cs
- LoginAutoFormat.cs
- DataBindingCollection.cs
- BrushConverter.cs
- Profiler.cs
- SqlDataSourceView.cs
- XmlSignificantWhitespace.cs
- TrustManager.cs
- RelationshipNavigation.cs
- Message.cs
- BitmapSizeOptions.cs
- LineServicesRun.cs
- WinEventWrap.cs
- DataTemplateKey.cs
- XPathConvert.cs
- SHA384Managed.cs
- KeyEvent.cs
- GraphicsPathIterator.cs
- DocumentXPathNavigator.cs
- Activity.cs
- DynamicResourceExtension.cs
- CompressedStack.cs
- BufferModesCollection.cs
- NullableConverter.cs
- validationstate.cs
- Control.cs
- XmlSerializerFactory.cs
- IsolatedStorageFilePermission.cs
- Attachment.cs
- TextCharacters.cs
- AssociatedControlConverter.cs
- XamlTypeMapper.cs
- _CookieModule.cs
- DeclarativeCatalogPart.cs
- TileBrush.cs
- SourceFileBuildProvider.cs
- ResolveMatchesCD1.cs
- OperationDescriptionCollection.cs
- CustomAttributeSerializer.cs
- InvalidFilterCriteriaException.cs