Пример #1
0
        private IList<TextEffect>       _textEffects;               // TextEffects that should be applied for this run
 

        /// <summary>
        /// Construct an lsrun
        /// </summary> 
        internal LSRun(
            TextRunInfo             runInfo, 
            IList<TextEffect>       textEffects, 
            Plsrun                  type,
            int                     offsetToFirstCp, 
            int                     textRunLength,
            int                     emSize,
            ushort                  charFlags,
            CharacterBufferRange    charBufferRange, 
            TextShapeableSymbols    shapeable,
            double                  realToIdeal, 
            byte                    bidiLevel 
            ) :
            this( 
                runInfo,
                textEffects,
                type,
                offsetToFirstCp, 
                textRunLength,
                emSize, 
                charFlags, 
                charBufferRange,
                (shapeable != null ? (int)Math.Round(shapeable.Baseline * realToIdeal) : 0), 
                (shapeable != null ? (int)Math.Round(shapeable.Height * realToIdeal) : 0),
                shapeable,
                bidiLevel
                ) 
        {}
Пример #2
0
 /// <summary>
 /// Construct an lsrun
 /// </summary>
 private LSRun(
     TextRunInfo runInfo,
     IList <TextEffect> textEffects,
     Plsrun type,
     int offsetToFirstCp,
     int textRunLength,
     int emSize,
     ushort charFlags,
     CharacterBufferRange charBufferRange,
     int baselineOffset,
     int height,
     TextShapeableSymbols shapeable,
     byte bidiLevel
     )
 {
     _runInfo         = runInfo;
     _type            = type;
     _offsetToFirstCp = offsetToFirstCp;
     _textRunLength   = textRunLength;
     _emSize          = emSize;
     _charFlags       = charFlags;
     _charBufferRange = charBufferRange;
     _baselineOffset  = baselineOffset;
     _height          = height;
     _bidiLevel       = bidiLevel;
     _shapeable       = shapeable;
     _textEffects     = textEffects;
 }
Пример #3
0
        public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int textSourceCharacterIndexLimit)
        {
            CharacterBufferRange range1 = new CharacterBufferRange(text, 0, Math.Min(text.Length, textSourceCharacterIndexLimit));
            CultureSpecificCharacterBufferRange range2 = new CultureSpecificCharacterBufferRange(Thread.CurrentThread.CurrentUICulture, range1);

            return(new TextSpan <CultureSpecificCharacterBufferRange>(range1.Length, range2));
        }
Пример #4
0
        private IList <TextEffect> _textEffects;                    // TextEffects that should be applied for this run


        /// <summary>
        /// Construct an lsrun
        /// </summary>
        internal LSRun(
            TextRunInfo runInfo,
            IList <TextEffect> textEffects,
            Plsrun type,
            int offsetToFirstCp,
            int textRunLength,
            int emSize,
            ushort charFlags,
            CharacterBufferRange charBufferRange,
            TextShapeableSymbols shapeable,
            double realToIdeal,
            byte bidiLevel
            ) :
            this(
                runInfo,
                textEffects,
                type,
                offsetToFirstCp,
                textRunLength,
                emSize,
                charFlags,
                charBufferRange,
                (shapeable != null ? (int)Math.Round(shapeable.Baseline * realToIdeal) : 0),
                (shapeable != null ? (int)Math.Round(shapeable.Height * realToIdeal) : 0),
                shapeable,
                bidiLevel
                )
        {
        }
Пример #5
0
 public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int textSourceCharacterIndexLimit)
 {
     try {
         foreach (VisualLineElement element in VisualLine.Elements)
         {
             if (textSourceCharacterIndexLimit > element.VisualColumn &&
                 textSourceCharacterIndexLimit <= element.VisualColumn + element.VisualLength)
             {
                 TextSpan <CultureSpecificCharacterBufferRange> span = element.GetPrecedingText(textSourceCharacterIndexLimit, this);
                 if (span == null)
                 {
                     break;
                 }
                 int relativeOffset = textSourceCharacterIndexLimit - element.VisualColumn;
                 if (span.Length > relativeOffset)
                 {
                     throw new ArgumentException("The returned TextSpan is too long.", element.GetType().Name + ".GetPrecedingText");
                 }
                 return(span);
             }
         }
         CharacterBufferRange empty = CharacterBufferRange.Empty;
         return(new TextSpan <CultureSpecificCharacterBufferRange>(empty.Length, new CultureSpecificCharacterBufferRange(null, empty)));
     } catch (Exception ex) {
         Debug.WriteLine(ex.ToString());
         throw;
     }
 }
Пример #6
0
        /// <summary>
        /// Get family name correspondent to the first n-characters of the specified character string
        /// </summary>
        bool IFontFamily.GetMapTargetFamilyNameAndScale(
            CharacterBufferRange unicodeString,
            CultureInfo culture,
            CultureInfo digitCulture,
            double defaultSizeInEm,
            out int cchAdvance,
            out string targetFamilyName,
            out double scaleInEm
            )
        {
            Invariant.Assert(unicodeString.CharacterBuffer != null && unicodeString.Length > 0);
            Invariant.Assert(culture != null);

            // Get the family map. This will find the first family map that matches
            // the specified culture, an ancestor neutral culture, or "any" culture.
            FontFamilyMap familyMap = GetTargetFamilyMap(
                unicodeString,
                culture,
                digitCulture,
                out cchAdvance
                );

            // Return the values for the matching FontFamilyMap. If there is none this is
            // FontFamilyMap.Default which has Target == null and Scale == 1.0.
            targetFamilyName = familyMap.Target;
            scaleInEm        = familyMap.Scale;

            return(true);
        }
        /// <summary>
        /// Get spans of index to the list of scaled shapeable typeface of the specified
        /// character string from the map table
        /// </summary>
        private bool GetCachedScaledTypefaceMap(
            CharacterBufferRange unicodeString,
            CultureInfo culture,
            CultureInfo digitCulture,
            ref SpanVector <int> cachedScaledTypefaceIndexSpans,
            int ichItem
            )
        {
            IntMap map;

            if (!_intMaps.TryGetValue(culture, out map))
            {
                return(false);
            }

            DigitMap digitMap = new DigitMap(digitCulture);

            int ich = 0;

            while (ich < unicodeString.Length)
            {
                // Get map entry for first character.
                int sizeofChar;
                int ch = digitMap[
                    Classification.UnicodeScalar(
                        new CharacterBufferRange(unicodeString, ich, unicodeString.Length - ich),
                        out sizeofChar
                        )
                         ];

                ushort firstIndex = map[ch];
                if (firstIndex == 0)
                {
                    return(false);
                }

                // Advance past subsequent characters with the same mapping.
                int cchSpan = sizeofChar;
                for (; ich + cchSpan < unicodeString.Length; cchSpan += sizeofChar)
                {
                    ch = digitMap[
                        Classification.UnicodeScalar(
                            new CharacterBufferRange(unicodeString, ich + cchSpan, unicodeString.Length - ich - cchSpan),
                            out sizeofChar
                            )
                         ];

                    if (map[ch] != firstIndex && !Classification.IsCombining(ch) && !Classification.IsJoiner(ch))
                    {
                        break;
                    }
                }

                // map entry is stored in index+1, since 0 indicates uninitialized entry
                cachedScaledTypefaceIndexSpans.Set(ichItem + ich, cchSpan, firstIndex - 1);
                ich += cchSpan;
            }
            return(true);
        }
Пример #8
0
 public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int textSourceCharacterIndexLimit)
 {
     var cbr = new CharacterBufferRange(_text, 0, textSourceCharacterIndexLimit);
     return new TextSpan<CultureSpecificCharacterBufferRange>(
         textSourceCharacterIndexLimit,
         new CultureSpecificCharacterBufferRange(CultureInfo.CurrentUICulture, cbr)
         );
 }
Пример #9
0
        public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int textSourceCharacterIndexLimit)
        {
            var cbr = new CharacterBufferRange(Text, 0, textSourceCharacterIndexLimit);

            return(new TextSpan <CultureSpecificCharacterBufferRange>(
                       textSourceCharacterIndexLimit,
                       new CultureSpecificCharacterBufferRange(System.Globalization.CultureInfo.CurrentUICulture, cbr)
                       ));
        }
Пример #10
0
        internal void Truncate(int newLength)
        {
            _charBufferRange = new CharacterBufferRange(
                _charBufferRange.CharacterBufferReference,
                newLength
                );

            _textRunLength = newLength;
        }
Пример #11
0
        /// <inheritdoc/>
        public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int visualColumnLimit, ITextRunConstructionContext context)
        {
            if (context == null)
                throw new ArgumentNullException(nameof(context));

            int relativeOffset = visualColumnLimit - VisualColumn;
            StringSegment text = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset, relativeOffset);
            CharacterBufferRange range = new CharacterBufferRange(text.Text, text.Offset, text.Count);
            return new TextSpan<CultureSpecificCharacterBufferRange>(range.Length, new CultureSpecificCharacterBufferRange(TextRunProperties.CultureInfo, range));
        }
Пример #12
0
        // Token: 0x0600666E RID: 26222 RVA: 0x001CC420 File Offset: 0x001CA620
        public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp)
        {
            CharacterBufferRange empty   = CharacterBufferRange.Empty;
            CultureInfo          culture = null;

            if (dcp > 0)
            {
                empty   = new CharacterBufferRange(this._content, 0, Math.Min(dcp, this._content.Length));
                culture = this._textProps.CultureInfo;
            }
            return(new TextSpan <CultureSpecificCharacterBufferRange>(dcp, new CultureSpecificCharacterBufferRange(culture, empty)));
        }
Пример #13
0
        /// <inheritdoc/>
        public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int visualColumnLimit, ITextRunConstructionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int                  relativeOffset = visualColumnLimit - VisualColumn;
            StringSegment        text           = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset, relativeOffset);
            CharacterBufferRange range          = new CharacterBufferRange(text.Text, text.Offset, text.Count);

            return(new TextSpan <CultureSpecificCharacterBufferRange>(range.Length, new CultureSpecificCharacterBufferRange(this.TextRunProperties.CultureInfo, range)));
        }
Пример #14
0
        /// <summary>
        /// Lookup characters nominal glyphs and width
        /// </summary>
        /// <param name="charBufferRange">character buffer range</param>
        /// <param name="emSize">height of Em</param>
        /// <param name="toIdeal"> scaling factor from real to ideal unit </param>
        /// <param name="nominalWidths">glyph nominal advances in ideal units</param>
        /// <param name="idealWidth">total width in ideal units</param>
        /// <returns>true for success</returns>
        /// <remarks>This function is only used in fast path, and can only be called
        /// if CheckFastPathNominalGlyphs has previously returned true.</remarks>
        internal void GetCharacterNominalWidthsAndIdealWidth(
            CharacterBufferRange charBufferRange,
            double emSize,
            float pixelsPerDip,
            double toIdeal,
            TextFormattingMode textFormattingMode,
            bool isSideways,
            out int[]            nominalWidths,
            out int idealWidth
            )
        {
            // This function should only be called if CheckFastPathNominalGlyphs has
            // returned true so we can assume the ITypefaceMetrics is a GlyphTypeface.
            GlyphTypeface glyphTypeface = TryGetGlyphTypeface();

            Invariant.Assert(glyphTypeface != null);

            MS.Internal.Text.TextInterface.GlyphMetrics[] glyphMetrics = BufferCache.GetGlyphMetrics(charBufferRange.Length);

            glyphTypeface.GetGlyphMetricsOptimized(charBufferRange,
                                                   emSize,
                                                   pixelsPerDip,
                                                   textFormattingMode,
                                                   isSideways,
                                                   glyphMetrics);

            nominalWidths = new int[charBufferRange.Length];
            idealWidth    = 0;

            if (TextFormattingMode.Display == textFormattingMode)
            {
                double designToEm = emSize / glyphTypeface.DesignEmHeight;
                for (int i = 0; i < charBufferRange.Length; i++)
                {
                    nominalWidths[i] = (int)Math.Round(TextFormatterImp.RoundDipForDisplayMode(glyphMetrics[i].AdvanceWidth * designToEm, pixelsPerDip) * toIdeal);
                    idealWidth      += nominalWidths[i];
                }
            }
            else
            {
                double designToEm = emSize * toIdeal / glyphTypeface.DesignEmHeight;
                for (int i = 0; i < charBufferRange.Length; i++)
                {
                    nominalWidths[i] = (int)Math.Round(glyphMetrics[i].AdvanceWidth * designToEm);
                    idealWidth      += nominalWidths[i];
                }
            }

            BufferCache.ReleaseGlyphMetrics(glyphMetrics);
        }
