// ------------------------------------------------------------------------------ // 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); }