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
- TimelineGroup.cs
- BufferAllocator.cs
- InvokeMethodActivity.cs
- TypeGeneratedEventArgs.cs
- DoubleCollectionConverter.cs
- Tuple.cs
- BoundingRectTracker.cs
- basevalidator.cs
- DataServiceHostFactory.cs
- XmlSchemaCompilationSettings.cs
- RemoteX509AsymmetricSecurityKey.cs
- RandomNumberGenerator.cs
- EdmEntityTypeAttribute.cs
- TextTreeTextBlock.cs
- DetailsViewRow.cs
- HtmlTitle.cs
- SocketManager.cs
- DeclarationUpdate.cs
- DefaultValueAttribute.cs
- DataGridViewHitTestInfo.cs
- namescope.cs
- MaskDescriptor.cs
- DtcInterfaces.cs
- TemplateControl.cs
- FreezableDefaultValueFactory.cs
- AnimationClock.cs
- Ipv6Element.cs
- Properties.cs
- Part.cs
- PolygonHotSpot.cs
- RelOps.cs
- FlowDocumentScrollViewerAutomationPeer.cs
- XNodeValidator.cs
- XmlComment.cs
- BookmarkUndoUnit.cs
- ReferenceEqualityComparer.cs
- ConstNode.cs
- TableLayoutPanelCellPosition.cs
- Variant.cs
- ButtonPopupAdapter.cs
- XmlSerializerNamespaces.cs
- WebPartTransformerCollection.cs
- RequestValidator.cs
- MSAAWinEventWrap.cs
- CharUnicodeInfo.cs
- COM2ComponentEditor.cs
- XmlDataLoader.cs
- XmlNodeComparer.cs
- basevalidator.cs
- TextLineBreak.cs
- AutoResizedEvent.cs
- KeyValueSerializer.cs
- MessageTraceRecord.cs
- SchemaImporterExtension.cs
- AutoCompleteStringCollection.cs
- CellParaClient.cs
- FileUtil.cs
- Rect3DConverter.cs
- TerminateWorkflow.cs
- KeyboardNavigation.cs
- Timeline.cs
- RoutedEventHandlerInfo.cs
- Mappings.cs
- ConvertersCollection.cs
- EdmProperty.cs
- DetailsViewInsertEventArgs.cs
- TreeNode.cs
- MultiBindingExpression.cs
- SchemaElementDecl.cs
- DPTypeDescriptorContext.cs
- RuntimeConfigLKG.cs
- DateTimeFormat.cs
- ChannelOptions.cs
- QuestionEventArgs.cs
- IsolatedStorageException.cs
- xml.cs
- LambdaCompiler.Statements.cs
- ItemsControl.cs
- MessageRpc.cs
- EnumType.cs
- NCryptSafeHandles.cs
- ConnectionProviderAttribute.cs
- GenerateTemporaryTargetAssembly.cs
- UriWriter.cs
- TreeViewTemplateSelector.cs
- VirtualPathProvider.cs
- DispatcherObject.cs
- PropagationProtocolsTracing.cs
- CodeGen.cs
- XmlNamespaceManager.cs
- FixedSOMPageElement.cs
- OledbConnectionStringbuilder.cs
- DataServiceQueryContinuation.cs
- SessionStateContainer.cs
- TraceListeners.cs
- GridSplitter.cs
- ObjectListTitleAttribute.cs
- ClientRoleProvider.cs
- XhtmlBasicLiteralTextAdapter.cs
- NetDispatcherFaultException.cs