Пример #15
0
 /// <summary>
 /// Get family name correspondent to the first n-characters of the specified character string
 /// </summary>
 bool IFontFamily.GetMapTargetFamilyNameAndScale(
     CharacterBufferRange unicodeString,
     CultureInfo culture,
     CultureInfo digitCulture,
     double defaultSizeInEm,
     out int cchAdvance,
     out string targetFamilyName,
     out double scaleInEm
     )
 {
     cchAdvance       = unicodeString.Length;
     targetFamilyName = null;
     scaleInEm        = defaultSizeInEm;
     return(false);
 }
 /// <summary>
 /// Map characters by font family name
 /// </summary>
 private int MapByFontFamilyName(
     CharacterBufferRange unicodeString,
     CultureInfo culture,
     CultureInfo digitCulture,
     string familyName,
     Uri baseUri,
     ref PhysicalFontFamily firstValidFamily,
     ref int firstValidLength,
     IDeviceFont deviceFont,
     double scaleInEm,
     int fontMappingDepth,
     SpanVector scaledTypefaceSpans,
     int firstCharIndex,
     out int nextValid
     )
 {
     if (familyName == null)
     {
         return(MapUnresolvedCharacters(
                    unicodeString,
                    culture,
                    digitCulture,
                    firstValidFamily,
                    ref firstValidLength,
                    scaledTypefaceSpans,
                    firstCharIndex,
                    out nextValid
                    ));
     }
     else
     {
         // Map as many characters as we can to families in the list.
         return(MapByFontFamilyList(
                    unicodeString,
                    culture,
                    digitCulture,
                    new FontFamily[] { new FontFamily(baseUri, familyName) },
                    ref firstValidFamily,
                    ref firstValidLength,
                    deviceFont,
                    scaleInEm,
                    fontMappingDepth,
                    scaledTypefaceSpans,
                    firstCharIndex,
                    out nextValid
                    ));
     }
 }
        // Token: 0x06004132 RID: 16690 RVA: 0x0012A184 File Offset: 0x00128384
        public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp)
        {
            CharacterBufferRange empty   = CharacterBufferRange.Empty;
            CultureInfo          culture = null;

            if (dcp > 0)
            {
                ITextPointer textPointer = this._owner.Host.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward);
                int          num         = Math.Min(128, textPointer.GetTextRunLength(LogicalDirection.Backward));
                char[]       array       = new char[num];
                textPointer.GetTextInRun(LogicalDirection.Backward, array, 0, num);
                empty   = new CharacterBufferRange(array, 0, num);
                culture = DynamicPropertyReader.GetCultureInfo((Control)this._owner.Host);
            }
            return(new TextSpan <CultureSpecificCharacterBufferRange>(empty.Length, new CultureSpecificCharacterBufferRange(culture, empty)));
        }
Пример #18
0
        /// <summary>
        /// Gets the character to glyph ID mapping.
        /// </summary>
        /// <param name="typeface">The typeface.</param>
        /// <returns>Dictionary with pairs character - glyphId.</returns>
        public IDictionary <char, ushort> GetCharacterToGlyphIdMapping(GlyphTypeface typeface)
        {
            /* Generate sequence of all characters representable in .Net and feed it into WPF conversion function
             * - it will process the characters through its internal representation of CMAP and return glyph IDs. */
            var conversionChars = Enumerable.Range(0, ushort.MaxValue).Select(p => (char)p).ToArray();
            var characterBuffer = new CharacterBufferRange(conversionChars, 0, conversionChars.Length);

            dynamic typefaceDynamic = new AccessPrivateWrapper(typeface);

            var glyphIdArray = new ushort[conversionChars.Length];

            typefaceDynamic.GetGlyphIndicesOptimized(characterBuffer, glyphIdArray);

            return(conversionChars
                   .Zip(glyphIdArray, (charId, glyphId) => new { charId, glyphId })
                   .Where(p => p.glyphId != 0)
                   .ToDictionary(p => p.charId, p => p.glyphId));
        }
        /// <summary>
        /// Maps characters that could not be resolved to any font family either to the first
        /// valid physical font family or to the default font we use for display null glyphs.
        /// </summary>
        private int MapUnresolvedCharacters(
            CharacterBufferRange unicodeString,
            CultureInfo culture,
            CultureInfo digitCulture,
            PhysicalFontFamily firstValidFamily,
            ref int firstValidLength,
            SpanVector scaledTypefaceSpans,
            int firstCharIndex,
            out int nextValid
            )
        {
            // If we have a valid font family use it. We don't set nullFont to true in this case.
            // We may end up displaying missing glyphs, but we don't need to force it.
            IFontFamily fontFamily = firstValidFamily;
            bool        nullFont   = false;

            if (firstValidLength <= 0)
            {
                // We didn't find any valid physical font family so use the default "Arial", and
                // set nullFont to true to ensure that we always display missing glyphs.
                fontFamily = FontFamily.LookupFontFamily(FontFamily.NullFontFamilyCanonicalName);
                Invariant.Assert(fontFamily != null);
                nullFont = true;
            }

            return(MapByFontFaceFamily(
                       unicodeString,
                       culture,
                       digitCulture,
                       fontFamily,
                       _canonicalStyle,
                       _canonicalWeight,
                       _canonicalStretch,
                       ref firstValidFamily,
                       ref firstValidLength,
                       null, // device font
                       nullFont,
                       1.0,
                       scaledTypefaceSpans,
                       firstCharIndex,
                       true, // ignore missing
                       out nextValid
                       ));
        }
Пример #20
0
        /// <summary>
        /// TextFormatter to get text immediately before specified text source position.
        /// </summary>
        public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(
            int textSourceCharacterIndexLimit
            )
        {
            CharacterBufferRange charString = CharacterBufferRange.Empty;

            if (textSourceCharacterIndexLimit > 0)
            {
                charString = new CharacterBufferRange(
                    new CharacterBufferReference(_characterArray, 0),
                    Math.Min(_characterArray.Length, textSourceCharacterIndexLimit)
                    );
            }

            return(new TextSpan <CultureSpecificCharacterBufferRange> (
                       textSourceCharacterIndexLimit,
                       new CultureSpecificCharacterBufferRange(CultureMapper.GetSpecificCulture(_textRunProperties.CultureInfo), charString)
                       ));
        }
Пример #21
0
 internal LSRun(
     TextRunInfo runInfo,
     Plsrun type,
     IntPtr controlChar,
     int textRunLength,
     int offsetToFirstCp,
     byte bidiLevel
     )
 {
     unsafe
     {
         _runInfo         = runInfo;
         _type            = type;
         _charBufferRange = new CharacterBufferRange((char *)controlChar, 1);
         _textRunLength   = textRunLength;
         _offsetToFirstCp = offsetToFirstCp;
         _bidiLevel       = bidiLevel;
     }
 }
 /// <summary>
 /// Construct a shapeable characters object
 /// </summary>
 /// <remarks>
 /// The shapeTypeface parameter can be null if and only if CheckFastPathNominalGlyphs
 /// has previously returned true.
 /// </remarks>
 internal TextShapeableCharacters(
     CharacterBufferRange    characterRange,
     TextRunProperties       properties,
     double                  emSize,
     ItemProps               textItem,
     ShapeTypeface           shapeTypeface,
     bool                    nullShape,
     TextFormattingMode      textFormattingMode,
     bool isSideways
     )
 {
     _isSideways = isSideways;
     _textFormattingMode = textFormattingMode;
     _characterBufferRange = characterRange;
     _properties = properties;
     _emSize = emSize;
     _textItem = textItem;
     _shapeTypeface = shapeTypeface;
     _nullShape = nullShape;
 }
Пример #23
0
        /// <summary>
        /// Lookup characters nominal glyphs and width
        /// </summary>
        /// <param name="charBufferRange">character buffer range</param>
        /// <param name="emSize">height of Em</param>
        /// <param name="toIdeal"> scaling factor from real to ideal unit </param>
        /// <param name="nominalWidths">glyph nominal advances in ideal units</param>
        /// <returns>true for success</returns>
        /// <remarks>This function is only used in fast path, and can only be called
        /// if CheckFastPathNominalGlyphs has previously returned true.</remarks>
        internal void GetCharacterNominalWidthsAndIdealWidth(
            CharacterBufferRange charBufferRange,
            double emSize,
            double toIdeal,
            TextFormattingMode textFormattingMode,
            bool isSideways,
            out int[]            nominalWidths
            )
        {
            int idealWidth;

            GetCharacterNominalWidthsAndIdealWidth(
                charBufferRange,
                emSize,
                toIdeal,
                textFormattingMode,
                isSideways,
                out nominalWidths,
                out idealWidth
                );
        }
Пример #24
0
            private List <OrderedTextRun> ReorderRuns()
            {
                var result           = new List <OrderedTextRun> ();
                var store            = _fullText.TextStore;
                var settings         = store.Settings;
                int cpFirst          = store.CpFirst;
                int pos              = cpFirst;
                int remaining_length = Length;

                // FIXME: Fetch bidi levels, reset to 0 for trailing whitespace

                while (remaining_length > 0)
                {
                    TextRun run;
                    int     runLength;
                    CharacterBufferRange chars = settings.FetchTextRun(pos, cpFirst, out run, out runLength);

                    if (runLength > remaining_length)
                    {
                        runLength = remaining_length;
                        chars     = new CharacterBufferRange(chars, 0, runLength);
                    }

                    // FIXME: determine bidi level and shorten runLength to keep bidi level constant

                    var ordered = new OrderedTextRun();
                    ordered.BidiLevel = 0;
                    ordered.TextRun   = run;
                    ordered.Range     = chars;
                    ordered.CpFirst   = pos;
                    result.Add(ordered);

                    remaining_length -= runLength;
                    pos += runLength;
                }

                // FIXME: reverse bidi levels

                return(result);
            }
Пример #25
0
        /// <summary>
        /// Get text immediately before specified text source position. Return CharacterBufferRange
        /// containing this text.
        /// </summary>
        /// <param name="dcp">
        /// dcp of position relative to start of line
        /// </param>
        internal override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp)
        {
            // Parameter validation
            Invariant.Assert(dcp >= 0);

            int nonTextLength = 0;
            CharacterBufferRange precedingText = CharacterBufferRange.Empty;
            CultureInfo          culture       = null;

            if (dcp > 0)
            {
                // Create TextPointer at dcp, and pointer at paragraph start to compare
                ITextPointer startPosition = TextContainerHelper.GetTextPointerFromCP(_paraClient.Paragraph.StructuralCache.TextContainer, _cpPara, LogicalDirection.Forward);
                ITextPointer position      = TextContainerHelper.GetTextPointerFromCP(_paraClient.Paragraph.StructuralCache.TextContainer, _cpPara + dcp, LogicalDirection.Forward);

                // Move backward until we find a position at the end of a text run, or reach start of TextContainer
                while (position.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text &&
                       position.CompareTo(startPosition) != 0)
                {
                    position.MoveByOffset(-1);
                    nonTextLength++;
                }


                // Return text in run. If it is at start of TextContainer this will return an empty string
                string precedingTextString = position.GetTextInRun(LogicalDirection.Backward);
                precedingText = new CharacterBufferRange(precedingTextString, 0, precedingTextString.Length);


                StaticTextPointer pointer = position.CreateStaticPointer();
                DependencyObject  element = (pointer.Parent != null) ? pointer.Parent : _paraClient.Paragraph.Element;
                culture = DynamicPropertyReader.GetCultureInfo(element);
            }

            return(new TextSpan <CultureSpecificCharacterBufferRange>(
                       nonTextLength + precedingText.Length,
                       new CultureSpecificCharacterBufferRange(culture, precedingText)
                       ));
        }
Пример #26
0
        // ------------------------------------------------------------------
        // Get text immediately before specified text source position.
        // ------------------------------------------------------------------
        public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp)
        {
            Debug.Assert(dcp >= 0, "Character index must be non-negative.");

            CharacterBufferRange charString = CharacterBufferRange.Empty;
            CultureInfo          culture    = null;

            if (dcp > 0)
            {
                charString = new CharacterBufferRange(
                    _content,
                    0,
                    Math.Min(dcp, _content.Length)
                    );
                culture = _textProps.CultureInfo;
            }

            return(new TextSpan <CultureSpecificCharacterBufferRange> (
                       dcp,
                       new CultureSpecificCharacterBufferRange(culture, charString)
                       ));
        }
        // Token: 0x06006600 RID: 26112 RVA: 0x001CAB08 File Offset: 0x001C8D08
        public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp)
        {
            int num = 0;
            CharacterBufferRange empty   = CharacterBufferRange.Empty;
            CultureInfo          culture = null;

            if (dcp > 0)
            {
                ITextPointer textPointer = this._owner.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward);
                while (textPointer.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text && textPointer.CompareTo(this._owner.TextContainer.Start) != 0)
                {
                    textPointer.MoveByOffset(-1);
                    num++;
                }
                string textInRun = textPointer.GetTextInRun(LogicalDirection.Backward);
                empty = new CharacterBufferRange(textInRun, 0, textInRun.Length);
                StaticTextPointer staticTextPointer = textPointer.CreateStaticPointer();
                DependencyObject  element           = (staticTextPointer.Parent != null) ? staticTextPointer.Parent : this._owner;
                culture = DynamicPropertyReader.GetCultureInfo(element);
            }
            return(new TextSpan <CultureSpecificCharacterBufferRange>(num + empty.Length, new CultureSpecificCharacterBufferRange(culture, empty)));
        }
