Beispiel #1
0
        // ------------------------------------------------------------------------------
        // Parses a semicolon-delimited list of glyph specifiers, each of which consists 
        // of up to 4 comma-delimited values:
        //   - glyph index (ushort)
        //   - glyph advance (double)
        //   - glyph offset X (double) 
        //   - glyph offset Y (double)
        // A glyph entry can be have a cluster size prefix (int or pair of ints separated by a colon) 
        // Whitespace adjacent to a delimiter (comma or semicolon) is ignored. 
        // Returns the number of glyph specs parsed (number of semicolons plus 1).
        // 


        private int ParseGlyphsProperty(
            GlyphTypeface               fontFace, 
            string                      unicodeString,
            bool                        sideways, 
            out List<ParsedGlyphData>   parsedGlyphs, 
            out ushort[]                clusterMap)
        { 
            string glyphsProp = Indices;

            // init for the whole parse, including the result arrays
            int parsedGlyphCount = 0; 
            int parsedCharacterCount = 0;
 
            int characterClusterSize = 1; 
            int glyphClusterSize = 1;
 
            bool inCluster = false;

            // make reasonable capacity guess on how many glyphs we can expect
            int estimatedNumberOfGlyphs; 

            if (!String.IsNullOrEmpty(unicodeString)) 
            { 
                clusterMap = new ushort[unicodeString.Length];
                estimatedNumberOfGlyphs = unicodeString.Length; 
            }
            else
            {
                clusterMap = null; 
                estimatedNumberOfGlyphs = 8;
            } 
 
            if (!String.IsNullOrEmpty(glyphsProp))
                estimatedNumberOfGlyphs = Math.Max(estimatedNumberOfGlyphs, glyphsProp.Length / 5); 

            parsedGlyphs = new List<ParsedGlyphData>(estimatedNumberOfGlyphs);

            ParsedGlyphData parsedGlyphData = new ParsedGlyphData(); 

            #region Parse Glyphs string 
            if (!String.IsNullOrEmpty(glyphsProp)) 
            {
                // init per-glyph values for the first glyph/position 
                int valueWithinGlyph = 0; // which value we're on (how many commas have we seen in this glyph)?
                int valueStartIndex = 0; // where (what index of Glyphs prop string) did this value start?

                // iterate and parse the characters of the Indices property 
                for (int i = 0; i <= glyphsProp.Length; i++)
                { 
                    // get next char or pseudo-terminator 
                    char c = i < glyphsProp.Length ? glyphsProp[i] : '\0';
 
                    // finished scanning the current per-glyph value?
                    if ((c == ',') || (c == ';') || (i == glyphsProp.Length))
                    {
                        int len = i - valueStartIndex; 

                        string valueSpec = glyphsProp.Substring(valueStartIndex, len); 
 
                        #region Interpret one comma-delimited value
 
                        switch (valueWithinGlyph)
                        {
                            case 0:
                                bool wasInCluster = inCluster; 
                                // interpret cluster size and glyph index spec
                                if (!ReadGlyphIndex( 
                                    valueSpec, 
                                    ref inCluster,
                                    ref glyphClusterSize, 
                                    ref characterClusterSize,
                                    ref parsedGlyphData.glyphIndex))
                                {
                                    if (String.IsNullOrEmpty(unicodeString)) 
                                        throw new ArgumentException(SR.Get(SRID.GlyphsIndexRequiredIfNoUnicode));
 
                                    if (unicodeString.Length <= parsedCharacterCount) 
                                        throw new ArgumentException(SR.Get(SRID.GlyphsUnicodeStringIsTooShort));
 
                                    parsedGlyphData.glyphIndex = GetGlyphFromCharacter(fontFace, unicodeString[parsedCharacterCount]);
                                }

                                if (!wasInCluster && clusterMap != null) 
                                {
                                    // fill out cluster map at the start of each cluster 
                                    if (inCluster) 
                                    {
                                        for (int ch = parsedCharacterCount; ch < parsedCharacterCount + characterClusterSize; ++ch) 
                                        {
                                            SetClusterMapEntry(clusterMap, ch, (ushort)parsedGlyphCount);
                                        }
                                    } 
                                    else
                                    { 
                                        SetClusterMapEntry(clusterMap, parsedCharacterCount, (ushort)parsedGlyphCount); 
                                    }
                                } 
                                parsedGlyphData.advanceWidth = GetAdvanceWidth(fontFace, parsedGlyphData.glyphIndex, sideways);
                                break;

                            case 1: 
                                // interpret glyph advance spec
                                if (!IsEmpty(valueSpec)) 
                                { 
                                    parsedGlyphData.advanceWidth = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                                    if (parsedGlyphData.advanceWidth < 0) 
                                        throw new ArgumentException(SR.Get(SRID.GlyphsAdvanceWidthCannotBeNegative));
                                }
                                break;
 
                            case 2:
                                // interpret glyph offset X 
                                if (!IsEmpty(valueSpec)) 
                                    parsedGlyphData.offsetX = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                                break; 

                            case 3:
                                // interpret glyph offset Y
                                if (!IsEmpty(valueSpec)) 
                                    parsedGlyphData.offsetY = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                                break; 
 
                            default:
                                // too many commas; can't interpret 
                                throw new ArgumentException(SR.Get(SRID.GlyphsTooManyCommas));
                        }
                        #endregion Interpret one comma-delimited value
 
                        // prepare to scan next value (if any)
                        valueWithinGlyph++; 
                        valueStartIndex = i + 1; 
                    }
 
                    // finished processing the current glyph?
                    if ((c == ';') || (i == glyphsProp.Length))
                    {
                        parsedGlyphs.Add(parsedGlyphData); 
                        parsedGlyphData = new ParsedGlyphData();
 
                        if (inCluster) 
                        {
                            --glyphClusterSize; 
                            // when we reach the end of a glyph cluster, increment character index
                            if (glyphClusterSize == 0)
                            {
                                parsedCharacterCount += characterClusterSize; 
                                inCluster = false;
                            } 
                        } 
                        else
                        { 
                            ++parsedCharacterCount;
                        }
                        parsedGlyphCount++;
 
                        // initalize new per-glyph values
                        valueWithinGlyph = 0; // which value we're on (how many commas have we seen in this glyph)? 
                        valueStartIndex = i + 1; // where (what index of Glyphs prop string) did this value start? 
                    }
                } 
            }
            #endregion

            // fill the remaining glyphs with defaults, assuming 1:1 mapping 
            if (unicodeString != null)
            { 
                while (parsedCharacterCount < unicodeString.Length) 
                {
                    if (inCluster) 
                        throw new ArgumentException(SR.Get(SRID.GlyphsIndexRequiredWithinCluster));

                    if (unicodeString.Length <= parsedCharacterCount)
                        throw new ArgumentException(SR.Get(SRID.GlyphsUnicodeStringIsTooShort)); 

                    parsedGlyphData.glyphIndex = GetGlyphFromCharacter(fontFace, unicodeString[parsedCharacterCount]); 
                    parsedGlyphData.advanceWidth = GetAdvanceWidth(fontFace, parsedGlyphData.glyphIndex, sideways); 
                    parsedGlyphs.Add(parsedGlyphData);
                    parsedGlyphData = new ParsedGlyphData(); 
                    SetClusterMapEntry(clusterMap, parsedCharacterCount, (ushort)parsedGlyphCount);
                    ++parsedCharacterCount;
                    ++parsedGlyphCount;
                } 
            }
 
            // return number of glyphs actually specified 
            return parsedGlyphCount;
        } 
        // ------------------------------------------------------------------------------------------------
        // Parses a semicolon-delimited list of glyph specifiers, each of which consists
        // of up to 4 comma-delimited values:
        //   - glyph index (ushort)
        //   - glyph advance (double)
        //   - glyph offset X (double)
        //   - glyph offset Y (double)
        // A glyph entry can be have a cluster size prefix (int or pair of ints separated by a colon)
        // Whitespace adjacent to a delimiter (comma or semicolon) is ignored.
        // Returns the number of glyph specs parsed (number of semicolons plus 1).
        // ------------------------------------------------------------------------------------------------
        private int ParseGlyphsProperty(GlyphTypeface fontFace, string unicodeString, bool sideways,
                                        out List <ParsedGlyphData> parsedGlyphs, out ushort[] clusterMap)
        {
            string glyphsProp = string.Empty;

            // init for the whole parse, including the result arrays
            int parsedGlyphCount     = 0;
            int parsedCharacterCount = 0;

            int characterClusterSize = 1;
            int glyphClusterSize     = 1;

            bool inCluster = false;

            // make reasonable capacity guess on how many glyphs we can expect
            int estimatedNumberOfGlyphs;

            if (!string.IsNullOrEmpty(unicodeString))
            {
                clusterMap = new ushort[unicodeString.Length];
                estimatedNumberOfGlyphs = unicodeString.Length;
            }
            else
            {
                clusterMap = null;
                estimatedNumberOfGlyphs = 8;
            }

            if (!string.IsNullOrEmpty(glyphsProp))
            {
                estimatedNumberOfGlyphs = Math.Max(estimatedNumberOfGlyphs, glyphsProp.Length / 5);
            }

            parsedGlyphs = new List <ParsedGlyphData>(estimatedNumberOfGlyphs);

            ParsedGlyphData parsedGlyphData = new ParsedGlyphData();

            if (!string.IsNullOrEmpty(glyphsProp))
            {
                // init per-glyph values for the first glyph/position
                int valueWithinGlyph = 0; // which value we're on (how many commas have we seen in this glyph)?
                int valueStartIndex  = 0; // where (what index of Glyphs prop string) did this value start?

                // iterate and parse the characters of the Indices property
                for (int i = 0; i <= glyphsProp.Length; i++)
                {
                    // get next char or pseudo-terminator
                    char c = i < glyphsProp.Length ? glyphsProp[i] : '\0';

                    // finished scanning the current per-glyph value?
                    if ((c == ',') || (c == ';') || (i == glyphsProp.Length))
                    {
                        int len = i - valueStartIndex;

                        string valueSpec = glyphsProp.Substring(valueStartIndex, len);

                        switch (valueWithinGlyph)
                        {
                        case 0:
                            bool wasInCluster = inCluster;
                            // interpret cluster size and glyph index spec
                            if (!ReadGlyphIndex(valueSpec, ref inCluster, ref glyphClusterSize,
                                                ref characterClusterSize, ref parsedGlyphData.glyphIndex))
                            {
                                if (string.IsNullOrEmpty(unicodeString))
                                {
                                    throw new ArgumentException();
                                }

                                if (unicodeString.Length <= parsedCharacterCount)
                                {
                                    throw new ArgumentException();
                                }

                                parsedGlyphData.glyphIndex = GetGlyphFromCharacter(fontFace, unicodeString[parsedCharacterCount]);
                            }

                            if (!wasInCluster && clusterMap != null)
                            {
                                // fill out cluster map at the start of each cluster
                                if (inCluster)
                                {
                                    for (int ch = parsedCharacterCount; ch < parsedCharacterCount + characterClusterSize; ++ch)
                                    {
                                        SetClusterMapEntry(clusterMap, ch, (ushort)parsedGlyphCount);
                                    }
                                }
                                else
                                {
                                    SetClusterMapEntry(clusterMap, parsedCharacterCount, (ushort)parsedGlyphCount);
                                }
                            }
                            parsedGlyphData.advanceWidth = GetAdvanceWidth(fontFace, parsedGlyphData.glyphIndex, sideways);
                            break;

                        case 1:
                            // interpret glyph advance spec
                            if (!IsEmpty(valueSpec))
                            {
                                parsedGlyphData.advanceWidth = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                                if (parsedGlyphData.advanceWidth < 0)
                                {
                                    throw new ArgumentException();
                                }
                            }
                            break;

                        case 2:
                            // interpret glyph offset X
                            if (!IsEmpty(valueSpec))
                            {
                                parsedGlyphData.offsetX = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                            }
                            break;

                        case 3:
                            // interpret glyph offset Y
                            if (!IsEmpty(valueSpec))
                            {
                                parsedGlyphData.offsetY = double.Parse(valueSpec, CultureInfo.InvariantCulture);
                            }
                            break;

                        default:
                            // too many commas; can't interpret
                            throw new ArgumentException();
                        }

                        // prepare to scan next value (if any)
                        valueWithinGlyph++;
                        valueStartIndex = i + 1;
                    }

                    // finished processing the current glyph?
                    if ((c == ';') || (i == glyphsProp.Length))
                    {
                        parsedGlyphs.Add(parsedGlyphData);
                        parsedGlyphData = new ParsedGlyphData();

                        if (inCluster)
                        {
                            --glyphClusterSize;
                            // when we reach the end of a glyph cluster, increment character index
                            if (glyphClusterSize == 0)
                            {
                                parsedCharacterCount += characterClusterSize;
                                inCluster             = false;
                            }
                        }
                        else
                        {
                            ++parsedCharacterCount;
                        }
                        parsedGlyphCount++;

                        // initalize new per-glyph values
                        valueWithinGlyph = 0;     // which value we're on (how many commas have we seen in this glyph)?
                        valueStartIndex  = i + 1; // where (what index of Glyphs prop string) did this value start?
                    }
                }
            }

            // fill the remaining glyphs with defaults, assuming 1:1 mapping
            if (unicodeString != null)
            {
                while (parsedCharacterCount < unicodeString.Length)
                {
                    if (inCluster)
                    {
                        throw new ArgumentException();
                    }

                    if (unicodeString.Length <= parsedCharacterCount)
                    {
                        throw new ArgumentException();
                    }

                    parsedGlyphData.glyphIndex   = GetGlyphFromCharacter(fontFace, unicodeString[parsedCharacterCount]);
                    parsedGlyphData.advanceWidth = GetAdvanceWidth(fontFace, parsedGlyphData.glyphIndex, sideways);
                    parsedGlyphs.Add(parsedGlyphData);
                    parsedGlyphData = new ParsedGlyphData();
                    SetClusterMapEntry(clusterMap, parsedCharacterCount, (ushort)parsedGlyphCount);
                    ++parsedCharacterCount;
                    ++parsedGlyphCount;
                }
            }

            // return number of glyphs actually specified
            return(parsedGlyphCount);
        }