/// <summary> /// Break a run of text into individually shape items. /// Shape items are delimited by /// Change of writing system /// Change of glyph typeface /// </summary> IList<TextShapeableSymbols> ITextSymbols.GetTextShapeableSymbols( GlyphingCache glyphingCache, CharacterBufferReference characterBufferReference, int length, bool rightToLeft, bool isRightToLeftParagraph, CultureInfo digitCulture, TextModifierScope textModifierScope, TextFormattingMode textFormattingMode, bool isSideways ) { if (characterBufferReference.CharacterBuffer == null) { throw new ArgumentNullException("characterBufferReference.CharacterBuffer"); } int offsetToFirstChar = characterBufferReference.OffsetToFirstChar - _characterBufferReference.OffsetToFirstChar; Debug.Assert(characterBufferReference.CharacterBuffer == _characterBufferReference.CharacterBuffer); Debug.Assert(offsetToFirstChar >= 0 && offsetToFirstChar < _length); if ( length < 0 || offsetToFirstChar + length > _length) { length = _length - offsetToFirstChar; } // Get the actual text run properties in effect, after invoking any // text modifiers that may be in scope. TextRunProperties textRunProperties = _textRunProperties; if (textModifierScope != null) { textRunProperties = textModifierScope.ModifyProperties(textRunProperties); } if (!rightToLeft) { // Fast loop early out for run with all non-complex characters // which can be optimized by not going thru shaping engine. int nominalLength; if (textRunProperties.Typeface.CheckFastPathNominalGlyphs( new CharacterBufferRange(characterBufferReference, length), textRunProperties.FontRenderingEmSize, 1.0, double.MaxValue, // widthMax true, // keepAWord digitCulture != null, CultureMapper.GetSpecificCulture(textRunProperties.CultureInfo), textFormattingMode, isSideways, false, //breakOnTabs out nominalLength ) && length == nominalLength) { return new TextShapeableCharacters[] { new TextShapeableCharacters( new CharacterBufferRange(characterBufferReference, nominalLength), textRunProperties, textRunProperties.FontRenderingEmSize, new MS.Internal.Text.TextInterface.ItemProps(), null, // shapeTypeface (no shaping required) false, // nullShape, textFormattingMode, isSideways ) }; } } IList<TextShapeableSymbols> shapeables = new List<TextShapeableSymbols>(2); glyphingCache.GetShapeableText( textRunProperties.Typeface, characterBufferReference, length, textRunProperties, digitCulture, isRightToLeftParagraph, shapeables, this as IShapeableTextCollector, textFormattingMode ); return shapeables; }
public FormattedTextSymbols( GlyphingCache glyphingCache, TextRun textSymbols, bool rightToLeft, double scalingFactor, TextFormattingMode textFormattingMode, bool isSideways ) { _textFormattingMode = textFormattingMode; _isSideways = isSideways; ITextSymbols symbols = textSymbols as ITextSymbols; Debug.Assert(symbols != null); // break down a single text run into pieces IList<TextShapeableSymbols> shapeables = symbols.GetTextShapeableSymbols( glyphingCache, textSymbols.CharacterBufferReference, textSymbols.Length, rightToLeft, // This is a bool indicating the RTL // based on the bidi level of text (if applicable). // For FormattedTextSymbols it is equal to paragraph flow direction. rightToLeft, // This is the flow direction of the paragraph as // specified by the user. DWrite needs the paragraph // flow direction of the paragraph // while WPF algorithms need the RTL of the text based on // Bidi if possible. null, // cultureInfo null, // textModifierScope _textFormattingMode, _isSideways ); Debug.Assert(shapeables != null && shapeables.Count > 0); _rightToLeft = rightToLeft; _glyphs = new Glyphs[shapeables.Count]; CharacterBuffer charBuffer = textSymbols.CharacterBufferReference.CharacterBuffer; int offsetToFirstChar = textSymbols.CharacterBufferReference.OffsetToFirstChar; int i = 0; int ich = 0; while (i < shapeables.Count) { TextShapeableSymbols current = shapeables[i] as TextShapeableSymbols; Debug.Assert(current != null); int cch = current.Length; int j; // make a separate character buffer for glyphrun persistence char[] charArray = new char[cch]; for (j = 0; j < cch; j++) charArray[j] = charBuffer[offsetToFirstChar + ich + j]; if (current.IsShapingRequired) { ushort[] clusterMap; ushort[] glyphIndices; int[] glyphAdvances; GlyphOffset[] glyphOffsets; // unsafe { fixed (char* fixedCharArray = &charArray[0]) { MS.Internal.Text.TextInterface.TextAnalyzer textAnalyzer = MS.Internal.FontCache.DWriteFactory.Instance.CreateTextAnalyzer(); GlyphTypeface glyphTypeface = current.GlyphTypeFace; DWriteFontFeature[][] fontFeatures; uint[] fontFeatureRanges; uint unsignedCch = checked((uint)cch); LSRun.CompileFeatureSet(current.Properties.TypographyProperties, unsignedCch, out fontFeatures, out fontFeatureRanges); textAnalyzer.GetGlyphsAndTheirPlacements( (ushort*)fixedCharArray, unsignedCch, glyphTypeface.FontDWrite, glyphTypeface.BlankGlyphIndex, false, // no sideway support yet /************************************************************************************************/ // rightToLeft, current.Properties.CultureInfo, /************************************************************************************************/ fontFeatures, fontFeatureRanges, current.Properties.FontRenderingEmSize, scalingFactor, Util.PixelsPerDip, _textFormattingMode, current.ItemProps, out clusterMap, out glyphIndices, out glyphAdvances, out glyphOffsets ); } _glyphs[i] = new Glyphs( current, charArray, glyphAdvances, clusterMap, glyphIndices, glyphOffsets, scalingFactor ); } } else { // shaping not required, // bypass glyphing process altogether int[] nominalAdvances = new int[charArray.Length]; unsafe { fixed (char* fixedCharArray = &charArray[0]) fixed (int* fixedNominalAdvances = &nominalAdvances[0]) { current.GetAdvanceWidthsUnshaped( fixedCharArray, cch, scalingFactor, // format resolution specified per em, fixedNominalAdvances ); } } _glyphs[i] = new Glyphs( current, charArray, nominalAdvances, scalingFactor ); } i++; ich += cch; } }