Пример #28
0
        /// <summary>
        /// Compute Unicode scalar value from unicode codepoint stream
        /// </summary>
        static internal int UnicodeScalar(
            CharacterBufferRange unicodeString,
            out int sizeofChar
            )
        {
            Invariant.Assert(unicodeString.CharacterBuffer != null && unicodeString.Length > 0);

            int ch = unicodeString[0];

            sizeofChar = 1;

            if (unicodeString.Length >= 2 &&
                (ch & 0xFC00) == 0xD800 &&
                (unicodeString[1] & 0xFC00) == 0xDC00
                )
            {
                ch = (((ch & 0x03FF) << 10) | (unicodeString[1] & 0x3FF)) + 0x10000;
                sizeofChar++;
            }

            return(ch);
        }
Пример #29
0
        // Token: 0x06006864 RID: 26724 RVA: 0x001D6C0C File Offset: 0x001D4E0C
        internal override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp)
        {
            Invariant.Assert(dcp >= 0);
            int num = 0;
            CharacterBufferRange empty   = CharacterBufferRange.Empty;
            CultureInfo          culture = null;

            if (dcp > 0)
            {
                ITextPointer textPointerFromCP  = TextContainerHelper.GetTextPointerFromCP(this._paraClient.Paragraph.StructuralCache.TextContainer, this._cpPara, LogicalDirection.Forward);
                ITextPointer textPointerFromCP2 = TextContainerHelper.GetTextPointerFromCP(this._paraClient.Paragraph.StructuralCache.TextContainer, this._cpPara + dcp, LogicalDirection.Forward);
                while (textPointerFromCP2.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text && textPointerFromCP2.CompareTo(textPointerFromCP) != 0)
                {
                    textPointerFromCP2.MoveByOffset(-1);
                    num++;
                }
                string textInRun = textPointerFromCP2.GetTextInRun(LogicalDirection.Backward);
                empty = new CharacterBufferRange(textInRun, 0, textInRun.Length);
                StaticTextPointer staticTextPointer = textPointerFromCP2.CreateStaticPointer();
                DependencyObject  element           = (staticTextPointer.Parent != null) ? staticTextPointer.Parent : this._paraClient.Paragraph.Element;
                culture = DynamicPropertyReader.GetCultureInfo(element);
            }
            return(new TextSpan <CultureSpecificCharacterBufferRange>(num + empty.Length, new CultureSpecificCharacterBufferRange(culture, empty)));
        }
Пример #30
0
        // ------------------------------------------------------------------
        // Get text immediately before specified text source position.
        // ------------------------------------------------------------------
        public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp)
        {
            // Parameter validation
            Debug.Assert(dcp >= 0);

            int nonTextLength = 0;
            CharacterBufferRange precedingText = CharacterBufferRange.Empty;
            CultureInfo          culture       = null;

            if (dcp > 0)
            {
                // Create TextPointer at dcp
                ITextPointer position = _owner.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward);

                // Move backward until we find a position at the end of a text run, or reach start of TextContainer
                while (position.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text &&
                       position.CompareTo(_owner.TextContainer.Start) != 0)
                {
                    position.MoveByOffset(-1);
                    nonTextLength++;
                }

                // Return text in run. If it is at start of TextContainer this will return an empty string
                string precedingTextString = position.GetTextInRun(LogicalDirection.Backward);
                precedingText = new CharacterBufferRange(precedingTextString, 0, precedingTextString.Length);

                StaticTextPointer pointer = position.CreateStaticPointer();
                DependencyObject  element = (pointer.Parent != null) ? pointer.Parent : _owner;
                culture = DynamicPropertyReader.GetCultureInfo(element);
            }

            return(new TextSpan <CultureSpecificCharacterBufferRange>(
                       nonTextLength + precedingText.Length,
                       new CultureSpecificCharacterBufferRange(culture, precedingText)
                       ));
        }
Пример #31
0
        /// <summary>
        /// Get family name correspondent to the first n-characters of the specified character string
        /// </summary>
        /// <param name="unicodeString">character string</param>
        /// <param name="culture">text culture info</param>
        /// <param name="digitCulture">culture used for digit subsitution or null</param>
        /// <param name="defaultSizeInEm">default size relative to em</param>
        /// <param name="cchAdvance">number of characters advanced</param>
        /// <param name="targetFamilyName">target family name</param>
        /// <param name="scaleInEm">size relative to em</param>
        /// <returns>number of character sharing the same family name and size</returns>
        /// <remarks>
        ///
        /// Null target family name returned indicates that the font family cannot find target
        /// name of the character range being advanced.
        ///
        /// Return value false indicates that the font family has no character map.
        /// It is a font face family.
        ///
        /// </remarks>
        unsafe bool IFontFamily.GetMapTargetFamilyNameAndScale(
            CharacterBufferRange unicodeString,
            CultureInfo culture,
            CultureInfo digitCulture,
            double defaultSizeInEm,
            out int cchAdvance,
            out string targetFamilyName,
            out double scaleInEm
            )
        {
            Invariant.Assert(unicodeString.CharacterBuffer != null && unicodeString.Length > 0);
            Invariant.Assert(culture != null);

            // Get the family map. This will find the first family map that matches
            // the specified culture, an ancestor neutral culture, or "any" culture.
            FamilyCollection.CachedFamilyMap *familyMap = GetCachedFamilyMap(
                unicodeString,
                culture,
                digitCulture,
                out cchAdvance
                );

            if (familyMap == null)
            {
                targetFamilyName = null;
                scaleInEm        = 1;
            }
            else
            {
                int *sizePrefix = (int *)((byte *)familyMap + familyMap->targetFamilyNameOffset);
                targetFamilyName = Util.StringCopyFromUncheckedPointer(sizePrefix + 1, *sizePrefix);
                scaleInEm        = familyMap->scaleInEm;
            }

            return(true);
        }
Пример #32
0
        /// <summary>
        /// Get text immediately before specified text source position.
        /// </summary>
        public override TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp)
        {
            CharacterBufferRange precedingText = CharacterBufferRange.Empty;
            CultureInfo          culture       = null;

            if (dcp > 0)
            {
                // Create TextPointer at dcp
                ITextPointer position = _owner.Host.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward);

                // Return text in run. If it is at start of TextContainer this will return an empty string.
                // Typically the caller requires just the preceding character.  Worst case is the entire
                // preceding sentence, which we approximate with a 128 char limit.
                int     runLength = Math.Min(128, position.GetTextRunLength(LogicalDirection.Backward));
                char [] text      = new char[runLength];
                position.GetTextInRun(LogicalDirection.Backward, text, 0, runLength);

                precedingText = new CharacterBufferRange(text, 0, runLength);
                culture       = DynamicPropertyReader.GetCultureInfo((Control)_owner.Host);
            }

            return(new TextSpan <CultureSpecificCharacterBufferRange>(
                       precedingText.Length, new CultureSpecificCharacterBufferRange(culture, precedingText)));
        }
Пример #33
0
        /// <summary>
        /// Get text immediately preceding cpLimit.
        /// </summary>
        internal TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(TextSource textSource, int cpLimit)
        {
            if (cpLimit > 0)
            {
                SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition);
                if (textRunSpanRider.At(cpLimit - 1))
                {
                    CharacterBufferRange charString = CharacterBufferRange.Empty;
                    CultureInfo          culture    = null;

                    TextRun run = textRunSpanRider.CurrentElement as TextRun;

                    if (run != null)
                    {
                        // Only TextRun containing text would have non-empty Character buffer range.
                        if (TextRunInfo.GetRunType(run) == Plsrun.Text &&
                            run.CharacterBufferReference.CharacterBuffer != null)
                        {
                            charString = new CharacterBufferRange(
                                run.CharacterBufferReference,
                                cpLimit - textRunSpanRider.CurrentSpanStart);

                            culture = CultureMapper.GetSpecificCulture(run.Properties.CultureInfo);
                        }

                        return(new TextSpan <CultureSpecificCharacterBufferRange>(
                                   cpLimit - textRunSpanRider.CurrentSpanStart, // cp length
                                   new CultureSpecificCharacterBufferRange(culture, charString)
                                   ));
                    }
                }
            }

            // not in cache so call back to client
            return(textSource.GetPrecedingText(cpLimit));
        }
Пример #34
0
        // ----------------------------------------------------------------- 
        // Get text immediately before specified text source position.
        // ------------------------------------------------------------------ 
        public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp) 
        {
            // Parameter validation 
            Debug.Assert(dcp >= 0);

            int nonTextLength = 0;
            CharacterBufferRange precedingText = CharacterBufferRange.Empty; 
            CultureInfo culture = null;
 
            if (dcp > 0) 
            {
                // Create TextPointer at dcp 
                ITextPointer position = _owner.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward);

                // Move backward until we find a position at the end of a text run, or reach start of TextContainer
                while (position.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text && 
                       position.CompareTo(_owner.TextContainer.Start) != 0)
                { 
                    position.MoveByOffset(-1); 
                    nonTextLength++;
                } 

                // Return text in run. If it is at start of TextContainer this will return an empty string
                string precedingTextString = position.GetTextInRun(LogicalDirection.Backward);
                precedingText = new CharacterBufferRange(precedingTextString, 0, precedingTextString.Length); 

                StaticTextPointer pointer = position.CreateStaticPointer(); 
                DependencyObject element = (pointer.Parent != null) ? pointer.Parent : _owner; 
                culture = DynamicPropertyReader.GetCultureInfo(element);
            } 

            return new TextSpan<CultureSpecificCharacterBufferRange>(
                nonTextLength + precedingText.Length,
                new CultureSpecificCharacterBufferRange(culture, precedingText) 
                );
        } 
Пример #35
0
        // -----------------------------------------------------------------
        // Get text immediately before specified text source position. 
        // ------------------------------------------------------------------
        public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp)
        {
            Debug.Assert(dcp >= 0, "Character index must be non-negative."); 

            CharacterBufferRange charString = CharacterBufferRange.Empty; 
            CultureInfo culture = null; 

            if (dcp > 0) 
            {
                charString = new CharacterBufferRange(
                    _content,
                    0, 
                    Math.Min(dcp, _content.Length)
                    ); 
                culture = _textProps.CultureInfo; 
            }
 
            return new TextSpan<CultureSpecificCharacterBufferRange> (
                dcp,
                new CultureSpecificCharacterBufferRange(culture, charString)
                ); 
        }
Пример #36
0
        /// <summary> 
        /// Lookup characters nominal glyphs and width
        /// </summary> 
        /// <param name="charBufferRange">character buffer range</param>
        /// <param name="emSize">height of Em</param>
        /// <param name="toIdeal"> scaling factor from real to ideal unit </param>
        /// <param name="nominalWidths">glyph nominal advances in ideal units</param> 
        /// <param name="idealWidth">total width in ideal units</param>
        /// <returns>true for success</returns> 
        /// <remarks>This function is only used in fast path, and can only be called 
        /// if CheckFastPathNominalGlyphs has previously returned true.</remarks>
        internal void GetCharacterNominalWidthsAndIdealWidth( 
            CharacterBufferRange charBufferRange,
            double               emSize,
            double               toIdeal,
            TextFormattingMode   textFormattingMode, 
            bool                 isSideways,
            out int[]            nominalWidths, 
            out int              idealWidth 
            )
        { 
            // This function should only be called if CheckFastPathNominalGlyphs has
            // returned true so we can assume the ITypefaceMetrics is a GlyphTypeface.
            GlyphTypeface glyphTypeface = TryGetGlyphTypeface();
            Invariant.Assert(glyphTypeface != null); 

            MS.Internal.Text.TextInterface.GlyphMetrics[] glyphMetrics = BufferCache.GetGlyphMetrics(charBufferRange.Length); 
 
            glyphTypeface.GetGlyphMetricsOptimized(charBufferRange,
                                                   emSize, 
                                                   textFormattingMode,
                                                   isSideways,
                                                   glyphMetrics);
 
            nominalWidths = new int[charBufferRange.Length];
            idealWidth = 0; 
 
            if (TextFormattingMode.Display == textFormattingMode)
            { 
                double designToEm = emSize / glyphTypeface.DesignEmHeight;
                for (int i = 0; i < charBufferRange.Length; i++)
                {
                    nominalWidths[i] = (int)Math.Round(TextFormatterImp.RoundDipForDisplayMode(glyphMetrics[i].AdvanceWidth * designToEm) * toIdeal); 
                    idealWidth += nominalWidths[i];
                } 
            } 
            else
            { 
                double designToEm = emSize * toIdeal / glyphTypeface.DesignEmHeight;
                for (int i = 0; i < charBufferRange.Length; i++)
                {
                    nominalWidths[i] = (int)Math.Round(glyphMetrics[i].AdvanceWidth * designToEm); 
                    idealWidth += nominalWidths[i];
                } 
            } 

            BufferCache.ReleaseGlyphMetrics(glyphMetrics); 
        }
