Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / MS / Internal / Shaping / ShapingWorkspace.cs / 1 / ShapingWorkspace.cs
//---------------------------------------------------------------------- // // Microsoft Windows Client Platform // Copyright (C) Microsoft Corporation, 2005 // // File: ShapingWorkspace.cs // // Contents: support for all the shaping engines - all the local // variables for GetGlyphs and helpers are wrapped and // all unsafe pointer manipulation // is done in this class. // // Created: 06-24-2005 by Nick Beal // //----------------------------------------------------------------------- using System; using System.Security; using System.Security.Permissions; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using MS.Internal.FontCache; using MS.Internal.FontFace; using System.Windows.Media; using System.Windows.Media.TextFormatting; using MS.Internal.PresentationCore; using MS.Utility; namespace MS.Internal.Shaping { ////// ShapingWorkspace provides safe interactions with the various buffers /// used by GetGlyphs. /// ////// This struct is created every time a shaping engine's GetGlyphs() method is called. /// /// All interaction on the unsafe buffers used for text characters and their related /// buffers (char map, cluster map, etc) done during IShaper.GetGlyphs methods /// are done through this struct's accessors and methods. /// internal struct ShapingWorkspace { // These are shorthand aliaes for setting flags in GetGlyphs context. internal const ushort GlyphFlagsNone = (ushort)(GlyphFlags.Unresolved); internal const ushort GlyphFlagsBase = (ushort)(GlyphFlags.ClusterStart | GlyphFlags.Unresolved); internal const ushort GlyphFlagsZeroWidth = (ushort)(GlyphFlags.ZeroWidth | GlyphFlags.Unresolved); internal const ushort GlyphFlagsDiacritic = (ushort)(GlyphFlags.Diacritic | GlyphFlags.Unresolved); ////// ShapingWorkspace - initializer for GetGlyphs helper currentRun /// ////// This function is always called from IShaper.GetGlyphs. /// ////// Critical: This code accepts checked pointers and extracts /// unsafe pointers. /// [SecurityCritical] unsafe public ShapingWorkspace ( Item item, ShapingOptions shapingFlags, ShaperFontClient shaperFontClient, ShaperBuffers shaperBuffers, CheckedCharPointer chars, int charCount, CheckedCharacterShapingPropertiesPointer charProperties, CheckedUShortPointer charClusterMap) { Invariant.Assert(charCount > 0,"ShapingWorkspace: invalid char count"); _charsCount = (ushort)charCount; // save the shaping directives from our client _forceControlCharsInvisible = ((shapingFlags & ShapingOptions.DisplayControlCode) == 0); _forceInhibitLigature = ((shapingFlags & ShapingOptions.InhibitLigature) != 0); // note if we have a leading ZWJ _hasLeadingJoin = ((item.Flags & ItemFlags.LeadingJoin) != 0); // initialize our shaper buffers and set up our local references shaperBuffers.Initialize( (ushort)charCount, (ushort)charCount); _charMap = shaperBuffers.CharMap; _glyphInfoList = shaperBuffers.GlyphInfoList; // keep track of our font client, char converter _fontClient = shaperFontClient; _charConverter = _fontClient.CharConverter; // unpack the pointer containers and initialize our pointer _pChars = chars.Probe(0, charCount); _pCharProps = charProperties.Probe(0, charCount); _pClusters = charClusterMap.Probe(0, charCount); _pShapes = (CharShapeInfo *)_pClusters; // initialize our various counts _addedGlyphsCount = 0; _nextCharIx = _nextGlyphIx = 0; _lastCharIx = (ushort)(charCount - 1); // initialize our local booleans _inited = true; _isAutoInsert = _controlCharsSeen = _finishedShaping = false; _isSerializedClusterEntriesRequired = true; } // various accessors, function... // I have laid these out alphabetically. If a particular variable has // an accessor with a different name than the variable, I've added a // comment in the position of the variable... ////// AddGlyphs - add more glyphs to glyphinfolist /// ////// This function can be used to increase or decrease the logical /// size of the glyph info lists. /// If increasing the size the GlyphInfoList is not actually /// resized, but the _addedGlyphsCount is incremented (the actual /// resize will occur later - this is to minimize the overhead of /// resizing). The resizing will actually occur when GlyphsCount /// accessor is invoked. /// If decreasing the logical size of the glyph info lists, a check /// is made so that any pending added glyphs are taken into account /// in the logical resizing and the _nextGlyphIx is adjusted if it /// is affected by the reduction of the list size. /// ////// Critical - The method calls critical code (GlyphInfoList.SetLength) /// [SecurityCritical] public int AddGlyphs(int addCount) { if (addCount < 0) { int glyphsCount = GlyphInfoList.Length; // we're decreasing the extent of our glyphs list // NOTE: we saved glyphsCount above because // GlyphsCount may increase the glyphInfoList // size (if there're pending "add glyphs") if ((GlyphsCount + addCount) > 0) { GlyphInfoList.SetLength (GlyphsCount + addCount); } if (_nextGlyphIx >= glyphsCount + addCount) { _nextGlyphIx = (ushort)((int)_nextGlyphIx + addCount); } } else { _addedGlyphsCount += addCount; } return addCount; } ////// AddLigatureChars - Updates the current glyph info's ligature count. /// ////// This function updates the current glyph info's ligature count and /// maps the charmap, cluster (or shape) values for each of the following /// chars that are part of the ligature. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void AddLigatureChars(int baseCharOffset, ushort ligaturesCount) { if (ligaturesCount > 1) { Debug.Assert(baseCharOffset == -1 || baseCharOffset == 0,"AddLigaturChars: unexpected base offset"); Invariant.Assert((ushort)(_nextCharIx + baseCharOffset + ligaturesCount) <= _charsCount, "AddLigatureChars: invalid input params"); _nextCharIx = (ushort)(_nextCharIx + baseCharOffset); ushort glyphIx = _charMap[_nextCharIx]; ushort clusterIxOrShape = FontClient.IsScriptSupportedByFont ? (ushort)CharShapeInfo.NoFlagsSet : _pClusters[_nextCharIx]; _glyphInfoList.LigatureCounts[ glyphIx ] = ligaturesCount; AddGlyphs( -(ligaturesCount - 1) ); // reduce size of GlyphInfoList while (--ligaturesCount > 0) { ++_nextCharIx; _charMap[_nextCharIx] = glyphIx; _pClusters[_nextCharIx] = clusterIxOrShape; } } } public bool AreLigaturesInhibited { get { return _forceInhibitLigature; } } public IScriptCharConverter CharConverter { get { return _charConverter; } } public UshortList CharMap // also see CurrentGlyphIx { get { return _charMap; } } public ushort CharsCount { get { return _charsCount; } } ////// ShapingWorkspace.CompileShapingProperties - helper function for GetGlyphs. /// ////// This is the last helper function called by shaping engine's GetGlyph call. /// All the glyphs and glyph properties resulting from shaping the text is trans- /// ferred into the output arrays/lists. /// ////// Critical: This code is unsafe /// [SecurityCritical] unsafe public void CompileShapingProperties ( bool forceDiacriticsToZeroWidth, out int glyphCount, out ushort[] glyphIndices, out GlyphShapingProperties[] glyphProperties ) { Invariant.Assert(_charsCount < ushort.MaxValue, "CompileShapingProperties, too many characters"); Invariant.Assert(_glyphInfoList.Length > 0, "CompileShapingProperties, glyphsCount <= 0"); ushort firstClusterGlyph = 0; ushort firstClusterChar = 0; ushort afterClusterGlyph; ushort afterClusterChar; ushort markFlags = (ushort)(forceDiacriticsToZeroWidth ? (GlyphFlags.Diacritic | GlyphFlags.ZeroWidth) : GlyphFlags.Diacritic); glyphCount = _glyphInfoList.Length ; glyphIndices = _glyphInfoList.Glyphs.GetCopy(); // create output copy of glyphs glyphProperties = new GlyphShapingProperties[glyphCount]; // Go through all the characters and compile shaping properties. Mapping is local // to cluster, so doing every cluster as a whole. for(int iChar = 1; iChar <= _charsCount; iChar++) { if (iChar < _charsCount && _pClusters[iChar] == firstClusterGlyph) { //We are still inside same cluster continue; } // New cluster found. Get boundaries afterClusterChar = (ushort)iChar; afterClusterGlyph = (iChar < _charsCount) ? _pClusters[iChar] : (ushort)glyphCount; //Cluster map should come valid from shaping engine Debug.Assert(firstClusterGlyph < afterClusterGlyph); int localMap = 0; for(int iClusterChar = firstClusterChar; iClusterChar < afterClusterChar; iClusterChar++) { // Clusters should be independent, otherwise they should be merged by shaping engine Debug.Assert(_charMap[iClusterChar] >= firstClusterGlyph); Debug.Assert(_charMap[iClusterChar] < afterClusterGlyph); if (_isSerializedClusterEntriesRequired && iClusterChar > firstClusterChar) { // Its possible that the clusters have mappings that will make line services // uphappy. An example is "Ra+halant+Ra+halant+Ra+halant+Ra+halant+Ra+halant" // The first "Ra+halant" will be a reph, repositioned to the end of the cluster. // The font may have formed a glyph from the base // "Ra" and the final "halant", so that the glyphs' first character values will be // (relative to the start of the cluster) 0,1,3,6 and the ligature counts will // be 2,2,2,2. The character map will be 0,1,1,3,3,0,4,4 (ie, the cluster's 1st // and 6th character map to the 1st glyph). We'll discover that here if the // current char is mapped to an earlier glyph. int prevCharIx = iClusterChar - 1; if (_charMap[iClusterChar] < _charMap[prevCharIx]) { // this cluster has "interesting" mapping. LS (Line Services) // doesn't like interesting mapping so lets remap cluster so that // all the entries in the map are sequential. ushort glyphIx = _charMap[iClusterChar]; _charMap[iClusterChar] = _charMap[prevCharIx]; while (prevCharIx > firstClusterChar && _charMap[prevCharIx] != glyphIx) { _charMap[prevCharIx] = _charMap[prevCharIx - 1]; _pCharProps[prevCharIx].EngineReserved = (byte)(_charMap[prevCharIx] - firstClusterGlyph); --prevCharIx; } ushort newGlyphIx = _charMap[iClusterChar]; while (++glyphIx <= newGlyphIx) { _glyphInfoList.FirstChars[glyphIx] += 1; } } } localMap = _charMap[iClusterChar] - firstClusterGlyph; // Shaping porperties do not support more than 255 glyph/chars in single cluster. // if cluster is longer than 255 glyphs, make everything beyond that map to glyph 255 if (localMap > 255) { localMap = 255; } // Only set canGlyphAlone on space when it is 1:1 mapping with the glyph. bool canGlyphAlone = (Classification.CharAttributeOf((int)Classification.GetUnicodeClassUTF16(_pChars[iClusterChar])).Flags & (ushort)CharacterAttributeFlags.CharacterSpace) != 0 && afterClusterChar - firstClusterChar == 1 // cluster is 1 char long && afterClusterGlyph - firstClusterGlyph == 1; // cluster is 1 glyph long _pCharProps[iClusterChar].CanGlyphAlone = canGlyphAlone; _pCharProps[iClusterChar].EngineReserved= (byte)localMap; } // make sure the start of the cluster is marked... // set the cluster start flag for the last cluster _glyphInfoList.GlyphFlags[firstClusterGlyph] |= (ushort)GlyphFlags.ClusterStart; // go through all the glyphs of the cluster for(int iClusterGlyph = firstClusterGlyph; iClusterGlyph < afterClusterGlyph; iClusterGlyph++) { ushort firstChar = _glyphInfoList.FirstChars[iClusterGlyph]; ushort ligaCount = _glyphInfoList.LigatureCounts[iClusterGlyph]; ushort glyphFlags = _glyphInfoList.GlyphFlags[iClusterGlyph]; // check for any adjustments that need to be made in the glyph // flags // First, validate the glyph id if (glyphIndices[iClusterGlyph] > FontClient.GetMaxGlyphId) { // invalid glyph index (too big to be valid), so just hide it. glyphFlags = GlyphFlagsZeroWidth; glyphIndices[iClusterGlyph] = FontClient.SpaceGlyph; } // If the glyph's missing... if ((glyphFlags & (ushort)GlyphFlags.Missing) != 0) { // for missing glyphs, check if we know something about it... // (check if its a unicode control character) if ((glyphFlags & (ushort)GlyphFlags.ZeroWidth) != 0) { glyphIndices[iClusterGlyph] = FontClient.SpaceGlyph; glyphFlags = GlyphFlagsZeroWidth; } } // if its a mark glyph ... else if ( ((glyphFlags & (ushort)GlyphFlags.GlyphTypeMask) == (ushort)GlyphFlags.Mark) || (iClusterGlyph != firstClusterGlyph && (glyphFlags & (ushort)GlyphFlags.GlyphTypeMask) == (ushort)GlyphFlags.Unresolved) ) { // set our diacritic glyph flags. glyphFlags = (ushort)(glyphFlags | markFlags); } // check if this is a unicode control char (ZWJ, etc) and we want to hide // such... else if ((glyphFlags & (ushort)GlyphFlags.ZeroWidth) != 0 && _forceControlCharsInvisible && _controlCharsSeen && FontClient.IsUnicodeControlGlyph(glyphIndices[iClusterGlyph])) { glyphIndices[iClusterGlyph] = FontClient.SpaceGlyph; glyphFlags = GlyphFlagsZeroWidth; } // Clusters should be independent, otherwise they should be merged by shaping engine Debug.Assert(firstChar >= firstClusterChar); Debug.Assert(firstChar < afterClusterChar); localMap = firstChar - firstClusterChar; // Shaping porperties do not support more than 255 glyph/chars in single cluster. // if cluster is longer than 255 glyphs, make everything beyond that map to glyph 255 if (localMap >= 255) { localMap = 255; } if (ligaCount > 255) { ligaCount = 255; } glyphProperties[iClusterGlyph] = new GlyphShapingProperties(glyphFlags, (ushort)((ligaCount << 8) | localMap) ); } firstClusterChar = afterClusterChar; firstClusterGlyph = afterClusterGlyph; } } // the "Current???" accessors are used in the various engines shaping loops ////// CurrentChar - returns current char /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public char CurrentChar { [SecurityCritical] get { return _pChars[_nextCharIx]; } } ////// CurrentCharIx - returns current char index /// public ushort CurrentCharIx { get { return (ushort)_nextCharIx; } } ////// CurrentGlyph - sets/gets current glyph /// public ushort CurrentGlyph { get { return _glyphInfoList.Glyphs[ _charMap[_nextCharIx] ]; } set { _glyphInfoList.Glyphs[ _charMap[_nextCharIx] ] = value; } } ////// CurrentShape - returns/sets current char shape info /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public CharShapeInfo CurrentShape { [SecurityCritical] get { return _pShapes[_nextCharIx]; } [SecurityCritical] set { _pShapes[_nextCharIx] = value; } } public ShaperFontClient FontClient { get { return _fontClient; } } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] unsafe public char GetChar(ushort charIx) { return (charIx < _charsCount) ? _pChars[charIx] : '\u0000'; } public ushort GetGlyph(ushort glyphIx) { return _glyphInfoList.Glyphs[glyphIx]; } public ushort GetGlyphIx(ushort charIx) { return _charMap[charIx]; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] public bool GetNextCharProperties (out char nextChar, out ushort nextGlyph, out CharShapeInfo nextShape) { ToNextChar(); nextChar = CurrentChar; nextGlyph = CharConverter.ToGlyph(nextChar); nextShape = CharConverter.ToShapeInfo(nextChar); return !_finishedShaping; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] public bool GetNextChar(out char nextChar) { ToNextChar(); nextChar = CurrentChar; return !_finishedShaping; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] public bool GetNextShape(out CharShapeInfo nextShape) { ToNextChar(); nextShape = CharConverter.ToShapeInfo(CurrentChar); return !_finishedShaping; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] unsafe public CharShapeInfo GetShapeInfo(int charIx) { return (CharShapeInfo)((charIx >= 0 && charIx < _charsCount) ? _pShapes[charIx] : CharShapeInfo.NoFlagsSet); } ////// GlyphsCount - returns glyphs count /// public int GlyphsCount { get { if (_nextGlyphIx == GlyphInfoList.Length && _addedGlyphsCount > 0) { // if we are at the end of the glyph list and there're deferred // glyph additions, add the deferred glyphs now. _isAutoInsert = true; // inhibit updating "inserted" glyphs InsertGlyphs((ushort)GlyphInfoList.Length,(ushort)_addedGlyphsCount); _addedGlyphsCount = 0; } return GlyphInfoList.Length; } } public GlyphInfoList GlyphInfoList { get { return _glyphInfoList; } } public bool HasLeadingJoin { get { return _hasLeadingJoin; } } public bool HideControlChars { get { return _forceControlCharsInvisible && _controlCharsSeen; } } public bool IsFinished { get { return _finishedShaping; } } ////// InsertGlyphs - insert more glyphs into glyphinfolist /// public int InsertGlyphs(ushort insertionPoint, ushort insertCount) { int insertedCount = 0; int glyphsCount = GlyphInfoList.Length; if ( insertionPoint < glyphsCount ) { GlyphInfoList.Insert(insertionPoint, insertCount); if (GlyphInfoList.Length > glyphsCount) { insertedCount = GlyphInfoList.Length - glyphsCount; } } else if ( insertionPoint == glyphsCount ) { if (_inited) GlyphInfoList.Insert( 0, insertCount > CharsCount ? insertCount : CharsCount); else GlyphInfoList.Insert(glyphsCount, insertCount); if (GlyphInfoList.Length > glyphsCount) { insertedCount = GlyphInfoList.Length - glyphsCount; } } // if we have inserted glyphs in front of the current glyph index, // increment the glyph index. The assumption is made that the // inserted glyph(s) should be mapped to the same character as the // first glyph that's being displaced (so we don't adjust the // charmap entry that points to this glyph position) if (!_inited && !_isAutoInsert && insertionPoint <= _nextGlyphIx && _nextGlyphIx <= glyphsCount) { for (int i = 0; i <= _nextCharIx; ++i) { if (_charMap[i] > insertionPoint) { _charMap[i] += (ushort)insertedCount; } } // normally, "afterInsertionGlyphIx" will be the first glyph position // after the inserted members of GlyphInfoList. There's one exception; // when the insertion point is at glyphsCount (this implies that // _nextGlyphIx also is at glyphsCount) we are "inserting" glyphs // "in front of" the last glyph but the insertion code above actually // added the elements to the end of the glyphs array. So, in this end // case, the inserted elements are set up from the last entry in the glyphs // array (last before we added these glyphs, of course). ushort afterInsertionGlyphIx = insertionPoint < glyphsCount ? (ushort)(insertionPoint + insertedCount) : (ushort)(glyphsCount - 1); Debug.Assert(afterInsertionGlyphIx < glyphsCount + insertedCount,"improper use of InsertGlyphs"); for (int i = 0; i < insertedCount; ++i) { GlyphInfoList.FirstChars[insertionPoint + i] = GlyphInfoList.FirstChars[afterInsertionGlyphIx]; GlyphInfoList.LigatureCounts[insertionPoint + i] = GlyphInfoList.LigatureCounts[afterInsertionGlyphIx]; GlyphInfoList.GlyphFlags[insertionPoint + i] = GlyphInfoList.GlyphFlags[afterInsertionGlyphIx]; } _nextGlyphIx = (ushort)(_nextGlyphIx + insertedCount); } _isAutoInsert = false; // always reset this return insertedCount; } public bool MoveGlyphs (ushort destGlyphIx, ushort srcGlyphIx, ushort moveCount ) { ushort glyphsCount = (ushort)_glyphInfoList.Length; if (srcGlyphIx + moveCount <= srcGlyphIx || // make sure no overflow destGlyphIx + moveCount <= destGlyphIx || // make sure no overflow (srcGlyphIx + moveCount - 1) >= glyphsCount || // and make sure valid move arguments (destGlyphIx + moveCount - 1) >= glyphsCount) // and make sure valid move arguments { return false; } if (srcGlyphIx < destGlyphIx && srcGlyphIx + moveCount > destGlyphIx) { // buffers overlap, copy from end of src buffer // src: ++++++++++++++++++++++++++++ // dest: ++++++++++++++++++++++++++++ srcGlyphIx += moveCount; destGlyphIx += moveCount; while (moveCount-- > 0) { _glyphInfoList.Glyphs[--destGlyphIx] = _glyphInfoList.Glyphs[--srcGlyphIx]; } } else if (srcGlyphIx != destGlyphIx) { while (moveCount-- > 0) { _glyphInfoList.Glyphs[destGlyphIx++] = _glyphInfoList.Glyphs[srcGlyphIx++]; } } return true; } ////// NextChar - returns next char /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public char NextChar { [SecurityCritical] get { return (_nextCharIx + 1 < _charsCount ) ? _pChars[_nextCharIx + 1] : (char)0; } } ////// NextGlyphIx - returns/sets next glyphIx /// public ushort NextGlyphIx { get { return _nextGlyphIx; } } ////// ShapingWorkspace.PopulateClusterMap - helper function for GetGlyphs. /// ////// This function creates the cluster map based on shape flags. The /// shape engine will have set the IsStartOfCluster flag for all /// characters that start a cluster. (CAUTION: the cluster array /// and the shape flag array are actually the same memory block /// so don't try to use a given character's shape info after /// setting its cluster info. ) /// ////// Critical - uses pointers, unsafe code /// [SecurityCritical] unsafe public void PopulateClusterMap () { if (!FontClient.IsFeatureTypeSupportedByFont) return; // nothing to do (SetCurrentClusterInfo already set the map up) ushort *clusterMap = _pClusters; Invariant.Assert(_glyphInfoList.Length > 0, "PopulateClusterMap, glyphsCount <= 0"); Reset(0,0,_charsCount); // Step 1. Create the cluster map based on the shape flags... // this loop goes through all the shapes (one per Unicode character) and // creates the cluster map. When each start of cluster is found the // associated glyph is assumed to be the value for this new cluster unless // there's reason to suspect its not. int firstGlyphInCluster = 0; int firstCharInCluster = 0; int earliestGlyphRef = 0; int earliestCharRef = 0; int currentCharIx; int currentGlyphIx; int currentGlyphFirstChar; // add the glyphs to the glyph array while (ToNextChar()) { currentCharIx = _nextCharIx; currentGlyphIx = _charMap[_nextCharIx]; Invariant.Assert(currentGlyphIx < _glyphInfoList.Length, "illegal glyph ix in charMap"); // It is possible that this current character is part of a ligature glyph that actually spans // multiple clusters (including a previous cluster, so check the glyph's FirstChar // clustermap entry (it might be even earlier than currentGlyphIx) currentGlyphFirstChar = (int)_glyphInfoList.FirstChars[currentGlyphIx]; if (currentGlyphFirstChar < firstCharInCluster) { // Since currentGlyphFirstChar < firstCharInCluster, clusterMap[currentGlyphFirstChar] // is already filled with its glyph id so we can use it and check if the glyph it // references is earlier than what we currently know about. if (clusterMap[currentGlyphFirstChar] < earliestGlyphRef) { earliestGlyphRef = clusterMap[currentGlyphFirstChar]; } if (currentGlyphFirstChar < earliestCharRef) { earliestCharRef = currentGlyphFirstChar; } } // As each character is processed, we'll keep track of the earliest glyph referenced // in the glyphs that are part of the current cluster. This information will be used // when the next start of cluster is detected. if (currentGlyphIx < earliestGlyphRef) { earliestGlyphRef = currentGlyphIx; // save this earlier glyph reference } if ((CurrentShape & (CharShapeInfo.IsStartOfCluster)) != 0) { // starting a new cluster... if (currentGlyphIx == earliestGlyphRef) { // we will normally be here for the first glyph, but otherwise // this seems an unlikely case; if here then this is not actually // the end of one cluster and the beginning of another, but rather // a concatenation of two clusters. } else { // this glyph starts a new cluster so go back over the glyphs in the preceding // cluster to verify that all is well. It may be that we have discovered new // information (ie, membership in cluster includes earlier glyphs or characters) // since the first char in the last cluster. if (earliestGlyphRef < firstGlyphInCluster || earliestCharRef < firstCharInCluster) { // we need to change the last cluster's extent and/or its value! firstGlyphInCluster = earliestGlyphRef; firstCharInCluster = earliestCharRef; for (int i = firstCharInCluster; i < currentCharIx; ++i) { clusterMap[i] = (ushort)firstGlyphInCluster; } } // As long as this char (the first in a new cluster) is after the current cluster's // first char, set up the counters for this new cluster. Otherwise, since this // character is before the current cluster's first character (must have been // updated in the if block just above) don't change the current cluster. if (firstCharInCluster < currentCharIx) { // keep track of the first char in cluster firstCharInCluster = currentCharIx; firstGlyphInCluster = currentGlyphIx; if (currentGlyphFirstChar < currentCharIx) { earliestCharRef = currentGlyphFirstChar; earliestGlyphRef = clusterMap[earliestCharRef]; } else { earliestCharRef = currentCharIx; earliestGlyphRef = currentGlyphIx; } } } } clusterMap[currentCharIx] = (ushort)firstGlyphInCluster; // save this char's cluster info } // If the last character is the last character in the last cluster (ie, if // it doesn't have the StartOfCluster flag ), then need to check // update the cluster info. Check "earliestCharRef" to determine this; normally, // if the last character has its StartOfCluster flag set, "earliestCharRef" will // be the index of the last character. if (earliestCharRef + 1 < CharsCount) { // check if final cluster adjustment is needed if (earliestGlyphRef < firstGlyphInCluster || earliestCharRef < firstCharInCluster) { // we need to change the last cluster's extent and/or its value! for (int i = earliestCharRef; i < CharsCount; ++i) { clusterMap[i] = (ushort)(earliestGlyphRef); } } } Invariant.Assert( clusterMap[0] == 0, "first cluster map entry is not zero"); } ////// PreviousChar - returns previous char /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public char PreviousChar { [SecurityCritical] get { return _pChars[ PreviousCharIx ]; } } ////// PreviousCharIx - returns previous char index /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public ushort PreviousCharIx { get { return (_inited ? _nextCharIx : (_nextCharIx == 0 ? (ushort) 0 : ( _nextCharIx >= _charsCount ? (ushort)(_charsCount- 1) : (ushort)(_nextCharIx - 1)))); } } ////// PreviousGlyph - sets/gets previous glyph /// public ushort PreviousGlyph { get { return _inited ? (ushort) 0 : _glyphInfoList.Glyphs[ PreviousGlyphIx ]; } set { _glyphInfoList.Glyphs[ PreviousGlyphIx ] = value; } } ////// PreviousGlyphIx - gets previous glyph index /// public ushort PreviousGlyphIx { get { return _inited ? (ushort) _nextGlyphIx : _charMap[ PreviousCharIx ]; } } ////// PreviousShape - gets previous char shape /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public CharShapeInfo PreviousShape { [SecurityCritical] get { return PreviousCharIx == CurrentCharIx ? CharShapeInfo.NoFlagsSet : _pShapes[ PreviousCharIx ]; } } ////// ForceSerializedCluster - /// public bool IsForceSerializedClusterOn { set { _isSerializedClusterEntriesRequired = value; } } ////// ShapingWorkspace.Reset - used to re-initial the MoveToNextChar mechanism /// (our iterator function, if you please!). This flavor of the /// reset function just resets the iteration booleans without moving /// the next character index /// public bool Reset( ) { _inited = true; _finishedShaping = (_nextCharIx > _lastCharIx); return !_finishedShaping; } ////// ShapingWorkspace.Reset - used to re-initial the MoveToNextChar mechanism /// (our iterator function, if you please!). /// public bool Reset( ushort nextCharIx, ushort nextGlyphIx, ushort charCount ) { _nextCharIx = nextCharIx; _nextGlyphIx = nextGlyphIx; _inited = true; _finishedShaping = false; _lastCharIx = (ushort)(nextCharIx + charCount - 1); if (_lastCharIx >= _charsCount) { _lastCharIx = (ushort)(_charsCount - 1); } // we allow nextGlyphIx == GlyphsCount because of the case where // we're adding glyphs syllable by syllable (so we will allocate // the next syllable's glyphs later) Invariant.Assert(_nextCharIx < _charsCount); Invariant.Assert(_nextGlyphIx <= GlyphsCount); return !_finishedShaping; } ////// SetGlyphPropertiesUsingShapeInfo - This is the default call to set up the /// glyphInfoList, charmap, and cluster (if script not supported) /// for the current character. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void SetGlyphPropertiesUsingShapeInfo ( CharShapeInfo currShape ) { char currChar = _pChars[_nextCharIx]; ushort glyph = CharConverter.ToGlyph(currChar); _pShapes[_nextCharIx] = currShape; _charMap[_nextCharIx] = _nextGlyphIx; // set charmap SetGlyphProperties( glyph ); } ////// SetGlyphPropertiesUsingChar - This sets up the /// glyphInfoList, charmap, and cluster (if script not supported) /// for the current character using the passed in character. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void SetGlyphPropertiesUsingChar ( CharShapeInfo currShape, char currChar) { ushort glyph = CharConverter.ToGlyph(currChar); _pShapes[_nextCharIx] = currShape; _charMap[_nextCharIx] = _nextGlyphIx; // set charmap SetGlyphProperties( glyph ); } ////// SetGlyphPropertiesUsingGlyph - This sets up the /// glyphInfoList, charmap, and cluster (if script not supported) /// for the current character using the passed in glyph. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void SetGlyphPropertiesUsingGlyph ( CharShapeInfo currShape, ushort glyph ) { _pShapes[_nextCharIx] = currShape; _charMap[_nextCharIx] = _nextGlyphIx; // set charmap SetGlyphProperties( glyph ); } ////// SetGlyphProperties - This sets up the glyphInfoList /// for the current glyph position and it increments /// _nextGlyphIx. If the script is not supported by this /// font (ie, the font has no OT tables for this script), /// then the cluster map is updated, too. If the font does have /// support for script, then shaping info is valid in _pShapes; /// so if this character's shape indicates that we desire /// a dotted circle glyph inserted then this is done. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void SetGlyphProperties ( ushort glyph ) { // Be aware of the side effect of GlyphsCount; it is necessary // that the comparison below be done if there're added glyphs // to be considered. Invariant.Assert(_nextGlyphIx < GlyphsCount, "SetGlyphProperties called with invalid glyphIx"); // fill this character's glyph info list entries _glyphInfoList.Glyphs[_nextGlyphIx] = glyph; _glyphInfoList.FirstChars[_nextGlyphIx] = (ushort)_nextCharIx; _glyphInfoList.LigatureCounts[_nextGlyphIx] = 1; _glyphInfoList.GlyphFlags[_nextGlyphIx] = GlyphFlagsNone; if (glyph == 0) { _glyphInfoList.GlyphFlags[_nextGlyphIx] |= (ushort)GlyphFlags.Missing; } // check the shape flags and take appropriate action CharShapeInfo currentShape = _pShapes[_nextCharIx]; // if this shape indicates that here is an inserted base insertion // point, insert the dotted circle. if ((currentShape & CharShapeInfo.RequiresInsertedBase) == CharShapeInfo.RequiresInsertedBase) { ushort dottedCircleGlyph = FontClient.DottedCircleGlyph; if (dottedCircleGlyph != 0) { InsertGlyphs(_nextGlyphIx,1); // this will increment _nextGlyphIx Invariant.Assert(_nextGlyphIx < GlyphsCount, "Invalid glyph ix for insertion of invalid base"); _glyphInfoList.Glyphs[_nextGlyphIx - 1] = dottedCircleGlyph; _glyphInfoList.GlyphFlags[_nextGlyphIx - 1] = (ushort)GlyphFlags.InvalidBase; } } else if( (currentShape & CharShapeInfo.IsUnicodeLayoutControl) != 0) { // This is a Unicode control glyph. Let font client know // so it can save glyph id if it wants to (may be used to // suppress visible Unicode control characters later) FontClient.SetUnicodeControlGlyph(CurrentChar, glyph); _controlCharsSeen = true; _glyphInfoList.GlyphFlags[_nextGlyphIx] = GlyphFlagsZeroWidth; } // If font doesn't have feature support, just set up the // cluster info directly if ( !FontClient.IsFeatureTypeSupportedByFont ) { SetCurrentClusterInfo(currentShape, glyph); } // set next glyph (allow to go one beyond the last to detect the last glyph // being written if ((ushort)( _nextGlyphIx + 1) <= GlyphsCount) { _nextGlyphIx = (ushort)( _nextGlyphIx + 1); } } ////// SetCurrentClusterInfo - This sets up the glyphInfoList /// for the current glyph position and it increments /// _nextGlyphIx. If the script is not supported by this /// font (ie, the font has no OT tables for this script), /// then the cluster map is updated, too. If the font does have /// support for script, then shaping info is valid in _pShapes; /// so if this character's shape indicates that we desire /// a dotted circle glyph inserted then this is done. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void SetCurrentClusterInfo ( CharShapeInfo currentShape, ushort glyph ) { bool startOfCluster = (currentShape & CharShapeInfo.IsStartOfCluster) != 0; // font doesn't have feature support, so just set up the // cluster info directly if( (currentShape & CharShapeInfo.IsUnicodeLayoutControl) != 0) { // For fonts that don't have feature support for this // script - if the user doesn't want to actually see // the control characters, replace them with a blank glyph. // (For fonts with support, we do this later after feature // application) if (_forceControlCharsInvisible) { _glyphInfoList.Glyphs[_nextGlyphIx] = FontClient.SpaceGlyph; } } else if (startOfCluster) { if (glyph == 0) { _glyphInfoList.GlyphFlags[_nextGlyphIx] = GlyphFlagsBase | (ushort)GlyphFlags.Missing; } else { _glyphInfoList.GlyphFlags[_nextGlyphIx] = GlyphFlagsBase; } } else { _glyphInfoList.GlyphFlags[_nextGlyphIx] = GlyphFlagsDiacritic; } _pClusters[_nextCharIx] = ( _nextCharIx == 0 || startOfCluster ? _nextGlyphIx : _pClusters[PreviousCharIx]); _charMap[_nextCharIx] = _nextGlyphIx; // set cluster map value } ////// SetGlyph - /// public void SetGlyph (ushort glyphIx, ushort glyph) { _glyphInfoList.Glyphs[glyphIx] = glyph; } ////// SetGlyph - /// public void SetGlyph (ushort glyphIx, ushort glyph, ushort firstChar) { _glyphInfoList.Glyphs[glyphIx] = glyph; _glyphInfoList.FirstChars[glyphIx] = firstChar; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] unsafe public bool SetNextGlyphProperties (out CharShapeInfo nextShape) { if (ToNextChar()) { char nextChar = CurrentChar; nextShape = CharConverter.ToShapeInfo(nextChar); _pShapes[_nextCharIx] = nextShape; _charMap[_nextCharIx] = _nextGlyphIx; // set charmap SetGlyphProperties( CharConverter.ToGlyph(nextChar) ); } else { nextShape = CharShapeInfo.NoFlagsSet; } return !_finishedShaping; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] unsafe public void SetShapeInfo(ushort charIx, CharShapeInfo charShape) { if (charIx >= 0 && charIx < _charsCount) { _pShapes[charIx] = charShape; } } ////// ToNextChar - increments index, returns false if finished /// public bool ToNextChar() { if (_inited) { // if this is the first character, don't increment the // index (so we can use this in a while (MoveToNextChar){...} // loop _inited = false; } else if (_nextCharIx < _lastCharIx) { ++_nextCharIx; } else { _finishedShaping = true; Invariant.Assert(_nextCharIx < _charsCount); } return !_finishedShaping; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] public void UpdateCurrentGlyphProperties (CharShapeInfo newShape) { if ((CurrentShape & CharShapeInfo.RequiresInsertedBase) == CharShapeInfo.RequiresInsertedBase && (newShape & CharShapeInfo.RequiresInsertedBase) != CharShapeInfo.RequiresInsertedBase ) { AddGlyphs(-1); } _nextGlyphIx = _charMap[_nextCharIx]; // set glyph ix back SetGlyphPropertiesUsingGlyph( newShape, CurrentGlyph ); } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] public void ValidateGlyphsOut (ushort[] glyphs) { bool areAllGlyphsZero = true; for (int i = 0; i < glyphs.Length; ++i) { if (glyphs[i] != 0) areAllGlyphsZero = false; } Debug.Assert(areAllGlyphsZero,"ValidateGlyphsOut found an all zero'ed glyphs"); } private int _addedGlyphsCount; private ushort _charsCount; // number of chars in run. private IScriptCharConverter _charConverter; private bool _controlCharsSeen; private bool _finishedShaping; private ShaperFontClient _fontClient; private bool _forceControlCharsInvisible; private bool _forceInhibitLigature; private bool _inited; private bool _isAutoInsert; private bool _hasLeadingJoin; private bool _isSerializedClusterEntriesRequired; private ushort _nextCharIx; private ushort _nextGlyphIx; ////// Critical: Holds reference to an unsafe pointer /// [SecurityCritical] private unsafe char* _pChars; // the Unicode text buffer [SecurityCritical] private unsafe CharacterShapingProperties* _pCharProps; // char properties buffer [SecurityCritical] private unsafe ushort* _pClusters; // clusterMap buffer [SecurityCritical] private unsafe CharShapeInfo* _pShapes; // char shapes buffer private UshortList _charMap; private GlyphInfoList _glyphInfoList; private ushort _lastCharIx; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------- // // Microsoft Windows Client Platform // Copyright (C) Microsoft Corporation, 2005 // // File: ShapingWorkspace.cs // // Contents: support for all the shaping engines - all the local // variables for GetGlyphs and helpers are wrapped and // all unsafe pointer manipulation // is done in this class. // // Created: 06-24-2005 by Nick Beal // //----------------------------------------------------------------------- using System; using System.Security; using System.Security.Permissions; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using MS.Internal.FontCache; using MS.Internal.FontFace; using System.Windows.Media; using System.Windows.Media.TextFormatting; using MS.Internal.PresentationCore; using MS.Utility; namespace MS.Internal.Shaping { ////// ShapingWorkspace provides safe interactions with the various buffers /// used by GetGlyphs. /// ////// This struct is created every time a shaping engine's GetGlyphs() method is called. /// /// All interaction on the unsafe buffers used for text characters and their related /// buffers (char map, cluster map, etc) done during IShaper.GetGlyphs methods /// are done through this struct's accessors and methods. /// internal struct ShapingWorkspace { // These are shorthand aliaes for setting flags in GetGlyphs context. internal const ushort GlyphFlagsNone = (ushort)(GlyphFlags.Unresolved); internal const ushort GlyphFlagsBase = (ushort)(GlyphFlags.ClusterStart | GlyphFlags.Unresolved); internal const ushort GlyphFlagsZeroWidth = (ushort)(GlyphFlags.ZeroWidth | GlyphFlags.Unresolved); internal const ushort GlyphFlagsDiacritic = (ushort)(GlyphFlags.Diacritic | GlyphFlags.Unresolved); ////// ShapingWorkspace - initializer for GetGlyphs helper currentRun /// ////// This function is always called from IShaper.GetGlyphs. /// ////// Critical: This code accepts checked pointers and extracts /// unsafe pointers. /// [SecurityCritical] unsafe public ShapingWorkspace ( Item item, ShapingOptions shapingFlags, ShaperFontClient shaperFontClient, ShaperBuffers shaperBuffers, CheckedCharPointer chars, int charCount, CheckedCharacterShapingPropertiesPointer charProperties, CheckedUShortPointer charClusterMap) { Invariant.Assert(charCount > 0,"ShapingWorkspace: invalid char count"); _charsCount = (ushort)charCount; // save the shaping directives from our client _forceControlCharsInvisible = ((shapingFlags & ShapingOptions.DisplayControlCode) == 0); _forceInhibitLigature = ((shapingFlags & ShapingOptions.InhibitLigature) != 0); // note if we have a leading ZWJ _hasLeadingJoin = ((item.Flags & ItemFlags.LeadingJoin) != 0); // initialize our shaper buffers and set up our local references shaperBuffers.Initialize( (ushort)charCount, (ushort)charCount); _charMap = shaperBuffers.CharMap; _glyphInfoList = shaperBuffers.GlyphInfoList; // keep track of our font client, char converter _fontClient = shaperFontClient; _charConverter = _fontClient.CharConverter; // unpack the pointer containers and initialize our pointer _pChars = chars.Probe(0, charCount); _pCharProps = charProperties.Probe(0, charCount); _pClusters = charClusterMap.Probe(0, charCount); _pShapes = (CharShapeInfo *)_pClusters; // initialize our various counts _addedGlyphsCount = 0; _nextCharIx = _nextGlyphIx = 0; _lastCharIx = (ushort)(charCount - 1); // initialize our local booleans _inited = true; _isAutoInsert = _controlCharsSeen = _finishedShaping = false; _isSerializedClusterEntriesRequired = true; } // various accessors, function... // I have laid these out alphabetically. If a particular variable has // an accessor with a different name than the variable, I've added a // comment in the position of the variable... ////// AddGlyphs - add more glyphs to glyphinfolist /// ////// This function can be used to increase or decrease the logical /// size of the glyph info lists. /// If increasing the size the GlyphInfoList is not actually /// resized, but the _addedGlyphsCount is incremented (the actual /// resize will occur later - this is to minimize the overhead of /// resizing). The resizing will actually occur when GlyphsCount /// accessor is invoked. /// If decreasing the logical size of the glyph info lists, a check /// is made so that any pending added glyphs are taken into account /// in the logical resizing and the _nextGlyphIx is adjusted if it /// is affected by the reduction of the list size. /// ////// Critical - The method calls critical code (GlyphInfoList.SetLength) /// [SecurityCritical] public int AddGlyphs(int addCount) { if (addCount < 0) { int glyphsCount = GlyphInfoList.Length; // we're decreasing the extent of our glyphs list // NOTE: we saved glyphsCount above because // GlyphsCount may increase the glyphInfoList // size (if there're pending "add glyphs") if ((GlyphsCount + addCount) > 0) { GlyphInfoList.SetLength (GlyphsCount + addCount); } if (_nextGlyphIx >= glyphsCount + addCount) { _nextGlyphIx = (ushort)((int)_nextGlyphIx + addCount); } } else { _addedGlyphsCount += addCount; } return addCount; } ////// AddLigatureChars - Updates the current glyph info's ligature count. /// ////// This function updates the current glyph info's ligature count and /// maps the charmap, cluster (or shape) values for each of the following /// chars that are part of the ligature. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void AddLigatureChars(int baseCharOffset, ushort ligaturesCount) { if (ligaturesCount > 1) { Debug.Assert(baseCharOffset == -1 || baseCharOffset == 0,"AddLigaturChars: unexpected base offset"); Invariant.Assert((ushort)(_nextCharIx + baseCharOffset + ligaturesCount) <= _charsCount, "AddLigatureChars: invalid input params"); _nextCharIx = (ushort)(_nextCharIx + baseCharOffset); ushort glyphIx = _charMap[_nextCharIx]; ushort clusterIxOrShape = FontClient.IsScriptSupportedByFont ? (ushort)CharShapeInfo.NoFlagsSet : _pClusters[_nextCharIx]; _glyphInfoList.LigatureCounts[ glyphIx ] = ligaturesCount; AddGlyphs( -(ligaturesCount - 1) ); // reduce size of GlyphInfoList while (--ligaturesCount > 0) { ++_nextCharIx; _charMap[_nextCharIx] = glyphIx; _pClusters[_nextCharIx] = clusterIxOrShape; } } } public bool AreLigaturesInhibited { get { return _forceInhibitLigature; } } public IScriptCharConverter CharConverter { get { return _charConverter; } } public UshortList CharMap // also see CurrentGlyphIx { get { return _charMap; } } public ushort CharsCount { get { return _charsCount; } } ////// ShapingWorkspace.CompileShapingProperties - helper function for GetGlyphs. /// ////// This is the last helper function called by shaping engine's GetGlyph call. /// All the glyphs and glyph properties resulting from shaping the text is trans- /// ferred into the output arrays/lists. /// ////// Critical: This code is unsafe /// [SecurityCritical] unsafe public void CompileShapingProperties ( bool forceDiacriticsToZeroWidth, out int glyphCount, out ushort[] glyphIndices, out GlyphShapingProperties[] glyphProperties ) { Invariant.Assert(_charsCount < ushort.MaxValue, "CompileShapingProperties, too many characters"); Invariant.Assert(_glyphInfoList.Length > 0, "CompileShapingProperties, glyphsCount <= 0"); ushort firstClusterGlyph = 0; ushort firstClusterChar = 0; ushort afterClusterGlyph; ushort afterClusterChar; ushort markFlags = (ushort)(forceDiacriticsToZeroWidth ? (GlyphFlags.Diacritic | GlyphFlags.ZeroWidth) : GlyphFlags.Diacritic); glyphCount = _glyphInfoList.Length ; glyphIndices = _glyphInfoList.Glyphs.GetCopy(); // create output copy of glyphs glyphProperties = new GlyphShapingProperties[glyphCount]; // Go through all the characters and compile shaping properties. Mapping is local // to cluster, so doing every cluster as a whole. for(int iChar = 1; iChar <= _charsCount; iChar++) { if (iChar < _charsCount && _pClusters[iChar] == firstClusterGlyph) { //We are still inside same cluster continue; } // New cluster found. Get boundaries afterClusterChar = (ushort)iChar; afterClusterGlyph = (iChar < _charsCount) ? _pClusters[iChar] : (ushort)glyphCount; //Cluster map should come valid from shaping engine Debug.Assert(firstClusterGlyph < afterClusterGlyph); int localMap = 0; for(int iClusterChar = firstClusterChar; iClusterChar < afterClusterChar; iClusterChar++) { // Clusters should be independent, otherwise they should be merged by shaping engine Debug.Assert(_charMap[iClusterChar] >= firstClusterGlyph); Debug.Assert(_charMap[iClusterChar] < afterClusterGlyph); if (_isSerializedClusterEntriesRequired && iClusterChar > firstClusterChar) { // Its possible that the clusters have mappings that will make line services // uphappy. An example is "Ra+halant+Ra+halant+Ra+halant+Ra+halant+Ra+halant" // The first "Ra+halant" will be a reph, repositioned to the end of the cluster. // The font may have formed a glyph from the base // "Ra" and the final "halant", so that the glyphs' first character values will be // (relative to the start of the cluster) 0,1,3,6 and the ligature counts will // be 2,2,2,2. The character map will be 0,1,1,3,3,0,4,4 (ie, the cluster's 1st // and 6th character map to the 1st glyph). We'll discover that here if the // current char is mapped to an earlier glyph. int prevCharIx = iClusterChar - 1; if (_charMap[iClusterChar] < _charMap[prevCharIx]) { // this cluster has "interesting" mapping. LS (Line Services) // doesn't like interesting mapping so lets remap cluster so that // all the entries in the map are sequential. ushort glyphIx = _charMap[iClusterChar]; _charMap[iClusterChar] = _charMap[prevCharIx]; while (prevCharIx > firstClusterChar && _charMap[prevCharIx] != glyphIx) { _charMap[prevCharIx] = _charMap[prevCharIx - 1]; _pCharProps[prevCharIx].EngineReserved = (byte)(_charMap[prevCharIx] - firstClusterGlyph); --prevCharIx; } ushort newGlyphIx = _charMap[iClusterChar]; while (++glyphIx <= newGlyphIx) { _glyphInfoList.FirstChars[glyphIx] += 1; } } } localMap = _charMap[iClusterChar] - firstClusterGlyph; // Shaping porperties do not support more than 255 glyph/chars in single cluster. // if cluster is longer than 255 glyphs, make everything beyond that map to glyph 255 if (localMap > 255) { localMap = 255; } // Only set canGlyphAlone on space when it is 1:1 mapping with the glyph. bool canGlyphAlone = (Classification.CharAttributeOf((int)Classification.GetUnicodeClassUTF16(_pChars[iClusterChar])).Flags & (ushort)CharacterAttributeFlags.CharacterSpace) != 0 && afterClusterChar - firstClusterChar == 1 // cluster is 1 char long && afterClusterGlyph - firstClusterGlyph == 1; // cluster is 1 glyph long _pCharProps[iClusterChar].CanGlyphAlone = canGlyphAlone; _pCharProps[iClusterChar].EngineReserved= (byte)localMap; } // make sure the start of the cluster is marked... // set the cluster start flag for the last cluster _glyphInfoList.GlyphFlags[firstClusterGlyph] |= (ushort)GlyphFlags.ClusterStart; // go through all the glyphs of the cluster for(int iClusterGlyph = firstClusterGlyph; iClusterGlyph < afterClusterGlyph; iClusterGlyph++) { ushort firstChar = _glyphInfoList.FirstChars[iClusterGlyph]; ushort ligaCount = _glyphInfoList.LigatureCounts[iClusterGlyph]; ushort glyphFlags = _glyphInfoList.GlyphFlags[iClusterGlyph]; // check for any adjustments that need to be made in the glyph // flags // First, validate the glyph id if (glyphIndices[iClusterGlyph] > FontClient.GetMaxGlyphId) { // invalid glyph index (too big to be valid), so just hide it. glyphFlags = GlyphFlagsZeroWidth; glyphIndices[iClusterGlyph] = FontClient.SpaceGlyph; } // If the glyph's missing... if ((glyphFlags & (ushort)GlyphFlags.Missing) != 0) { // for missing glyphs, check if we know something about it... // (check if its a unicode control character) if ((glyphFlags & (ushort)GlyphFlags.ZeroWidth) != 0) { glyphIndices[iClusterGlyph] = FontClient.SpaceGlyph; glyphFlags = GlyphFlagsZeroWidth; } } // if its a mark glyph ... else if ( ((glyphFlags & (ushort)GlyphFlags.GlyphTypeMask) == (ushort)GlyphFlags.Mark) || (iClusterGlyph != firstClusterGlyph && (glyphFlags & (ushort)GlyphFlags.GlyphTypeMask) == (ushort)GlyphFlags.Unresolved) ) { // set our diacritic glyph flags. glyphFlags = (ushort)(glyphFlags | markFlags); } // check if this is a unicode control char (ZWJ, etc) and we want to hide // such... else if ((glyphFlags & (ushort)GlyphFlags.ZeroWidth) != 0 && _forceControlCharsInvisible && _controlCharsSeen && FontClient.IsUnicodeControlGlyph(glyphIndices[iClusterGlyph])) { glyphIndices[iClusterGlyph] = FontClient.SpaceGlyph; glyphFlags = GlyphFlagsZeroWidth; } // Clusters should be independent, otherwise they should be merged by shaping engine Debug.Assert(firstChar >= firstClusterChar); Debug.Assert(firstChar < afterClusterChar); localMap = firstChar - firstClusterChar; // Shaping porperties do not support more than 255 glyph/chars in single cluster. // if cluster is longer than 255 glyphs, make everything beyond that map to glyph 255 if (localMap >= 255) { localMap = 255; } if (ligaCount > 255) { ligaCount = 255; } glyphProperties[iClusterGlyph] = new GlyphShapingProperties(glyphFlags, (ushort)((ligaCount << 8) | localMap) ); } firstClusterChar = afterClusterChar; firstClusterGlyph = afterClusterGlyph; } } // the "Current???" accessors are used in the various engines shaping loops ////// CurrentChar - returns current char /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public char CurrentChar { [SecurityCritical] get { return _pChars[_nextCharIx]; } } ////// CurrentCharIx - returns current char index /// public ushort CurrentCharIx { get { return (ushort)_nextCharIx; } } ////// CurrentGlyph - sets/gets current glyph /// public ushort CurrentGlyph { get { return _glyphInfoList.Glyphs[ _charMap[_nextCharIx] ]; } set { _glyphInfoList.Glyphs[ _charMap[_nextCharIx] ] = value; } } ////// CurrentShape - returns/sets current char shape info /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public CharShapeInfo CurrentShape { [SecurityCritical] get { return _pShapes[_nextCharIx]; } [SecurityCritical] set { _pShapes[_nextCharIx] = value; } } public ShaperFontClient FontClient { get { return _fontClient; } } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] unsafe public char GetChar(ushort charIx) { return (charIx < _charsCount) ? _pChars[charIx] : '\u0000'; } public ushort GetGlyph(ushort glyphIx) { return _glyphInfoList.Glyphs[glyphIx]; } public ushort GetGlyphIx(ushort charIx) { return _charMap[charIx]; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] public bool GetNextCharProperties (out char nextChar, out ushort nextGlyph, out CharShapeInfo nextShape) { ToNextChar(); nextChar = CurrentChar; nextGlyph = CharConverter.ToGlyph(nextChar); nextShape = CharConverter.ToShapeInfo(nextChar); return !_finishedShaping; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] public bool GetNextChar(out char nextChar) { ToNextChar(); nextChar = CurrentChar; return !_finishedShaping; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] public bool GetNextShape(out CharShapeInfo nextShape) { ToNextChar(); nextShape = CharConverter.ToShapeInfo(CurrentChar); return !_finishedShaping; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] unsafe public CharShapeInfo GetShapeInfo(int charIx) { return (CharShapeInfo)((charIx >= 0 && charIx < _charsCount) ? _pShapes[charIx] : CharShapeInfo.NoFlagsSet); } ////// GlyphsCount - returns glyphs count /// public int GlyphsCount { get { if (_nextGlyphIx == GlyphInfoList.Length && _addedGlyphsCount > 0) { // if we are at the end of the glyph list and there're deferred // glyph additions, add the deferred glyphs now. _isAutoInsert = true; // inhibit updating "inserted" glyphs InsertGlyphs((ushort)GlyphInfoList.Length,(ushort)_addedGlyphsCount); _addedGlyphsCount = 0; } return GlyphInfoList.Length; } } public GlyphInfoList GlyphInfoList { get { return _glyphInfoList; } } public bool HasLeadingJoin { get { return _hasLeadingJoin; } } public bool HideControlChars { get { return _forceControlCharsInvisible && _controlCharsSeen; } } public bool IsFinished { get { return _finishedShaping; } } ////// InsertGlyphs - insert more glyphs into glyphinfolist /// public int InsertGlyphs(ushort insertionPoint, ushort insertCount) { int insertedCount = 0; int glyphsCount = GlyphInfoList.Length; if ( insertionPoint < glyphsCount ) { GlyphInfoList.Insert(insertionPoint, insertCount); if (GlyphInfoList.Length > glyphsCount) { insertedCount = GlyphInfoList.Length - glyphsCount; } } else if ( insertionPoint == glyphsCount ) { if (_inited) GlyphInfoList.Insert( 0, insertCount > CharsCount ? insertCount : CharsCount); else GlyphInfoList.Insert(glyphsCount, insertCount); if (GlyphInfoList.Length > glyphsCount) { insertedCount = GlyphInfoList.Length - glyphsCount; } } // if we have inserted glyphs in front of the current glyph index, // increment the glyph index. The assumption is made that the // inserted glyph(s) should be mapped to the same character as the // first glyph that's being displaced (so we don't adjust the // charmap entry that points to this glyph position) if (!_inited && !_isAutoInsert && insertionPoint <= _nextGlyphIx && _nextGlyphIx <= glyphsCount) { for (int i = 0; i <= _nextCharIx; ++i) { if (_charMap[i] > insertionPoint) { _charMap[i] += (ushort)insertedCount; } } // normally, "afterInsertionGlyphIx" will be the first glyph position // after the inserted members of GlyphInfoList. There's one exception; // when the insertion point is at glyphsCount (this implies that // _nextGlyphIx also is at glyphsCount) we are "inserting" glyphs // "in front of" the last glyph but the insertion code above actually // added the elements to the end of the glyphs array. So, in this end // case, the inserted elements are set up from the last entry in the glyphs // array (last before we added these glyphs, of course). ushort afterInsertionGlyphIx = insertionPoint < glyphsCount ? (ushort)(insertionPoint + insertedCount) : (ushort)(glyphsCount - 1); Debug.Assert(afterInsertionGlyphIx < glyphsCount + insertedCount,"improper use of InsertGlyphs"); for (int i = 0; i < insertedCount; ++i) { GlyphInfoList.FirstChars[insertionPoint + i] = GlyphInfoList.FirstChars[afterInsertionGlyphIx]; GlyphInfoList.LigatureCounts[insertionPoint + i] = GlyphInfoList.LigatureCounts[afterInsertionGlyphIx]; GlyphInfoList.GlyphFlags[insertionPoint + i] = GlyphInfoList.GlyphFlags[afterInsertionGlyphIx]; } _nextGlyphIx = (ushort)(_nextGlyphIx + insertedCount); } _isAutoInsert = false; // always reset this return insertedCount; } public bool MoveGlyphs (ushort destGlyphIx, ushort srcGlyphIx, ushort moveCount ) { ushort glyphsCount = (ushort)_glyphInfoList.Length; if (srcGlyphIx + moveCount <= srcGlyphIx || // make sure no overflow destGlyphIx + moveCount <= destGlyphIx || // make sure no overflow (srcGlyphIx + moveCount - 1) >= glyphsCount || // and make sure valid move arguments (destGlyphIx + moveCount - 1) >= glyphsCount) // and make sure valid move arguments { return false; } if (srcGlyphIx < destGlyphIx && srcGlyphIx + moveCount > destGlyphIx) { // buffers overlap, copy from end of src buffer // src: ++++++++++++++++++++++++++++ // dest: ++++++++++++++++++++++++++++ srcGlyphIx += moveCount; destGlyphIx += moveCount; while (moveCount-- > 0) { _glyphInfoList.Glyphs[--destGlyphIx] = _glyphInfoList.Glyphs[--srcGlyphIx]; } } else if (srcGlyphIx != destGlyphIx) { while (moveCount-- > 0) { _glyphInfoList.Glyphs[destGlyphIx++] = _glyphInfoList.Glyphs[srcGlyphIx++]; } } return true; } ////// NextChar - returns next char /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public char NextChar { [SecurityCritical] get { return (_nextCharIx + 1 < _charsCount ) ? _pChars[_nextCharIx + 1] : (char)0; } } ////// NextGlyphIx - returns/sets next glyphIx /// public ushort NextGlyphIx { get { return _nextGlyphIx; } } ////// ShapingWorkspace.PopulateClusterMap - helper function for GetGlyphs. /// ////// This function creates the cluster map based on shape flags. The /// shape engine will have set the IsStartOfCluster flag for all /// characters that start a cluster. (CAUTION: the cluster array /// and the shape flag array are actually the same memory block /// so don't try to use a given character's shape info after /// setting its cluster info. ) /// ////// Critical - uses pointers, unsafe code /// [SecurityCritical] unsafe public void PopulateClusterMap () { if (!FontClient.IsFeatureTypeSupportedByFont) return; // nothing to do (SetCurrentClusterInfo already set the map up) ushort *clusterMap = _pClusters; Invariant.Assert(_glyphInfoList.Length > 0, "PopulateClusterMap, glyphsCount <= 0"); Reset(0,0,_charsCount); // Step 1. Create the cluster map based on the shape flags... // this loop goes through all the shapes (one per Unicode character) and // creates the cluster map. When each start of cluster is found the // associated glyph is assumed to be the value for this new cluster unless // there's reason to suspect its not. int firstGlyphInCluster = 0; int firstCharInCluster = 0; int earliestGlyphRef = 0; int earliestCharRef = 0; int currentCharIx; int currentGlyphIx; int currentGlyphFirstChar; // add the glyphs to the glyph array while (ToNextChar()) { currentCharIx = _nextCharIx; currentGlyphIx = _charMap[_nextCharIx]; Invariant.Assert(currentGlyphIx < _glyphInfoList.Length, "illegal glyph ix in charMap"); // It is possible that this current character is part of a ligature glyph that actually spans // multiple clusters (including a previous cluster, so check the glyph's FirstChar // clustermap entry (it might be even earlier than currentGlyphIx) currentGlyphFirstChar = (int)_glyphInfoList.FirstChars[currentGlyphIx]; if (currentGlyphFirstChar < firstCharInCluster) { // Since currentGlyphFirstChar < firstCharInCluster, clusterMap[currentGlyphFirstChar] // is already filled with its glyph id so we can use it and check if the glyph it // references is earlier than what we currently know about. if (clusterMap[currentGlyphFirstChar] < earliestGlyphRef) { earliestGlyphRef = clusterMap[currentGlyphFirstChar]; } if (currentGlyphFirstChar < earliestCharRef) { earliestCharRef = currentGlyphFirstChar; } } // As each character is processed, we'll keep track of the earliest glyph referenced // in the glyphs that are part of the current cluster. This information will be used // when the next start of cluster is detected. if (currentGlyphIx < earliestGlyphRef) { earliestGlyphRef = currentGlyphIx; // save this earlier glyph reference } if ((CurrentShape & (CharShapeInfo.IsStartOfCluster)) != 0) { // starting a new cluster... if (currentGlyphIx == earliestGlyphRef) { // we will normally be here for the first glyph, but otherwise // this seems an unlikely case; if here then this is not actually // the end of one cluster and the beginning of another, but rather // a concatenation of two clusters. } else { // this glyph starts a new cluster so go back over the glyphs in the preceding // cluster to verify that all is well. It may be that we have discovered new // information (ie, membership in cluster includes earlier glyphs or characters) // since the first char in the last cluster. if (earliestGlyphRef < firstGlyphInCluster || earliestCharRef < firstCharInCluster) { // we need to change the last cluster's extent and/or its value! firstGlyphInCluster = earliestGlyphRef; firstCharInCluster = earliestCharRef; for (int i = firstCharInCluster; i < currentCharIx; ++i) { clusterMap[i] = (ushort)firstGlyphInCluster; } } // As long as this char (the first in a new cluster) is after the current cluster's // first char, set up the counters for this new cluster. Otherwise, since this // character is before the current cluster's first character (must have been // updated in the if block just above) don't change the current cluster. if (firstCharInCluster < currentCharIx) { // keep track of the first char in cluster firstCharInCluster = currentCharIx; firstGlyphInCluster = currentGlyphIx; if (currentGlyphFirstChar < currentCharIx) { earliestCharRef = currentGlyphFirstChar; earliestGlyphRef = clusterMap[earliestCharRef]; } else { earliestCharRef = currentCharIx; earliestGlyphRef = currentGlyphIx; } } } } clusterMap[currentCharIx] = (ushort)firstGlyphInCluster; // save this char's cluster info } // If the last character is the last character in the last cluster (ie, if // it doesn't have the StartOfCluster flag ), then need to check // update the cluster info. Check "earliestCharRef" to determine this; normally, // if the last character has its StartOfCluster flag set, "earliestCharRef" will // be the index of the last character. if (earliestCharRef + 1 < CharsCount) { // check if final cluster adjustment is needed if (earliestGlyphRef < firstGlyphInCluster || earliestCharRef < firstCharInCluster) { // we need to change the last cluster's extent and/or its value! for (int i = earliestCharRef; i < CharsCount; ++i) { clusterMap[i] = (ushort)(earliestGlyphRef); } } } Invariant.Assert( clusterMap[0] == 0, "first cluster map entry is not zero"); } ////// PreviousChar - returns previous char /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public char PreviousChar { [SecurityCritical] get { return _pChars[ PreviousCharIx ]; } } ////// PreviousCharIx - returns previous char index /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public ushort PreviousCharIx { get { return (_inited ? _nextCharIx : (_nextCharIx == 0 ? (ushort) 0 : ( _nextCharIx >= _charsCount ? (ushort)(_charsCount- 1) : (ushort)(_nextCharIx - 1)))); } } ////// PreviousGlyph - sets/gets previous glyph /// public ushort PreviousGlyph { get { return _inited ? (ushort) 0 : _glyphInfoList.Glyphs[ PreviousGlyphIx ]; } set { _glyphInfoList.Glyphs[ PreviousGlyphIx ] = value; } } ////// PreviousGlyphIx - gets previous glyph index /// public ushort PreviousGlyphIx { get { return _inited ? (ushort) _nextGlyphIx : _charMap[ PreviousCharIx ]; } } ////// PreviousShape - gets previous char shape /// ////// Critical - The method reads into an unvalidated unsafe array /// unsafe public CharShapeInfo PreviousShape { [SecurityCritical] get { return PreviousCharIx == CurrentCharIx ? CharShapeInfo.NoFlagsSet : _pShapes[ PreviousCharIx ]; } } ////// ForceSerializedCluster - /// public bool IsForceSerializedClusterOn { set { _isSerializedClusterEntriesRequired = value; } } ////// ShapingWorkspace.Reset - used to re-initial the MoveToNextChar mechanism /// (our iterator function, if you please!). This flavor of the /// reset function just resets the iteration booleans without moving /// the next character index /// public bool Reset( ) { _inited = true; _finishedShaping = (_nextCharIx > _lastCharIx); return !_finishedShaping; } ////// ShapingWorkspace.Reset - used to re-initial the MoveToNextChar mechanism /// (our iterator function, if you please!). /// public bool Reset( ushort nextCharIx, ushort nextGlyphIx, ushort charCount ) { _nextCharIx = nextCharIx; _nextGlyphIx = nextGlyphIx; _inited = true; _finishedShaping = false; _lastCharIx = (ushort)(nextCharIx + charCount - 1); if (_lastCharIx >= _charsCount) { _lastCharIx = (ushort)(_charsCount - 1); } // we allow nextGlyphIx == GlyphsCount because of the case where // we're adding glyphs syllable by syllable (so we will allocate // the next syllable's glyphs later) Invariant.Assert(_nextCharIx < _charsCount); Invariant.Assert(_nextGlyphIx <= GlyphsCount); return !_finishedShaping; } ////// SetGlyphPropertiesUsingShapeInfo - This is the default call to set up the /// glyphInfoList, charmap, and cluster (if script not supported) /// for the current character. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void SetGlyphPropertiesUsingShapeInfo ( CharShapeInfo currShape ) { char currChar = _pChars[_nextCharIx]; ushort glyph = CharConverter.ToGlyph(currChar); _pShapes[_nextCharIx] = currShape; _charMap[_nextCharIx] = _nextGlyphIx; // set charmap SetGlyphProperties( glyph ); } ////// SetGlyphPropertiesUsingChar - This sets up the /// glyphInfoList, charmap, and cluster (if script not supported) /// for the current character using the passed in character. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void SetGlyphPropertiesUsingChar ( CharShapeInfo currShape, char currChar) { ushort glyph = CharConverter.ToGlyph(currChar); _pShapes[_nextCharIx] = currShape; _charMap[_nextCharIx] = _nextGlyphIx; // set charmap SetGlyphProperties( glyph ); } ////// SetGlyphPropertiesUsingGlyph - This sets up the /// glyphInfoList, charmap, and cluster (if script not supported) /// for the current character using the passed in glyph. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void SetGlyphPropertiesUsingGlyph ( CharShapeInfo currShape, ushort glyph ) { _pShapes[_nextCharIx] = currShape; _charMap[_nextCharIx] = _nextGlyphIx; // set charmap SetGlyphProperties( glyph ); } ////// SetGlyphProperties - This sets up the glyphInfoList /// for the current glyph position and it increments /// _nextGlyphIx. If the script is not supported by this /// font (ie, the font has no OT tables for this script), /// then the cluster map is updated, too. If the font does have /// support for script, then shaping info is valid in _pShapes; /// so if this character's shape indicates that we desire /// a dotted circle glyph inserted then this is done. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void SetGlyphProperties ( ushort glyph ) { // Be aware of the side effect of GlyphsCount; it is necessary // that the comparison below be done if there're added glyphs // to be considered. Invariant.Assert(_nextGlyphIx < GlyphsCount, "SetGlyphProperties called with invalid glyphIx"); // fill this character's glyph info list entries _glyphInfoList.Glyphs[_nextGlyphIx] = glyph; _glyphInfoList.FirstChars[_nextGlyphIx] = (ushort)_nextCharIx; _glyphInfoList.LigatureCounts[_nextGlyphIx] = 1; _glyphInfoList.GlyphFlags[_nextGlyphIx] = GlyphFlagsNone; if (glyph == 0) { _glyphInfoList.GlyphFlags[_nextGlyphIx] |= (ushort)GlyphFlags.Missing; } // check the shape flags and take appropriate action CharShapeInfo currentShape = _pShapes[_nextCharIx]; // if this shape indicates that here is an inserted base insertion // point, insert the dotted circle. if ((currentShape & CharShapeInfo.RequiresInsertedBase) == CharShapeInfo.RequiresInsertedBase) { ushort dottedCircleGlyph = FontClient.DottedCircleGlyph; if (dottedCircleGlyph != 0) { InsertGlyphs(_nextGlyphIx,1); // this will increment _nextGlyphIx Invariant.Assert(_nextGlyphIx < GlyphsCount, "Invalid glyph ix for insertion of invalid base"); _glyphInfoList.Glyphs[_nextGlyphIx - 1] = dottedCircleGlyph; _glyphInfoList.GlyphFlags[_nextGlyphIx - 1] = (ushort)GlyphFlags.InvalidBase; } } else if( (currentShape & CharShapeInfo.IsUnicodeLayoutControl) != 0) { // This is a Unicode control glyph. Let font client know // so it can save glyph id if it wants to (may be used to // suppress visible Unicode control characters later) FontClient.SetUnicodeControlGlyph(CurrentChar, glyph); _controlCharsSeen = true; _glyphInfoList.GlyphFlags[_nextGlyphIx] = GlyphFlagsZeroWidth; } // If font doesn't have feature support, just set up the // cluster info directly if ( !FontClient.IsFeatureTypeSupportedByFont ) { SetCurrentClusterInfo(currentShape, glyph); } // set next glyph (allow to go one beyond the last to detect the last glyph // being written if ((ushort)( _nextGlyphIx + 1) <= GlyphsCount) { _nextGlyphIx = (ushort)( _nextGlyphIx + 1); } } ////// SetCurrentClusterInfo - This sets up the glyphInfoList /// for the current glyph position and it increments /// _nextGlyphIx. If the script is not supported by this /// font (ie, the font has no OT tables for this script), /// then the cluster map is updated, too. If the font does have /// support for script, then shaping info is valid in _pShapes; /// so if this character's shape indicates that we desire /// a dotted circle glyph inserted then this is done. /// ////// Critical - The method reads/write unsafe pointers /// [SecurityCritical] unsafe public void SetCurrentClusterInfo ( CharShapeInfo currentShape, ushort glyph ) { bool startOfCluster = (currentShape & CharShapeInfo.IsStartOfCluster) != 0; // font doesn't have feature support, so just set up the // cluster info directly if( (currentShape & CharShapeInfo.IsUnicodeLayoutControl) != 0) { // For fonts that don't have feature support for this // script - if the user doesn't want to actually see // the control characters, replace them with a blank glyph. // (For fonts with support, we do this later after feature // application) if (_forceControlCharsInvisible) { _glyphInfoList.Glyphs[_nextGlyphIx] = FontClient.SpaceGlyph; } } else if (startOfCluster) { if (glyph == 0) { _glyphInfoList.GlyphFlags[_nextGlyphIx] = GlyphFlagsBase | (ushort)GlyphFlags.Missing; } else { _glyphInfoList.GlyphFlags[_nextGlyphIx] = GlyphFlagsBase; } } else { _glyphInfoList.GlyphFlags[_nextGlyphIx] = GlyphFlagsDiacritic; } _pClusters[_nextCharIx] = ( _nextCharIx == 0 || startOfCluster ? _nextGlyphIx : _pClusters[PreviousCharIx]); _charMap[_nextCharIx] = _nextGlyphIx; // set cluster map value } ////// SetGlyph - /// public void SetGlyph (ushort glyphIx, ushort glyph) { _glyphInfoList.Glyphs[glyphIx] = glyph; } ////// SetGlyph - /// public void SetGlyph (ushort glyphIx, ushort glyph, ushort firstChar) { _glyphInfoList.Glyphs[glyphIx] = glyph; _glyphInfoList.FirstChars[glyphIx] = firstChar; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] unsafe public bool SetNextGlyphProperties (out CharShapeInfo nextShape) { if (ToNextChar()) { char nextChar = CurrentChar; nextShape = CharConverter.ToShapeInfo(nextChar); _pShapes[_nextCharIx] = nextShape; _charMap[_nextCharIx] = _nextGlyphIx; // set charmap SetGlyphProperties( CharConverter.ToGlyph(nextChar) ); } else { nextShape = CharShapeInfo.NoFlagsSet; } return !_finishedShaping; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] unsafe public void SetShapeInfo(ushort charIx, CharShapeInfo charShape) { if (charIx >= 0 && charIx < _charsCount) { _pShapes[charIx] = charShape; } } ////// ToNextChar - increments index, returns false if finished /// public bool ToNextChar() { if (_inited) { // if this is the first character, don't increment the // index (so we can use this in a while (MoveToNextChar){...} // loop _inited = false; } else if (_nextCharIx < _lastCharIx) { ++_nextCharIx; } else { _finishedShaping = true; Invariant.Assert(_nextCharIx < _charsCount); } return !_finishedShaping; } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] public void UpdateCurrentGlyphProperties (CharShapeInfo newShape) { if ((CurrentShape & CharShapeInfo.RequiresInsertedBase) == CharShapeInfo.RequiresInsertedBase && (newShape & CharShapeInfo.RequiresInsertedBase) != CharShapeInfo.RequiresInsertedBase ) { AddGlyphs(-1); } _nextGlyphIx = _charMap[_nextCharIx]; // set glyph ix back SetGlyphPropertiesUsingGlyph( newShape, CurrentGlyph ); } ////// Critical - The method reads into an unsafe array /// [SecurityCritical] public void ValidateGlyphsOut (ushort[] glyphs) { bool areAllGlyphsZero = true; for (int i = 0; i < glyphs.Length; ++i) { if (glyphs[i] != 0) areAllGlyphsZero = false; } Debug.Assert(areAllGlyphsZero,"ValidateGlyphsOut found an all zero'ed glyphs"); } private int _addedGlyphsCount; private ushort _charsCount; // number of chars in run. private IScriptCharConverter _charConverter; private bool _controlCharsSeen; private bool _finishedShaping; private ShaperFontClient _fontClient; private bool _forceControlCharsInvisible; private bool _forceInhibitLigature; private bool _inited; private bool _isAutoInsert; private bool _hasLeadingJoin; private bool _isSerializedClusterEntriesRequired; private ushort _nextCharIx; private ushort _nextGlyphIx; ////// Critical: Holds reference to an unsafe pointer /// [SecurityCritical] private unsafe char* _pChars; // the Unicode text buffer [SecurityCritical] private unsafe CharacterShapingProperties* _pCharProps; // char properties buffer [SecurityCritical] private unsafe ushort* _pClusters; // clusterMap buffer [SecurityCritical] private unsafe CharShapeInfo* _pShapes; // char shapes buffer private UshortList _charMap; private GlyphInfoList _glyphInfoList; private ushort _lastCharIx; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ToolBar.cs
- UserControlCodeDomTreeGenerator.cs
- _FtpDataStream.cs
- ConfigErrorGlyph.cs
- ArrayListCollectionBase.cs
- RelationshipConverter.cs
- ExceptionUtil.cs
- AdvancedBindingEditor.cs
- ComboBox.cs
- LicenseProviderAttribute.cs
- AndCondition.cs
- ManualResetEvent.cs
- StronglyTypedResourceBuilder.cs
- RootBrowserWindowAutomationPeer.cs
- SqlProvider.cs
- MissingFieldException.cs
- PolyQuadraticBezierSegment.cs
- ObjectToIdCache.cs
- HwndStylusInputProvider.cs
- QilSortKey.cs
- DoubleLinkListEnumerator.cs
- ObjectStorage.cs
- KeyPressEvent.cs
- ValueCollectionParameterReader.cs
- WebScriptMetadataMessage.cs
- DoubleLink.cs
- HtmlObjectListAdapter.cs
- IndentTextWriter.cs
- BehaviorEditorPart.cs
- RawStylusInput.cs
- ReverseInheritProperty.cs
- IncrementalCompileAnalyzer.cs
- PropertyConverter.cs
- WinEventHandler.cs
- TextElementEnumerator.cs
- NullableConverter.cs
- WCFBuildProvider.cs
- HtmlTableCell.cs
- Matrix3DConverter.cs
- AttributeCollection.cs
- WindowsStatic.cs
- PersonalizationProviderCollection.cs
- DataGridViewRowPostPaintEventArgs.cs
- MutexSecurity.cs
- IPEndPoint.cs
- loginstatus.cs
- login.cs
- ListViewSelectEventArgs.cs
- ExpressionBinding.cs
- DrawTreeNodeEventArgs.cs
- RadioButtonList.cs
- MaskedTextBox.cs
- TableDetailsRow.cs
- HtmlForm.cs
- ButtonColumn.cs
- ToolStripPanelRenderEventArgs.cs
- HttpHostedTransportConfiguration.cs
- SingleAnimation.cs
- TransactionContextValidator.cs
- TextModifier.cs
- MenuItemAutomationPeer.cs
- TextTreeNode.cs
- TabControlDesigner.cs
- GenericTextProperties.cs
- XamlTypeWithExplicitNamespace.cs
- SystemMulticastIPAddressInformation.cs
- Main.cs
- NamespaceDecl.cs
- CopyAction.cs
- SectionVisual.cs
- PerformanceCounterPermission.cs
- TableCellAutomationPeer.cs
- XhtmlConformanceSection.cs
- BufferedGraphicsContext.cs
- PenThreadPool.cs
- SyndicationFeedFormatter.cs
- MetadataArtifactLoaderFile.cs
- TypeHelper.cs
- SendMailErrorEventArgs.cs
- Point3DValueSerializer.cs
- CfgParser.cs
- System.Data.OracleClient_BID.cs
- CompositeControl.cs
- CqlBlock.cs
- EventLogLink.cs
- COAUTHINFO.cs
- EventManager.cs
- SafeSystemMetrics.cs
- SparseMemoryStream.cs
- TemplateParser.cs
- RegistrySecurity.cs
- TextDocumentView.cs
- SID.cs
- PeerCollaboration.cs
- CodeTypeDelegate.cs
- AutomationProperty.cs
- LicenseContext.cs
- Socket.cs
- ListControl.cs
- StrokeCollectionDefaultValueFactory.cs