Пример #37
0
        /// <summary> 
        /// Scan through specified character string checking for valid character
        /// nominal glyph.
        /// </summary>
        /// <param name="charBufferRange">character buffer range</param> 
        /// <param name="emSize">height of Em</param>
        /// <param name="scalingFactor">This is the factor by which we will scale up 
        /// the metrics. Typically this value to used to convert metrics from the real 
        /// space to the ideal space</param>
        /// <param name="widthMax">maximum width allowed</param> 
        /// <param name="keepAWord">do not stop arbitrarily in the middle of a word</param>
        /// <param name="numberSubstitution">digits require complex shaping</param>
        /// <param name="cultureInfo">CultureInfo of the text</param>
        /// <param name="stringLengthFit">number of character fit in given width</param> 
        /// <returns>whether the specified string can be optimized by nominal glyph lookup</returns>
        internal bool CheckFastPathNominalGlyphs( 
            CharacterBufferRange    charBufferRange, 
            double                  emSize,
            double                  scalingFactor, 
            double                  widthMax,
            bool                    keepAWord,
            bool                    numberSubstitution,
            CultureInfo             cultureInfo, 
            TextFormattingMode      textFormattingMode,
            bool                    isSideways, 
            out int                 stringLengthFit 
            )
        { 
            stringLengthFit = 0;

            if (CachedTypeface.NullFont) return false;
 
            GlyphTypeface glyphTypeface = TryGetGlyphTypeface();
 
            if (glyphTypeface == null) return false; 

            double totalWidth = 0; 
            int i = 0;

            ushort blankGlyph = glyphTypeface.BlankGlyphIndex;
            ushort glyph = blankGlyph; 

            ushort charFlagsMask = numberSubstitution ? 
                (ushort)(CharacterAttributeFlags.CharacterComplex | CharacterAttributeFlags.CharacterDigit) : 
                (ushort)CharacterAttributeFlags.CharacterComplex;
            ushort charFlags = 0; 
            ushort charFastTextCheck = (ushort)(CharacterAttributeFlags.CharacterFastText | CharacterAttributeFlags.CharacterIdeo);

            bool symbolTypeface = glyphTypeface.Symbol;
            if (symbolTypeface) 
            {
                // we don't care what code points are present if it's a non-Unicode font such as Symbol or Wingdings; 
                // the code points don't have any standardized meanings, and we always want to bypass shaping 
                charFlagsMask = 0;
            } 

            bool ignoreWidths = widthMax == double.MaxValue;
            ushort[] glyphIndices = BufferCache.GetUShorts(charBufferRange.Length);
            MS.Internal.Text.TextInterface.GlyphMetrics[] glyphMetrics = ignoreWidths ? null : BufferCache.GetGlyphMetrics(charBufferRange.Length); 

            glyphTypeface.GetGlyphMetricsOptimized(charBufferRange, 
                                                   emSize, 
                                                   glyphIndices,
                                                   glyphMetrics, 
                                                   textFormattingMode,
                                                   isSideways
                                                   );
 
            double designToEm = emSize / glyphTypeface.DesignEmHeight;
 
            // 
            // This block will advance until one of:
            // 1. The end of the charBufferRange is reached 
            // 2. The charFlags have some of the charFlagsMask values
            // 3. The glyph is BlankGlyph (space)
            // 4. Glyph index is 0 (unless symbol font)
            // 
            // At this point totalWidth includes all of the widths including the stop character (which fits above)
            // i indexes the next character (not included in the width) 
            // 
            if(keepAWord)
            { 
                do
                {
                    char ch = charBufferRange[i++];
                    if (ch == TextStore.CharLineFeed || ch == TextStore.CharCarriageReturn) 
                    {
                        --i; 
                        break; 
                    }
                    else 
                    {
                        int charClass = (int)Classification.GetUnicodeClassUTF16(ch);
                        charFlags = Classification.CharAttributeOf(charClass).Flags;
                        charFastTextCheck &= charFlags; 

                        glyph = glyphIndices[i-1]; 
                        if (!ignoreWidths) 
                        {
                            totalWidth += TextFormatterImp.RoundDip(glyphMetrics[i - 1].AdvanceWidth * designToEm, textFormattingMode) * scalingFactor; 
                        }
                    }
                } while(
                        i < charBufferRange.Length 
                    &&  ((charFlags & charFlagsMask) == 0)
                    &&  (glyph != 0 || symbolTypeface) 
                    &&  glyph != blankGlyph 
                    );
 
                // i is now at a character immediately following a leading blank
            }

            // 
            // This block will advance until one of:
            // 1. The end of the charBufferRange is reached 
            // 2. The charFlags have some of the charFlagsMask values 
            // 3. Glyph index is 0 (unless symbol font)
            // 4. totalWidth > widthMax 
            //

            while(
                    i < charBufferRange.Length 
                &&  (ignoreWidths || totalWidth <= widthMax)
                &&  ((charFlags & charFlagsMask) == 0) 
                &&  (glyph != 0 || symbolTypeface) 
                )
            { 
                char ch = charBufferRange[i++];
                if (ch == TextStore.CharLineFeed || ch == TextStore.CharCarriageReturn)
                {
                    --i; 
                    break;
                } 
                else 
                {
                    int charClass = (int)Classification.GetUnicodeClassUTF16(ch); 
                    charFlags = Classification.CharAttributeOf(charClass).Flags;
                    charFastTextCheck &= charFlags;

                    glyph = glyphIndices[i-1]; 
                    if (!ignoreWidths)
                    { 
                        totalWidth += TextFormatterImp.RoundDip(glyphMetrics[i - 1].AdvanceWidth * designToEm, textFormattingMode) * scalingFactor; 
                    }
                } 
            }

            BufferCache.ReleaseUShorts(glyphIndices);
            glyphIndices = null; 
            BufferCache.ReleaseGlyphMetrics(glyphMetrics);
            glyphMetrics = null; 
 
            if (symbolTypeface)
            { 
                // always optimize for non-Unicode font as we don't support shaping or typographic features;
                // we also don't fall back from non-Unicode fonts so we don't care if there are missing glyphs
                stringLengthFit = i;
                return true; 
            }
 
            if (glyph == 0) 
            {
                // character is not supported by the font 
                return false;
            }

            if ((charFlags & charFlagsMask) != 0) 
            {
                // complex character encountered, exclude it 
                Debug.Assert(i > 0); 

                if(--i <= 0) 
                {
                    // first char is complex, fail the call
                    return false;
                } 
            }
 
            stringLengthFit = i; 
            TypographyAvailabilities typography = glyphTypeface.FontFaceLayoutInfo.TypographyAvailabilities;
 
            if ((charFastTextCheck & (byte) CharacterAttributeFlags.CharacterFastText) != 0)
            {
                // all input code points are Fast Text
                if ((typography & 
                         (  TypographyAvailabilities.FastTextTypographyAvailable
                          | TypographyAvailabilities.FastTextMajorLanguageLocalizedFormAvailable 
                         ) 
                     ) != 0
                   ) 
                {
                    // Considered too risky to optimize. It is either because the font
                    // has required features or the font has 'locl' lookup for major languages.
                    return false; 
                }
                else if ((typography & TypographyAvailabilities.FastTextExtraLanguageLocalizedFormAvailable) != 0) 
                { 
                    // The font has 'locl' lookup for FastText code points for non major languages.
                    // Check whether the input is major langauge. If it is, we are still good to optimize. 
                    return MajorLanguages.Contains(cultureInfo);
                }
                else
                { 
                    // No FastText flags are present, safe to optimize
                    return true; 
                } 
            }
            else if ((charFastTextCheck & (byte) CharacterAttributeFlags.CharacterIdeo) != 0) 
            {
                // The input are all ideographs, check the IdeoTypographyAvailable bit. It is safe if
                // the bit is not set.
                return ((typography & TypographyAvailabilities.IdeoTypographyAvailable) == 0); 
            }
            else 
            { 
                // for all the rest of the cases, just check whether there is any required typography
                // present at all. If none exists, it is optimizable. We might under-optimize here but 
                // it will be non-major languages.
                return ((typography & TypographyAvailabilities.Available) == 0);
            }
        } 
Пример #38
0
        private FontFamilyMap GetTargetFamilyMap(
            CharacterBufferRange unicodeString,
            CultureInfo culture,
            CultureInfo digitCulture,
            out int cchAdvance
            )
        {
            DigitMap digitMap = new DigitMap(digitCulture);
            ushort[] familyMaps = _fontInfo.GetFamilyMapsOfLanguage(XmlLanguage.GetLanguage(culture.IetfLanguageTag));

            int sizeofChar = 0;
            int ch = 0;

            // skip all the leading joinder characters. They need to be shaped with the 
            // surrounding strong characters.
            cchAdvance = Classification.AdvanceWhile(unicodeString, ItemClass.JoinerClass);
            
            if (cchAdvance >= unicodeString.Length)            
            {
                // It is rare that the run only contains joiner characters.
                // If it really happens, just map them to the initial family map. 
                return _fontInfo.GetFamilyMapOfChar(
                    familyMaps, 
                    Classification.UnicodeScalar(unicodeString, out sizeofChar)
                    );
            }

            //
            // If the run starts with combining marks, we will not be able to find base characters for them
            // within the run. These combining marks will be mapped to their best fonts as normal characters.
            //            
            ch = Classification.UnicodeScalar(
                new CharacterBufferRange(unicodeString, cchAdvance, unicodeString.Length - cchAdvance),
                out sizeofChar
                );
                
            bool hasBaseChar = !Classification.IsCombining(ch);
                        
            ch = digitMap[ch];            
            FontFamilyMap familyMap = _fontInfo.GetFamilyMapOfChar(familyMaps, ch);
                
            Invariant.Assert(familyMap != null);
            
            for (cchAdvance += sizeofChar; cchAdvance < unicodeString.Length; cchAdvance += sizeofChar)
            {
                ch = Classification.UnicodeScalar(
                        new CharacterBufferRange(unicodeString, cchAdvance, unicodeString.Length - cchAdvance),
                        out sizeofChar
                     );

                if (Classification.IsJoiner(ch))
                    continue; // continue to advance if current char is a joiner

                if (!Classification.IsCombining(ch))
                {
                    hasBaseChar = true; 
                }
                else if (hasBaseChar)
                {
                    continue; // continue to advance for combining mark with base char
                }

                ch = digitMap[ch];

                if (_fontInfo.GetFamilyMapOfChar(familyMaps, ch) != familyMap)
                    break;
            }

            return familyMap;
        }
Пример #39
0
        private void MapItem( 
            CharacterBufferRange unicodeString,
            CultureInfo          culture,
            Span                 itemSpan,
            ref SpanVector<int>  cachedScaledTypefaceIndexSpans, 
            int                  ichItem
            ) 
        { 
            CultureInfo digitCulture = ((MS.Internal.Text.TextInterface.ItemProps)itemSpan.element).DigitCulture;
 
            bool isCached = GetCachedScaledTypefaceMap(
                unicodeString,
                culture,
                digitCulture, 
                ref cachedScaledTypefaceIndexSpans,
                ichItem 
                ); 

            if(!isCached) 
            {
                // shapeable typeface to shape each character in the item has not been located,
                // look thru information in font family searching for the right shapeable typeface.
 
                SpanVector scaledTypefaceSpans = new SpanVector(null);
                int nextValid; 
 
                // we haven't yet found a valid physical font family
                PhysicalFontFamily firstValidFamily = null; 
                int firstValidLength = 0;

                if (!_nullFont)
                { 
                    MapByFontFamilyList(
                        unicodeString, 
                        culture, 
                        digitCulture,
                        _fontFamilies, 
                        ref firstValidFamily,
                        ref firstValidLength,
                        null,   // device font
                        1.0,    // default size is one em 
                        0,      // recursion depth
                        scaledTypefaceSpans, 
                        0,      // firstCharIndex 
                        out nextValid
                        ); 
                }
                else
                {
                    MapUnresolvedCharacters( 
                        unicodeString,
                        culture, 
                        digitCulture, 
                        firstValidFamily,
                        ref firstValidLength, 
                        scaledTypefaceSpans,
                        0,       // firstCharIndex
                        out nextValid
                        ); 
                }
 
                CacheScaledTypefaceMap( 
                    unicodeString,
                    culture, 
                    digitCulture,
                    scaledTypefaceSpans,
                    ref cachedScaledTypefaceIndexSpans,
                    ichItem 
                    );
            } 
        } 
Пример #40
0
        /// <summary>
        /// Map characters by font face family 
        /// </summary> 
        private int MapByFontFaceFamily(
            CharacterBufferRange    unicodeString, 
            CultureInfo             culture,
            CultureInfo             digitCulture,
            IFontFamily             fontFamily,
            FontStyle               canonicalStyle, 
            FontWeight              canonicalWeight,
            FontStretch             canonicalStretch, 
            ref PhysicalFontFamily  firstValidFamily, 
            ref int                 firstValidLength,
            IDeviceFont             deviceFont, 
            bool                    nullFont,
            double                  scaleInEm,
            SpanVector              scaledTypefaceSpans,
            int                     firstCharIndex, 
            bool                    ignoreMissing,
            out int                 nextValid 
            ) 
        {
            Invariant.Assert(fontFamily != null); 

            PhysicalFontFamily fontFaceFamily = fontFamily as PhysicalFontFamily;
            Invariant.Assert(fontFaceFamily != null);
 
            int advance = unicodeString.Length;
            nextValid = 0; 
 
            GlyphTypeface glyphTypeface = null;
 
            if(ignoreMissing)
            {
                glyphTypeface = fontFaceFamily.GetGlyphTypeface(canonicalStyle, canonicalWeight, canonicalStretch);
            } 
            else if(nullFont)
            { 
                glyphTypeface = fontFaceFamily.GetGlyphTypeface(canonicalStyle, canonicalWeight, canonicalStretch); 

                advance = 0; // by definition, null font always yields missing glyphs for whatever codepoint 
                nextValid = unicodeString.Length;
            }
            else
            { 
                glyphTypeface = fontFaceFamily.MapGlyphTypeface(
                    canonicalStyle, 
                    canonicalWeight, 
                    canonicalStretch,
                    unicodeString, 
                    digitCulture,
                    ref advance,
                    ref nextValid
                    ); 
            }
 
            Invariant.Assert(glyphTypeface != null); 

            int cch = unicodeString.Length; 
            if(!ignoreMissing && advance > 0)
            {
                cch = advance;
            } 

            // Do we need to set firstValidFamily? 
            if (firstValidLength <= 0) 
            {
                // Either firstValidFamily hasn't been set, or has "expired" (see below). The first valid 
                // family is the first existing physical font in the font linking chain. We want to remember
                // it so we can use it to map any unresolved characters.
                firstValidFamily = fontFaceFamily;
 
                // Set the "expiration date" for firstValidFamily. We know that this is the first physical
                // font for the specified character range, but after that family map lookup may result in 
                // a different first physical family. 
                firstValidLength = unicodeString.Length;
            } 

            // Each time we advance we near the expiration date for firstValidFamily.
            firstValidLength -= advance;
 

            Debug.Assert(cch > 0); 
            scaledTypefaceSpans.SetValue( 
                firstCharIndex,
                cch, 
                new ScaledShapeTypeface(
                    glyphTypeface,
                    deviceFont,
                    scaleInEm, 
                    nullFont
                    ) 
                ); 

            return advance; 
        }
Пример #41
0
        /// <summary>
        /// Maps characters to one of the font families in the specified FontFamilyList. This 
        /// function differs from MapByFontFamilyList in that it returns as soon as at least 
        /// one character is mapped; it does not keep going until it cannot map any more text.
        /// </summary> 
        private int MapOnceByFontFamilyList(
            CharacterBufferRange                unicodeString,
            CultureInfo                         culture,
            CultureInfo                         digitCulture, 
            FontFamily[]                        familyList,
            ref PhysicalFontFamily              firstValidFamily, 
            ref int                             firstValidLength, 
            IDeviceFont                         deviceFont,
            double                              scaleInEm, 
            int                                 recursionDepth,
            SpanVector                          scaledTypefaceSpans,
            int                                 firstCharIndex,
            out int                             nextValid 
            )
        { 
            Invariant.Assert(familyList != null); 

            int advance = 0; 
            nextValid = 0;
            CharacterBufferRange mapString = unicodeString;
            FontStyle canonicalStyle = _canonicalStyle;
            FontWeight canonicalWeight = _canonicalWeight; 
            FontStretch canonicalStretch = _canonicalStretch;
 
            // Note: FontFamilyIdentifier limits the number of family names in a single string. We 
            // don't want to also limit the number of iterations here because if Typeface.FontFamily
            // has the maximum number of tokens, this should not prevent us from falling back to the 
            // FallbackFontFamily (PS # 1148305).

            // Outer loop to loop over the list of FontFamily.
            for (int i = 0; i < familyList.Length; i++) 
            {
                // grab the font family identifier and initialize the 
                // target family based on whether it is a named font. 
                FontFamilyIdentifier fontFamilyIdentifier = familyList[i].FamilyIdentifier;
 
                CanonicalFontFamilyReference canonicalFamilyReference = null;
                IFontFamily targetFamily;

                if (fontFamilyIdentifier.Count != 0) 
                {
                    // Look up font family and face, in the case of multiple canonical families the weight/style/stretch 
                    // may not match the typeface map's, since it is created w/ the first canonical family. 
                    canonicalFamilyReference = fontFamilyIdentifier[0];
                    targetFamily = FontFamily.LookupFontFamilyAndFace(canonicalFamilyReference, ref canonicalStyle, ref canonicalWeight, ref canonicalStretch); 
                }
                else
                {
                    targetFamily = familyList[i].FirstFontFamily; 
                }
 
                int familyNameIndex = 0; 

                // Inner loop to loop over all name tokens of a FontFamily. 
                for (;;)
                {
                    if (targetFamily != null)
                    { 
                        advance = MapByFontFamily(
                            mapString, 
                            culture, 
                            digitCulture,
                            targetFamily, 
                            canonicalFamilyReference,
                            canonicalStyle,
                            canonicalWeight,
                            canonicalStretch, 
                            ref firstValidFamily,
                            ref firstValidLength, 
                            deviceFont, 
                            scaleInEm,
                            recursionDepth, 
                            scaledTypefaceSpans,
                            firstCharIndex,
                            out nextValid
                            ); 

                        if (nextValid < mapString.Length) 
                        { 
                            // only strings before the smallest invalid needs to be mapped since
                            // string beyond smallest invalid can already be mapped to a higher priority font. 
                            mapString = new CharacterBufferRange(
                                unicodeString.CharacterBuffer,
                                unicodeString.OffsetToFirstChar,
                                nextValid 
                                );
                        } 
 
                        if (advance > 0)
                        { 
                            // found the family that shapes this string. We terminate both the
                            // inner and outer loops.
                            i = familyList.Length;
                            break; 
                        }
                    } 
                    else 
                    {
                        // By definition null target does not map any of the input. 
                        nextValid = mapString.Length;
                    }

                    if (++familyNameIndex < fontFamilyIdentifier.Count) 
                    {
                        // Get the next canonical family name and target family. 
                        canonicalFamilyReference = fontFamilyIdentifier[familyNameIndex]; 
                        targetFamily = FontFamily.LookupFontFamilyAndFace(canonicalFamilyReference, ref canonicalStyle, ref canonicalWeight, ref canonicalStretch);
                    } 
                    else
                    {
                        // Unnamed FontFamily or no more family names in this FontFamily.
                        break; 
                    }
                } 
            } 

            nextValid = mapString.Length; 
            return advance;
        }
Пример #42
0
 /// <summary>
 /// Construct an lsrun 
 /// </summary>
 private LSRun(
     TextRunInfo             runInfo,
     IList<TextEffect>       textEffects, 
     Plsrun                  type,
     int                     offsetToFirstCp, 
     int                     textRunLength, 
     int                     emSize,
     ushort                  charFlags, 
     CharacterBufferRange    charBufferRange,
     int                     baselineOffset,
     int                     height,
     TextShapeableSymbols    shapeable, 
     byte                    bidiLevel
     ) 
 { 
     _runInfo = runInfo;
     _type = type; 
     _offsetToFirstCp = offsetToFirstCp;
     _textRunLength = textRunLength;
     _emSize = emSize;
     _charFlags = charFlags; 
     _charBufferRange = charBufferRange;
     _baselineOffset = baselineOffset; 
     _height = height; 
     _bidiLevel = bidiLevel;
     _shapeable = shapeable; 
     _textEffects = textEffects;
 }
Пример #43
0
        /// <summary>
        /// Get text immediately before specified text source position.
        /// </summary>
        public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int dcp)
        {
            CharacterBufferRange precedingText = CharacterBufferRange.Empty;
            CultureInfo culture = null;

            if (dcp > 0)
            {
                // Create TextPointer at dcp 
                ITextPointer position = _owner.Host.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward);

                // Return text in run. If it is at start of TextContainer this will return an empty string.
                // Typically the caller requires just the preceding character.  Worst case is the entire
                // preceding sentence, which we approximate with a 128 char limit.
                int runLength = Math.Min(128, position.GetTextRunLength(LogicalDirection.Backward));
                char []text = new char[runLength];
                position.GetTextInRun(LogicalDirection.Backward, text, 0, runLength);

                precedingText = new CharacterBufferRange(text, 0, runLength);
                culture = DynamicPropertyReader.GetCultureInfo((Control)_owner.Host);
            }

            return new TextSpan<CultureSpecificCharacterBufferRange>(
                precedingText.Length, new CultureSpecificCharacterBufferRange(culture, precedingText));
        }
Пример #44
0
            /// <summary>
            /// TextFormatter to get text immediately before specified text source position.
            /// </summary>
            /// <param name="textSourceCharacterIndexLimit">character index to specify where in the source text the text retrieval stops.</param>
            /// <returns>character string immediately before the specify text source character index.</returns>
            public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(
                int         textSourceCharacterIndexLimit
                )
            {
                CharacterBufferRange charString = CharacterBufferRange.Empty;
                CultureInfo culture = null;
            
                if (textSourceCharacterIndexLimit > 0)                    
                {
                    SpanRider thatFormatRider = new SpanRider(
                        _that._formatRuns,
                        _that._latestPosition,
                        textSourceCharacterIndexLimit - 1
                        );
                    
                    charString = new CharacterBufferRange(
                        new CharacterBufferReference(_that._text, thatFormatRider.CurrentSpanStart),
                        textSourceCharacterIndexLimit - thatFormatRider.CurrentSpanStart
                        );

                    culture = ((TextRunProperties)thatFormatRider.CurrentElement).CultureInfo;
                }

                return new TextSpan<CultureSpecificCharacterBufferRange> (
                    charString.Length,
                    new CultureSpecificCharacterBufferRange(culture, charString)
                    );
            }
Пример #45
0
        /// <summary>
        /// Add shapeable text object to the list
        /// </summary>
        void IShapeableTextCollector.Add(
            IList<TextShapeableSymbols>  shapeables,
            CharacterBufferRange         characterBufferRange,
            TextRunProperties            textRunProperties,
            MS.Internal.Text.TextInterface.ItemProps textItem,
            ShapeTypeface                shapeTypeface,
            double                       emScale,
            bool                         nullShape,
            TextFormattingMode               textFormattingMode
            )
        {
            Debug.Assert(shapeables != null);

            shapeables.Add(
                new TextShapeableCharacters(
                    characterBufferRange,
                    textRunProperties,
                    textRunProperties.FontRenderingEmSize * emScale,
                    textItem,
                    shapeTypeface,
                    nullShape,
                    textFormattingMode,
                    false
                    )
                );
        }
Пример #46
0
        /// <summary>
        /// Get family name correspondent to the first n-characters of the specified character string
        /// </summary>
        bool IFontFamily.GetMapTargetFamilyNameAndScale(
            CharacterBufferRange unicodeString,
            CultureInfo culture,
            CultureInfo digitCulture,
            double defaultSizeInEm,
            out int cchAdvance,
            out string targetFamilyName,
            out double scaleInEm
            )
        {
            Invariant.Assert(unicodeString.CharacterBuffer != null && unicodeString.Length > 0);
            Invariant.Assert(culture != null);

            // Get the family map. This will find the first family map that matches
            // the specified culture, an ancestor neutral culture, or "any" culture.
            FontFamilyMap familyMap = GetTargetFamilyMap(
                unicodeString,
                culture,
                digitCulture,
                out cchAdvance
                );

            // Return the values for the matching FontFamilyMap. If there is none this is
            // FontFamilyMap.Default which has Target == null and Scale == 1.0.
            targetFamilyName = familyMap.Target;
            scaleInEm = familyMap.Scale;

            return true;
        }
Пример #47
0
        /// <summary>
        /// Get text immediately preceding cpLimit.
        /// </summary>
        internal TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(TextSource textSource, int cpLimit) 
        {
            if (cpLimit > 0) 
            { 
                SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition);
                if (textRunSpanRider.At(cpLimit - 1)) 
                {
                    CharacterBufferRange charString = CharacterBufferRange.Empty;
                    CultureInfo culture = null;
 
                    TextRun run = textRunSpanRider.CurrentElement as TextRun;
 
                    if (run != null) 
                    {
                        // Only TextRun containing text would have non-empty Character buffer range. 
                        if ( TextRunInfo.GetRunType(run) == Plsrun.Text
                          && run.CharacterBufferReference.CharacterBuffer != null)
                        {
                            charString = new CharacterBufferRange( 
                                run.CharacterBufferReference,
                                cpLimit - textRunSpanRider.CurrentSpanStart); 
 
                            culture = CultureMapper.GetSpecificCulture(run.Properties.CultureInfo);
                        } 

                        return new TextSpan<CultureSpecificCharacterBufferRange>(
                            cpLimit - textRunSpanRider.CurrentSpanStart, // cp length
                            new CultureSpecificCharacterBufferRange(culture, charString) 
                         );
                    } 
                } 
            }
 
            // not in cache so call back to client
            return textSource.GetPrecedingText(cpLimit);
        }
Пример #48
0
        static public int AdvanceWhile(
            CharacterBufferRange unicodeString, 
            ItemClass            itemClass
            )
        {
            int i     = 0; 
            int limit = unicodeString.Length;
            int sizeofChar = 0; 
 
            while (i < limit)
            { 
                int ch = Classification.UnicodeScalar(
                    new CharacterBufferRange(unicodeString, i, limit - i),
                    out sizeofChar
                    ); 

                unsafe 
                { 
                    byte currentClass = (byte) Classification.CharAttributeTable[(int)GetUnicodeClass(ch)].ItemClass;
                    if (currentClass != (byte) itemClass) 
                        break;
                }

                i += sizeofChar; 
            }
 
            return i; 
        }
Пример #49
0
        /// <summary> 
        /// Compute Unicode scalar value from unicode codepoint stream 
        /// </summary>
        static internal int UnicodeScalar( 
            CharacterBufferRange unicodeString,
            out int              sizeofChar
            )
        { 
            Invariant.Assert(unicodeString.CharacterBuffer != null && unicodeString.Length > 0);
 
            int ch = unicodeString[0]; 
            sizeofChar = 1;
 
            if (    unicodeString.Length >= 2
                &&  (ch & 0xFC00) == 0xD800
                &&  (unicodeString[1] & 0xFC00) == 0xDC00
                ) 
            {
                ch = (((ch & 0x03FF) << 10) | (unicodeString[1] & 0x3FF)) + 0x10000; 
                sizeofChar++; 
            }
 
            return ch;
        }
Пример #50
0
        /// <summary> 
        /// Cache index to the list of scaled shapeable typeface 
        /// </summary>
        private void CacheScaledTypefaceMap( 
            CharacterBufferRange        unicodeString,
            CultureInfo                 culture,
            CultureInfo                 digitCulture,
            SpanVector                  scaledTypefaceSpans, 
            ref SpanVector<int>         cachedScaledTypefaceIndexSpans,
            int                         ichItem 
            ) 
        {
 
            IntMap map;
            if (!_intMaps.TryGetValue(culture, out map))
            {
                map = new IntMap(); 
                _intMaps.Add(culture, map);
            } 
 
            DigitMap digitMap = new DigitMap(digitCulture);
 
            SpanRider typefaceSpanRider = new SpanRider(scaledTypefaceSpans);

            int ich = 0;
            while(ich < unicodeString.Length) 
            {
                typefaceSpanRider.At(ich); 
 
                int cch = Math.Min(unicodeString.Length - ich, typefaceSpanRider.Length);
 
                int index = IndexOfScaledTypeface((ScaledShapeTypeface)typefaceSpanRider.CurrentElement);
                Debug.Assert(index >= 0, "Invalid scaled shapeable typeface index spans");

                cachedScaledTypefaceIndexSpans.Set(ichItem + ich, cch, index); 

                // we keep index + 1 in the map, so that we leave map entry zero 
                // to indicate uninitialized entry. 
                index++;
 
                int sizeofChar;
                for (int c = 0; c < cch; c += sizeofChar)
                {
                    int ch = digitMap[ 
                        Classification.UnicodeScalar(
                            new CharacterBufferRange(unicodeString, ich + c, unicodeString.Length - ich - c), 
                            out sizeofChar 
                        )
                    ]; 

                    // only cache typeface map index for base characters
                    if(!Classification.IsCombining(ch) && !Classification.IsJoiner(ch))
                    { 
                        // Dump values of local variables when the condition fails for better debuggability.
                        // We use "if" to avoid the expensive string.Format() in normal case. 
                        if (map[ch] != 0 && map[ch] != index) 
                        {
                            Invariant.Assert( 
                                false,
                                string.Format(
                                    CultureInfo.InvariantCulture,
                                    "shapeable cache stores conflicting info, ch = {0}, map[ch] = {1}, index = {2}", 
                                    ch, map[ch], index
                                ) 
                            ); 
                        }
 
                        map[ch] = (ushort)index;
                    }
                }
 
                ich += cch;
            } 
        } 
        internal sealed override unsafe void GetAdvanceWidthsUnshaped(
            char*         characterString,
            int           characterLength,
            double        scalingFactor,
            int*          advanceWidthsUnshaped
            )
        {
            if (!IsShapingRequired)
            {
                if (    (_shapeTypeface            != null)
                    &&  (_shapeTypeface.DeviceFont != null))
                {
                    // Use device font to compute advance widths
                    _shapeTypeface.DeviceFont.GetAdvanceWidths(
                        characterString,
                        characterLength,
                        _emSize * scalingFactor,
                        advanceWidthsUnshaped
                    );
                }
                else
                {
                    bool nullFont;
                    GlyphTypeface glyphTypeface = GetGlyphTypeface(out nullFont);
                    Invariant.Assert(glyphTypeface != null);

                    glyphTypeface.GetAdvanceWidthsUnshaped(
                        characterString,
                        characterLength,
                        _emSize,
                        scalingFactor,
                        advanceWidthsUnshaped,
                        nullFont,
                        _textFormattingMode,
                        _isSideways
                        );
                }
            }
            else
            {
                GlyphTypeface glyphTypeface = _shapeTypeface.GlyphTypeface;

                Invariant.Assert(glyphTypeface != null);
                Invariant.Assert(characterLength > 0);

                CharacterBufferRange newBuffer = new CharacterBufferRange(characterString, characterLength);
                MS.Internal.Text.TextInterface.GlyphMetrics[] glyphMetrics = BufferCache.GetGlyphMetrics(characterLength);

                glyphTypeface.GetGlyphMetricsOptimized(newBuffer,
                                                       _emSize,
                                                       _textFormattingMode,
                                                       _isSideways,
                                                       glyphMetrics
                                                       );

                double designToEm = _emSize * scalingFactor / glyphTypeface.DesignEmHeight;

                for (int i = 0; i < characterLength; i++)
                {
                    advanceWidthsUnshaped[i] = (int)Math.Round(glyphMetrics[i].AdvanceWidth * designToEm);
                }

                BufferCache.ReleaseGlyphMetrics(glyphMetrics);
            }
        }
Пример #52
0
        /// <summary>
        /// Map characters by font family 
        /// </summary>
        /// <remarks>
        /// Advance:
        ///     number of characters not mapped to missing glyph 
        ///
        /// NextValid: 
        ///     Offset to the nearest first character not mapped to missing glyph 
        ///
        /// [Number of invalid characters following valid ones] = NextValid - Advance 
        ///
        ///         A B C D E F G H x x x x x F G H I J
        ///         --------------->
        ///             Advance 
        ///
        ///         -------------------------> 
        ///                NextValid 
        ///
        /// </remarks> 
        private int MapByFontFamily(
            CharacterBufferRange            unicodeString,
            CultureInfo                     culture,
            CultureInfo                     digitCulture, 
            IFontFamily                     fontFamily,
            CanonicalFontFamilyReference    canonicalFamilyReference, 
            FontStyle                       canonicalStyle, 
            FontWeight                      canonicalWeight,
            FontStretch                     canonicalStretch, 
            ref PhysicalFontFamily          firstValidFamily,
            ref int                         firstValidLength,
            IDeviceFont                     deviceFont,
            double                          scaleInEm, 
            int                             recursionDepth,
            SpanVector                      scaledTypefaceSpans, 
            int                             firstCharIndex, 
            out int                         nextValid
            ) 
        {
            // This is the *one* place where we check for the font mapping depths of the font linking
            // process. This protects the linking process against extremely long chain of linking or
            // circular dependencies in the composite fonts. 
            if (recursionDepth >= MaxTypefaceMapDepths)
            { 
                // Stop the recursion. In effect, this FontFamily does not map any of the input. 
                // Higher-level code must map the input text to some other FontFamily, or to the
                // "null font" if there is no valid FontFamily. 
                nextValid = 0;
                return 0;
            }
 
            // If a device font is not already specified higher up the stack, look for a device font
            // for this font family that matches the typeface style, weight, and stretch. 
            if (deviceFont == null) 
            {
                deviceFont = fontFamily.GetDeviceFont(_canonicalStyle, _canonicalWeight, _canonicalStretch); 
            }

            DigitMap digitMap = new DigitMap(digitCulture);
 
            int advance = 0;
            int cchAdvance; 
            int cchNextValid; 
            int ich = 0;
 
            nextValid = 0;

            bool terminated = false;
 
            while (ich < unicodeString.Length  &&  !terminated)
            { 
                // Determine length of run with consistent mapping. Start by assuming we'll be able to 
                // use the whole string, then reduce to the length that can be mapped consistently.
                int cchMap = unicodeString.Length - ich; 

                // Determine whether the run is using a device font, and limit the run to the
                // first boundary between device/non-device font usage.
                bool useDeviceFont = false; 
                if (deviceFont != null)
                { 
                    // Determine whether the first run uses a device font by inspecting the first character. 
                    // We do not support device fonts for codepoints >= U+10000 (aka surrogates), so we
                    // don't need to call Classification.UnicodeScalar. 
                    useDeviceFont = deviceFont.ContainsCharacter(digitMap[unicodeString[ich]]);

                    // Advance as long as 'useDeviceFont' remains unchanged.
                    int i = ich + 1; 
                    while (    (i < unicodeString.Length)
                           &&  (useDeviceFont == deviceFont.ContainsCharacter(digitMap[unicodeString[i]]))) 
                    { 
                        i++;
                    } 

                    cchMap = i - ich;
                }
 

                // Map as many characters to a family as we can up to the limit (cchMap) just determined. 
                string targetFamilyName; 
                double mapSizeInEm;
 
                bool isCompositeFontFamily = fontFamily.GetMapTargetFamilyNameAndScale(
                    new CharacterBufferRange(
                        unicodeString,
                        ich, 
                        cchMap
                        ), 
                    culture, 
                    digitCulture,
                    scaleInEm, 
                    out cchMap,
                    out targetFamilyName,
                    out mapSizeInEm
                    ); 

                Debug.Assert(cchMap <= unicodeString.Length - ich); 
 
                CharacterBufferRange mappedString = new CharacterBufferRange(
                    unicodeString, 
                    ich,
                    cchMap
                    );
 

                if (!isCompositeFontFamily) 
                { 
                    // not a composite font family
                    cchAdvance = MapByFontFaceFamily( 
                        mappedString,
                        culture,
                        digitCulture,
                        fontFamily, 
                        canonicalStyle,
                        canonicalWeight, 
                        canonicalStretch, 
                        ref firstValidFamily,
                        ref firstValidLength, 
                        useDeviceFont ? deviceFont : null,
                        false, // nullFont
                        mapSizeInEm,
                        scaledTypefaceSpans, 
                        firstCharIndex + ich,
                        false, // ignoreMissing 
                        out cchNextValid 
                        );
                } 
                else if (!string.IsNullOrEmpty(targetFamilyName))
                {
                    // The base Uri used for resolving target family names is the Uri of the composite font.
                    Uri baseUri = (canonicalFamilyReference != null) ? canonicalFamilyReference.LocationUri : null; 

                    // map to the target of the family map 
                    cchAdvance = MapByFontFamilyName( 
                        mappedString,
                        culture, 
                        digitCulture,
                        targetFamilyName,
                        baseUri,
                        ref firstValidFamily, 
                        ref firstValidLength,
                        useDeviceFont ? deviceFont : null, 
                        mapSizeInEm, 
                        recursionDepth + 1, // increment the depth
                        scaledTypefaceSpans, 
                        firstCharIndex + ich,
                        out cchNextValid
                        );
                } 
                else
                { 
                    // family map lookup returned no target family 
                    cchAdvance = 0;
                    cchNextValid = cchMap; 
                }

                int cchValid = cchMap;
                int cchInvalid = 0; 

                cchValid = cchAdvance; 
                cchInvalid = cchNextValid; 

                if(cchValid < cchMap) 
                {
                    terminated = true;
                }
 
                advance += cchValid;
                nextValid = ich + cchInvalid; 
 
                ich += cchValid;
            } 

            return advance;
        }
 /// <summary>
 /// Construct a CultureSpecificCharacterBufferRange class
 /// </summary>
 public CultureSpecificCharacterBufferRange(CultureInfo culture, CharacterBufferRange characterBufferRange)
 {        
     _culture = culture;
     _characterBufferRange = characterBufferRange;
 }
Пример #54
0
        /// <summary> 
        /// Maps characters that could not be resolved to any font family either to the first 
        /// valid physical font family or to the default font we use for display null glyphs.
        /// </summary> 
        private int MapUnresolvedCharacters(
            CharacterBufferRange    unicodeString,
            CultureInfo             culture,
            CultureInfo             digitCulture, 
            PhysicalFontFamily      firstValidFamily,
            ref int                 firstValidLength, 
            SpanVector              scaledTypefaceSpans, 
            int                     firstCharIndex,
            out int                 nextValid 
            )
        {
            // If we have a valid font family use it. We don't set nullFont to true in this case.
            // We may end up displaying missing glyphs, but we don't need to force it. 
            IFontFamily fontFamily = firstValidFamily;
            bool nullFont = false; 
 
            if (firstValidLength <= 0)
            { 
                // We didn't find any valid physical font family so use the default "Arial", and
                // set nullFont to true to ensure that we always display missing glyphs.
                fontFamily = FontFamily.LookupFontFamily(FontFamily.NullFontFamilyCanonicalName);
                Invariant.Assert(fontFamily != null); 
                nullFont = true;
            } 
 
            return MapByFontFaceFamily(
                unicodeString, 
                culture,
                digitCulture,
                fontFamily,
                _canonicalStyle, 
                _canonicalWeight,
                _canonicalStretch, 
                ref firstValidFamily, 
                ref firstValidLength,
                null, // device font 
                nullFont,
                1.0,
                scaledTypefaceSpans,
                firstCharIndex, 
                true, // ignore missing
                out nextValid 
                ); 
        }
Пример #55
0
        /// <summary>
        /// Fetch cached textrun 
        /// </summary>
        internal TextRun FetchTextRun( 
            FormatSettings          settings, 
            int                     cpFetch,
            int                     cpFirst, 
            out int                 offsetToFirstCp,
            out int                 runLength
            )
        { 
            SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition, cpFetch);
            _latestPosition = textRunSpanRider.SpanPosition; 
            TextRun textRun = (TextRun)textRunSpanRider.CurrentElement; 

            if(textRun == null) 
            {
                // run not already cached, fetch new run and cache it

                textRun = settings.TextSource.GetTextRun(cpFetch); 

                if (textRun.Length < 1) 
                { 
                    throw new ArgumentOutOfRangeException("textRun.Length", SR.Get(SRID.ParameterMustBeGreaterThanZero));
                } 

                Plsrun plsrun = TextRunInfo.GetRunType(textRun);

                if (plsrun == Plsrun.Text || plsrun == Plsrun.InlineObject) 
                {
                    TextRunProperties properties = textRun.Properties; 
 
                    if (properties == null)
                        throw new ArgumentException(SR.Get(SRID.TextRunPropertiesCannotBeNull)); 

                    if (properties.FontRenderingEmSize <= 0)
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassMustBeGreaterThanZero, "FontRenderingEmSize", "TextRunProperties"));
 
                    double realMaxFontRenderingEmSize = Constants.RealInfiniteWidth / Constants.GreatestMutiplierOfEm;
 
                    if (properties.FontRenderingEmSize > realMaxFontRenderingEmSize) 
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeGreaterThan, "FontRenderingEmSize", "TextRunProperties", realMaxFontRenderingEmSize));
 
                    CultureInfo culture = CultureMapper.GetSpecificCulture(properties.CultureInfo);

                    if (culture == null)
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeNull, "CultureInfo", "TextRunProperties")); 

                    if (properties.Typeface == null) 
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeNull, "Typeface", "TextRunProperties")); 
                }
 

                //
                // TextRun is specifial to SpanVector because TextRun also encodes position which needs to be
                // consistent with the positions encoded by SpanVector. In run cache, the begining of a span 
                // should always correspond to the begining of a cached text run. If the end of the currently fetched
                // run overlaps with the begining of an already cached run, the begining of the cached run needs to be 
                // adjusted as well as its span. Because we can't gurantee the correctness of the overlapped range 
                // so we'll simply remove the overlapped runs here.
                // 

                // Move the rider to the end of the current run
                textRunSpanRider.At(cpFetch + textRun.Length - 1);
                _latestPosition = textRunSpanRider.SpanPosition; 

                if (textRunSpanRider.CurrentElement != _textRunVector.Default) 
                { 
                    // The end overlaps with one or more cached runs, clear the range from the
                    // begining of the current fetched run to the end of the last overlapped cached run. 
                    _latestPosition = _textRunVector.SetReference(
                        cpFetch,
                        textRunSpanRider.CurrentPosition + textRunSpanRider.Length - cpFetch,
                        _textRunVector.Default, 
                        _latestPosition
                        ); 
                } 

                _latestPosition = _textRunVector.SetReference(cpFetch, textRun.Length, textRun, _latestPosition); 

                // Refresh the rider's SpanPosition following previous SpanVector.SetReference calls
                textRunSpanRider.At(_latestPosition, cpFetch);
            } 

            offsetToFirstCp = textRunSpanRider.CurrentPosition - textRunSpanRider.CurrentSpanStart; 
            runLength = textRunSpanRider.Length; 
            Debug.Assert(textRun != null && runLength > 0, "Invalid run!");
 
            bool isText = textRun is ITextSymbols;

            if (isText)
            { 
                // Chop text run to optimal length so we dont spend forever analysing
                // them all at once. 
 
                int looseCharLength = TextStore.TypicalCharactersPerLine - cpFetch + cpFirst;
 
                if(looseCharLength <= 0)
                {
                    // this line already exceeds typical line length, incremental fetch goes
                    // about a quarter of the typical length. 

                    looseCharLength = (int)Math.Round(TextStore.TypicalCharactersPerLine * 0.25); 
                } 

                if(runLength > looseCharLength) 
                {
                    if (TextRunInfo.GetRunType(textRun) == Plsrun.Text)
                    {
                        // 
                        // When chopping the run at the typical line length,
                        // - don't chop in between of higher & lower surrogate 
                        // - don't chop combining mark away from its base character 
                        // - don't chop joiner from surrounding characters
                        // 
                        // Starting from the initial chopping point, we look ahead to find a safe position. We stop at
                        // a limit in case the run consists of many combining mark & joiner. That is rare and doesn't make
                        // much sense in shaping already.
                        // 

                        CharacterBufferReference charBufferRef = textRun.CharacterBufferReference; 
 
                        // We look ahead by one more line at most. It is not normal to have
                        // so many combining mark or joiner characters in a row. It doesn't make sense to 
                        // look further if so.
                        int lookAheadLimit = Math.Min(runLength, looseCharLength + TextStore.TypicalCharactersPerLine);

                        int sizeOfChar = 0; 
                        int endOffset  = 0;
                        bool canBreakAfterPrecedingChar = false; 
 
                        for (endOffset = looseCharLength - 1; endOffset < lookAheadLimit; endOffset += sizeOfChar)
                        { 
                            CharacterBufferRange charString = new CharacterBufferRange(
                                charBufferRef.CharacterBuffer,
                                charBufferRef.OffsetToFirstChar + offsetToFirstCp + endOffset,
                                runLength - endOffset 
                                );
 
                            int ch = Classification.UnicodeScalar(charString, out sizeOfChar); 

                            // We can only safely break if the preceding char is not a joiner character (i.e. can-break-after), 
                            // and the current char is not combining or joiner (i.e. can-break-before).
                            if (canBreakAfterPrecedingChar && !Classification.IsCombining(ch) && !Classification.IsJoiner(ch) )
                            {
                                break; 
                            }
 
                            canBreakAfterPrecedingChar = !Classification.IsJoiner(ch); 
                        }
 
                        looseCharLength = Math.Min(runLength, endOffset);
                    }

                    runLength = looseCharLength; 
                }
            } 
 

            Debug.Assert( 

                // valid run found
                runLength > 0
 
                // non-text run always fetched at run start
                &&  (   isText 
                    || textRunSpanRider.CurrentSpanStart - textRunSpanRider.CurrentPosition == 0) 

                // span rider of both text and format point to valid position 
                &&  (textRunSpanRider.Length > 0 && textRunSpanRider.CurrentElement != null),

                "Text run fetching error!"
                ); 

            return textRun; 
        } 
Пример #56
0
 /// <summary>
 /// Map characters by font family name
 /// </summary>
 private int MapByFontFamilyName( 
     CharacterBufferRange        unicodeString,
     CultureInfo                 culture, 
     CultureInfo                 digitCulture, 
     string                      familyName,
     Uri                         baseUri, 
     ref PhysicalFontFamily      firstValidFamily,
     ref int                     firstValidLength,
     IDeviceFont                 deviceFont,
     double                      scaleInEm, 
     int                         fontMappingDepth,
     SpanVector                  scaledTypefaceSpans, 
     int                         firstCharIndex, 
     out int                     nextValid
     ) 
 {
     if (familyName == null)
     {
         return MapUnresolvedCharacters( 
             unicodeString,
             culture, 
             digitCulture, 
             firstValidFamily,
             ref firstValidLength, 
             scaledTypefaceSpans,
             firstCharIndex,
             out nextValid
             ); 
     }
     else 
     { 
         // Map as many characters as we can to families in the list.
         return MapByFontFamilyList( 
             unicodeString,
             culture,
             digitCulture,
             new FontFamily[] { new FontFamily(baseUri, familyName) }, 
             ref firstValidFamily,
             ref firstValidLength, 
             deviceFont, 
             scaleInEm,
             fontMappingDepth, 
             scaledTypefaceSpans,
             firstCharIndex,
             out nextValid
             ); 
     }
 } 
Пример #57
0
        /// <summary>
        /// Scan through specified character string checking for valid character
        /// nominal glyph.
        /// </summary>
        /// <param name="charBufferRange">character buffer range</param>
        /// <param name="emSize">height of Em</param>
        /// <param name="scalingFactor">This is the factor by which we will scale up
        /// the metrics. Typically this value to used to convert metrics from the real
        /// space to the ideal space</param>
        /// <param name="widthMax">maximum width allowed</param>
        /// <param name="keepAWord">do not stop arbitrarily in the middle of a word</param>
        /// <param name="numberSubstitution">digits require complex shaping</param>
        /// <param name="cultureInfo">CultureInfo of the text</param>
        /// <param name="textFormattingMode">The TextFormattingMode used (Ideal vs. Display)</param>
        /// <param name="isSideways">Indicates whether to rotate glyphs.</param>
        /// <param name="breakOnTabs">Determines whether to stop checking at a tab and
        /// break the run there</param>
        /// <param name="stringLengthFit">number of character fit in given width</param>
        /// <returns>whether the specified string can be optimized by nominal glyph lookup</returns>
        internal bool CheckFastPathNominalGlyphs(
            CharacterBufferRange charBufferRange,
            double emSize,
            double scalingFactor,
            double widthMax,
            bool keepAWord,
            bool numberSubstitution,
            CultureInfo cultureInfo,
            TextFormattingMode textFormattingMode,
            bool isSideways,
            bool breakOnTabs,
            out int stringLengthFit
            )
        {
            stringLengthFit = 0;

            if (CachedTypeface.NullFont)
            {
                return(false);
            }

            GlyphTypeface glyphTypeface = TryGetGlyphTypeface();

            if (glyphTypeface == null)
            {
                return(false);
            }

            double totalWidth = 0;
            int    i          = 0;

            ushort blankGlyph = glyphTypeface.BlankGlyphIndex;
            ushort glyph      = blankGlyph;

            ushort charFlagsMask = numberSubstitution ?
                                   (ushort)(CharacterAttributeFlags.CharacterComplex | CharacterAttributeFlags.CharacterDigit) :
                                   (ushort)CharacterAttributeFlags.CharacterComplex;
            ushort charFlags         = 0;
            ushort charFastTextCheck = (ushort)(CharacterAttributeFlags.CharacterFastText | CharacterAttributeFlags.CharacterIdeo);

            bool symbolTypeface = glyphTypeface.Symbol;

            if (symbolTypeface)
            {
                // we don't care what code points are present if it's a non-Unicode font such as Symbol or Wingdings;
                // the code points don't have any standardized meanings, and we always want to bypass shaping
                charFlagsMask = 0;
            }

            bool ignoreWidths = widthMax == double.MaxValue;

            ushort[] glyphIndices = BufferCache.GetUShorts(charBufferRange.Length);
            MS.Internal.Text.TextInterface.GlyphMetrics[] glyphMetrics = ignoreWidths ? null : BufferCache.GetGlyphMetrics(charBufferRange.Length);

            glyphTypeface.GetGlyphMetricsOptimized(charBufferRange,
                                                   emSize,
                                                   glyphIndices,
                                                   glyphMetrics,
                                                   textFormattingMode,
                                                   isSideways
                                                   );

            double designToEm = emSize / glyphTypeface.DesignEmHeight;

            //
            // This block will advance until one of:
            // 1. The end of the charBufferRange is reached
            // 2. The charFlags have some of the charFlagsMask values
            // 3. The glyph is BlankGlyph (space)
            // 4. Glyph index is 0 (unless symbol font)
            //
            // At this point totalWidth includes all of the widths including the stop character (which fits above)
            // i indexes the next character (not included in the width)
            //
            if (keepAWord)
            {
                do
                {
                    char ch = charBufferRange[i++];
                    if (ch == TextStore.CharLineFeed || ch == TextStore.CharCarriageReturn || (breakOnTabs && ch == TextStore.CharTab))
                    {
                        --i;
                        break;
                    }
                    else
                    {
                        int charClass = (int)Classification.GetUnicodeClassUTF16(ch);
                        charFlags          = Classification.CharAttributeOf(charClass).Flags;
                        charFastTextCheck &= charFlags;

                        glyph = glyphIndices[i - 1];
                        if (!ignoreWidths)
                        {
                            totalWidth += TextFormatterImp.RoundDip(glyphMetrics[i - 1].AdvanceWidth * designToEm, textFormattingMode) * scalingFactor;
                        }
                    }
                } while(
                    i < charBufferRange.Length &&
                    ((charFlags & charFlagsMask) == 0) &&
                    (glyph != 0 || symbolTypeface) &&
                    glyph != blankGlyph
                    );

                // i is now at a character immediately following a leading blank
            }

            //
            // This block will advance until one of:
            // 1. The end of the charBufferRange is reached
            // 2. The charFlags have some of the charFlagsMask values
            // 3. Glyph index is 0 (unless symbol font)
            // 4. totalWidth > widthMax
            //

            while (
                i < charBufferRange.Length &&
                (ignoreWidths || totalWidth <= widthMax) &&
                ((charFlags & charFlagsMask) == 0) &&
                (glyph != 0 || symbolTypeface)
                )
            {
                char ch = charBufferRange[i++];
                if (ch == TextStore.CharLineFeed || ch == TextStore.CharCarriageReturn || (breakOnTabs && ch == TextStore.CharTab))
                {
                    --i;
                    break;
                }
                else
                {
                    int charClass = (int)Classification.GetUnicodeClassUTF16(ch);
                    charFlags          = Classification.CharAttributeOf(charClass).Flags;
                    charFastTextCheck &= charFlags;

                    glyph = glyphIndices[i - 1];
                    if (!ignoreWidths)
                    {
                        totalWidth += TextFormatterImp.RoundDip(glyphMetrics[i - 1].AdvanceWidth * designToEm, textFormattingMode) * scalingFactor;
                    }
                }
            }

            BufferCache.ReleaseUShorts(glyphIndices);
            glyphIndices = null;
            BufferCache.ReleaseGlyphMetrics(glyphMetrics);
            glyphMetrics = null;

            if (symbolTypeface)
            {
                // always optimize for non-Unicode font as we don't support shaping or typographic features;
                // we also don't fall back from non-Unicode fonts so we don't care if there are missing glyphs
                stringLengthFit = i;
                return(true);
            }

            if (glyph == 0)
            {
                // character is not supported by the font
                return(false);
            }

            if ((charFlags & charFlagsMask) != 0)
            {
                // complex character encountered, exclude it
                Debug.Assert(i > 0);

                if (--i <= 0)
                {
                    // first char is complex, fail the call
                    return(false);
                }
            }

            stringLengthFit = i;
            TypographyAvailabilities typography = glyphTypeface.FontFaceLayoutInfo.TypographyAvailabilities;

            if ((charFastTextCheck & (byte)CharacterAttributeFlags.CharacterFastText) != 0)
            {
                // all input code points are Fast Text
                if ((typography &
                     (TypographyAvailabilities.FastTextTypographyAvailable
                      | TypographyAvailabilities.FastTextMajorLanguageLocalizedFormAvailable
                     )
                     ) != 0
                    )
                {
                    // Considered too risky to optimize. It is either because the font
                    // has required features or the font has 'locl' lookup for major languages.
                    return(false);
                }
                else if ((typography & TypographyAvailabilities.FastTextExtraLanguageLocalizedFormAvailable) != 0)
                {
                    // The font has 'locl' lookup for FastText code points for non major languages.
                    // Check whether the input is major langauge. If it is, we are still good to optimize.
                    return(MajorLanguages.Contains(cultureInfo));
                }
                else
                {
                    // No FastText flags are present, safe to optimize
                    return(true);
                }
            }
            else if ((charFastTextCheck & (byte)CharacterAttributeFlags.CharacterIdeo) != 0)
            {
                // The input are all ideographs, check the IdeoTypographyAvailable bit. It is safe if
                // the bit is not set.
                return((typography & TypographyAvailabilities.IdeoTypographyAvailable) == 0);
            }
            else
            {
                // for all the rest of the cases, just check whether there is any required typography
                // present at all. If none exists, it is optimizable. We might under-optimize here but
                // it will be non-major languages.
                return((typography & TypographyAvailabilities.Available) == 0);
            }
        }
Пример #58
0
        /// <summary>
        /// Maps as may characters as it can (or *all* characters if recursionDepth == 0) to 
        /// font families in the specified FontFamilyList.
        /// </summary>
        private int MapByFontFamilyList(
            CharacterBufferRange    unicodeString, 
            CultureInfo             culture,
            CultureInfo             digitCulture, 
            FontFamily[]            familyList, 
            ref PhysicalFontFamily  firstValidFamily,
            ref int                 firstValidLength, 
            IDeviceFont             deviceFont,
            double                  scaleInEm,
            int                     recursionDepth,
            SpanVector              scaledTypefaceSpans, 
            int                     firstCharIndex,
            out int                 nextValid 
            ) 
        {
            int advance = 0; 
            int cchAdvance;
            int cchNextValid = 0;
            int ich = 0;
 
            nextValid = 0;
 
            while (ich < unicodeString.Length) 
            {
                cchAdvance = MapOnceByFontFamilyList( 
                    new CharacterBufferRange(
                        unicodeString,
                        ich,
                        unicodeString.Length - ich 
                        ),
                    culture, 
                    digitCulture, 
                    familyList,
                    ref firstValidFamily, 
                    ref firstValidLength,
                    deviceFont,
                    scaleInEm,
                    recursionDepth, 
                    scaledTypefaceSpans,
                    firstCharIndex + ich, 
                    out cchNextValid 
                    );
 
                if (cchAdvance <= 0)
                {
                    // We could not map any characters. If this is a recursive call then it's OK to
                    // exit the loop without mapping all the characters; the caller may be able to 
                    // map the text to some other font family.
                    if (recursionDepth > 0) 
                        break; 

                    Debug.Assert(cchNextValid > 0 && cchNextValid <= unicodeString.Length - ich); 

                    // The top-level call has to map all the input.
                    cchAdvance = MapUnresolvedCharacters(
                        new CharacterBufferRange( 
                            unicodeString,
                            ich, 
                            cchNextValid 
                            ),
                        culture, 
                        digitCulture,
                        firstValidFamily,
                        ref firstValidLength,
                        scaledTypefaceSpans, 
                        firstCharIndex + ich,
                        out cchNextValid 
                        ); 

                    Debug.Assert(cchNextValid == 0); 
                }

                ich += cchAdvance;
            } 

            advance += ich; 
            nextValid = ich + cchNextValid; 

            // The top-level call must map all the input; recursive calls map only what they can. 
            Debug.Assert(recursionDepth > 0 || advance == unicodeString.Length);
            return advance;
        }
Пример #59
0
        /// <summary>
        /// Get spans of index to the list of scaled shapeable typeface of the specified
        /// character string from the map table
        /// </summary> 
        private bool GetCachedScaledTypefaceMap(
            CharacterBufferRange        unicodeString, 
            CultureInfo                 culture, 
            CultureInfo                 digitCulture,
            ref SpanVector<int>         cachedScaledTypefaceIndexSpans, 
            int                         ichItem
            )
        {
            IntMap map; 
            if (!_intMaps.TryGetValue(culture, out map))
            { 
                return false; 
            }
 
            DigitMap digitMap = new DigitMap(digitCulture);

            int ich = 0;
            while (ich < unicodeString.Length) 
            {
                // Get map entry for first character. 
                int sizeofChar; 
                int ch = digitMap[
                    Classification.UnicodeScalar( 
                        new CharacterBufferRange(unicodeString, ich, unicodeString.Length - ich),
                        out sizeofChar
                    )
                ]; 

                ushort firstIndex = map[ch]; 
                if (firstIndex == 0) 
                    return false;
 
                // Advance past subsequent characters with the same mapping.
                int cchSpan = sizeofChar;
                for (; ich + cchSpan < unicodeString.Length; cchSpan += sizeofChar)
                { 
                    ch = digitMap[
                        Classification.UnicodeScalar( 
                            new CharacterBufferRange(unicodeString, ich + cchSpan, unicodeString.Length - ich - cchSpan), 
                            out sizeofChar
                        ) 
                    ];

                    if (map[ch] != firstIndex && !Classification.IsCombining(ch) && !Classification.IsJoiner(ch))
                        break; 
                }
 
                // map entry is stored in index+1, since 0 indicates uninitialized entry 
                cachedScaledTypefaceIndexSpans.Set(ichItem + ich, cchSpan, firstIndex - 1);
                ich += cchSpan; 
            }
            return true;
        }
Пример #60
0
        internal void GetShapeableText(
            CharacterBufferReference    characterBufferReference,
            int                         stringLength, 
            TextRunProperties           textRunProperties,
            CultureInfo                 digitCulture, 
            bool                        isRightToLeftParagraph, 
            IList<TextShapeableSymbols> shapeableList,
            IShapeableTextCollector     collector, 
            TextFormattingMode          textFormattingMode
            )
        {
            SpanVector<int> cachedScaledTypefaceIndexSpans; 

            int ichItem = 0; 
 
            CharacterBufferRange unicodeString = new CharacterBufferRange(
                characterBufferReference, 
                stringLength
                );

            CultureInfo culture = textRunProperties.CultureInfo; 
            IList<Span> spans;
 
            GCHandle gcHandle; 
            IntPtr ptext = characterBufferReference.CharacterBuffer.PinAndGetCharacterPointer(characterBufferReference.OffsetToFirstChar, out gcHandle);
 
            // Contextual number substitution cannot be performed on the run level, since it depends
            // on context - nearest preceding strong character. For this reason, contextual number
            // substitutions has been already done (TextStore.CreateLSRunsUniformBidiLevel) and
            // digitCulture has been updated to reflect culture which is dependent on the context. 
            // NumberSubstitutionMethod.AsCulture method can be resolved to Context, hence it also needs to be resolved to appropriate
            // not ambiguous method. 
            // Both of those values (Context and AsCulture) are resolved to one of following: European, Traditional or NativeNational, 
            // which can be safely handled by DWrite without getting context information.
            bool ignoreUserOverride; 
            NumberSubstitutionMethod numberSubstitutionMethod = DigitState.GetResolvedSubstitutionMethod(textRunProperties, digitCulture, out ignoreUserOverride);

            // Itemize the text based on DWrite's text analysis for scripts and number substitution.
            unsafe 
            {
                checked 
                { 
                    spans = MS.Internal.Text.TextInterface.TextAnalyzer.Itemize(
                        (ushort*)ptext.ToPointer(), 
                        (uint)stringLength,
                        culture,
                        MS.Internal.FontCache.DWriteFactory.Instance,
                        isRightToLeftParagraph, 
                        digitCulture,
                        ignoreUserOverride, 
                        (uint)numberSubstitutionMethod, 
                        ClassificationUtility.Instance,
                        UnsafeNativeMethods.CreateTextAnalysisSink, 
                        UnsafeNativeMethods.GetScriptAnalysisList,
                        UnsafeNativeMethods.GetNumberSubstitutionList,
                        UnsafeNativeMethods.CreateTextAnalysisSource
                        ); 
                }
 
            } 
            characterBufferReference.CharacterBuffer.UnpinCharacterPointer(gcHandle);
 
            SpanVector itemSpans = new SpanVector(null, new FrugalStructList<Span>((ICollection<Span>)spans));

            cachedScaledTypefaceIndexSpans = new SpanVector<int>(-1);
            foreach(Span itemSpan in itemSpans) 
            {
                MapItem( 
                    new CharacterBufferRange( 
                        unicodeString,
                        ichItem, 
                        itemSpan.length
                        ),
                    culture,
                    itemSpan, 
                    ref cachedScaledTypefaceIndexSpans,
                    ichItem 
                    ); 

                #if DEBUG 
                ValidateMapResult(
                    ichItem,
                    itemSpan.length,
                    ref cachedScaledTypefaceIndexSpans 
                    );
                #endif 
 
                ichItem += itemSpan.length;
            } 


            Debug.Assert(ichItem == unicodeString.Length);
 
            // intersect item spans with shapeable spans to create span of shapeable runs
 
            int ich = 0; 

            SpanRider itemSpanRider = new SpanRider(itemSpans); 
            SpanRider<int> typefaceIndexSpanRider = new SpanRider<int>(cachedScaledTypefaceIndexSpans);

            while(ich < unicodeString.Length)
            { 
                itemSpanRider.At(ich);
                typefaceIndexSpanRider.At(ich); 
 
                int index = typefaceIndexSpanRider.CurrentValue;
                Debug.Assert(index >= 0); 

                int cch = unicodeString.Length - ich;
                cch = Math.Min(cch, itemSpanRider.Length);
                cch = Math.Min(cch, typefaceIndexSpanRider.Length); 

                ScaledShapeTypeface scaledShapeTypeface = _cachedScaledTypefaces[index]; 
 
                collector.Add(
                    shapeableList, 
                    new CharacterBufferRange(
                        unicodeString,
                        ich,
                        cch 
                        ),
                    textRunProperties, 
                    (MS.Internal.Text.TextInterface.ItemProps)itemSpanRider.CurrentElement, 
                    scaledShapeTypeface.ShapeTypeface,
                    scaledShapeTypeface.ScaleInEm, 
                    scaledShapeTypeface.NullShape,
                    textFormattingMode
                    );
 
                ich += cch;
            } 
        }