Beispiel #1
0
        /// <summary> 
        /// Create a rectangle of the two specified UV coordinates 
        /// </summary>
        /// <param name="origin">line drawing origin</param> 
        /// <param name="topLeft">logical top-left point</param>
        /// <param name="bottomRight">logical bottom-right point</param>
        /// <param name="line">container line</param>
        internal static Rect RectUV( 
            Point           origin,
            LSPOINT         topLeft, 
            LSPOINT         bottomRight, 
            TextMetrics.FullTextLine    line
            ) 
        {
            int dx = topLeft.x - bottomRight.x;
            if(dx == 1 || dx == -1)
            { 
                // in certain situation LS can be off by 1
                bottomRight.x = topLeft.x; 
            } 

            Rect rect = new Rect( 
                new Point(line.Formatter.IdealToReal(topLeft.x), line.Formatter.IdealToReal(topLeft.y)),
                new Point(line.Formatter.IdealToReal(bottomRight.x), line.Formatter.IdealToReal(bottomRight.y))
                );
 
            if(DoubleUtil.AreClose(rect.TopLeft.X, rect.BottomRight.X))
            { 
                rect.Width = 0; 
            }
 
            if(DoubleUtil.AreClose(rect.TopLeft.Y, rect.BottomRight.Y))
            {
                rect.Height = 0;
            } 

            return rect; 
        } 
            public override IEnumerable<IndexedGlyphRun> GetIndexedGlyphRuns()
            {
                if ((_statusFlags & StatusFlags.IsDisposed) != 0)
                {
                    throw new ObjectDisposedException(SR.Get(SRID.TextLineHasBeenDisposed));
                }

                IEnumerable<IndexedGlyphRun> result = null;

                if (_ploline.Value != System.IntPtr.Zero)
                {
                    TextFormatterContext context = _metrics._formatter.AcquireContext(
                        new DrawingState(null, new Point(0, 0), null, this),
                        _ploc.Value
                        );

                    //
                    // Kick off line enumeration
                    //
                    LsErr lserr = LsErr.None;

                    LSPOINT point = new LSPOINT(0, 0);
                    lserr = UnsafeNativeMethods.LoEnumLine(
                        _ploline.Value,   // line
                        false,      // reverse enumeration
                        false,      // geometry needed
                        ref point   // starting point
                        );

                    // result
                    result = context.IndexedGlyphRuns;

                    // get the exception in context before it is released
                    Exception callbackException = context.CallbackException;

                    // clear the context
                    context.ClearIndexedGlyphRuns();
                    context.Release();

                    if (lserr != LsErr.None)
                    {
                        if (callbackException != null)
                        {
                            // rethrow exception thrown in callbacks
                            throw callbackException;
                        }
                        else
                        {
                            // throw with LS error codes
                            TextFormatterContext.ThrowExceptionFromLsError(SR.Get(SRID.EnumLineFailure, lserr), lserr);
                        }
                    }
                }

                return result;
            }
        private unsafe GlyphRun ComputeShapedGlyphRun(
            LSRun                   lsrun,                      // ls run
            TextFormatterImp        textFormatterImp,           // The TextFormatter Implementation
            bool                    originProvided,             // flag indicate whether the origin of the run is provided                        
            LSPOINT                 lsrunOrigin,                // physical start of the run
            int                     charCount,                  // characters count
            char                    *pwchText,                  // characters for the GlyphRun
            ushort                  *puClusterMap,              // cluster map
            int                     glyphCount,                 // glyph count
            ushort                  *puGlyphs,                  // glyph indices
            int                     *piJustifiedGlyphAdvances,  // glyph advances
            GlyphOffset             *piiGlyphOffsets,           // glyph offsets
            bool                    justify
            )
        {
            TextMetrics.FullTextLine currentLine = Draw.CurrentLine;

            Point runOrigin = new Point();
            int nominalX = 0;
            int nominalY = 0;

            if (originProvided)
            {   
                if (currentLine.RightToLeft)
                {





                    lsrunOrigin.x = -lsrunOrigin.x;
                }

                if (textFormatterImp.TextFormattingMode == TextFormattingMode.Display && justify)
                {
                    LSRun.UVToNominalXY(
                        Draw.LineOrigin,
                        Draw.VectorToLineOrigin,
                        currentLine.LSLineUToParagraphU(lsrunOrigin.x),
                        lsrunOrigin.y + lsrun.BaselineMoveOffset,
                        currentLine,
                        out nominalX,
                        out nominalY
                        );
                }
                else
                {
                    runOrigin = LSRun.UVToXY(
                        Draw.LineOrigin,
                        Draw.VectorToLineOrigin,
                        currentLine.LSLineUToParagraphU(lsrunOrigin.x),
                        lsrunOrigin.y + lsrun.BaselineMoveOffset,
                        currentLine
                        );
                }
            }





            char[] charString = new char[charCount];
            ushort[] clusterMap = new ushort[charCount];

            for (int i = 0; i < charCount; i++)
            {
                charString[i] = pwchText[i];
                clusterMap[i] = puClusterMap[i];
            }

            ushort[] glyphIndices = new ushort[glyphCount];            
            IList<double> glyphAdvances;
            IList<Point> glyphOffsets;

            bool isRightToLeft = (lsrun.BidiLevel & 1) != 0;

            if (textFormatterImp.TextFormattingMode == TextFormattingMode.Ideal)
            {
                glyphAdvances = new ThousandthOfEmRealDoubles(textFormatterImp.IdealToReal(lsrun.EmSize), glyphCount);
                glyphOffsets = new ThousandthOfEmRealPoints(textFormatterImp.IdealToReal(lsrun.EmSize), glyphCount);

                for (int i = 0; i < glyphCount; i++)
                {
                    glyphIndices[i] = puGlyphs[i];
                    glyphAdvances[i] = textFormatterImp.IdealToReal(piJustifiedGlyphAdvances[i]);
                    glyphOffsets[i] = new Point(
                        textFormatterImp.IdealToReal(piiGlyphOffsets[i].du),
                        textFormatterImp.IdealToReal(piiGlyphOffsets[i].dv)
                        );
                }
            }
            else
            {
                if (justify)
                {
                    AdjustMetricsForDisplayModeJustifiedText(
                        pwchText,
                        piJustifiedGlyphAdvances,
                        glyphCount,
                        isRightToLeft,
                        nominalX,
                        nominalY,
                        out runOrigin,
                        out glyphAdvances
                        );
                }
                else
                {
                    glyphAdvances = new List<double>(glyphCount);
                    for (int i = 0; i < glyphCount; i++)
                    {
                        glyphAdvances.Add(textFormatterImp.IdealToReal(piJustifiedGlyphAdvances[i]));
                    }
                }
                glyphOffsets  = new List<Point>(glyphCount);
                for (int i = 0; i < glyphCount; i++)
                {
                    glyphIndices[i] = puGlyphs[i];
                    glyphOffsets.Add(new Point(
                            textFormatterImp.IdealToReal(piiGlyphOffsets[i].du),
                            textFormatterImp.IdealToReal(piiGlyphOffsets[i].dv)
                            ));
                }
            }

#if CHECK_GLYPHS
            if (   lsrun._glyphs != null
                && glyphCount <= lsrun._glyphs.Length)
            {
                for (int i = 0; i < glyphCount; i++)
                {
                    Debug.Assert(glyphIndices[i] == lsrun._glyphs[i], "Corrupted glyphs");
                }
            }
#endif            

            GlyphRun glyphRun = lsrun.Shapeable.ComputeShapedGlyphRun(
                runOrigin,
                charString, 
                clusterMap, 
                glyphIndices, 
                glyphAdvances, 
                glyphOffsets,
                isRightToLeft, 
                false   // no sideway support yet
                );

            return glyphRun;
        }
        private unsafe GlyphRun ComputeUnshapedGlyphRun(
            LSRun               lsrun,              // LSrun used to shape the GlyphRun            
            LsTFlow             textFlow,           // flow direction
            TextFormatterImp    textFormatterImp,   // The TextFormatter Implementation
            bool                originProvided,     // flag indicate whether the origin of the run is provided                        
            LSPOINT             lsrunOrigin,        // physical start of the run
            int                 dupRun,             // width of the run
            int                 cchText,            // character count
            char                *pwchText,          // characters for display 
            int                 *piCharAdvances,    // character advance widths,
            bool                justify
            )
        {
            GlyphRun glyphRun = null;
            if (lsrun.Type == Plsrun.Text)
            {
                Debug.Assert(lsrun.Shapeable != null);
                Point runOrigin    = new Point();
                int nominalX = 0;
                int nominalY = 0;

                if (originProvided)
                {                   
                    TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                    
                    if (textFlow == LsTFlow.lstflowWS)
                    {
                        lsrunOrigin.x -= dupRun;
                    }

                    if (currentLine.RightToLeft)
                    {
                        lsrunOrigin.x = -lsrunOrigin.x;
                    }

                    if (textFormatterImp.TextFormattingMode == TextFormattingMode.Display && justify)
                    {
                        LSRun.UVToNominalXY(
                            Draw.LineOrigin,
                            Draw.VectorToLineOrigin,
                            currentLine.LSLineUToParagraphU(lsrunOrigin.x),
                            lsrunOrigin.y + lsrun.BaselineMoveOffset,
                            currentLine,
                            out nominalX,
                            out nominalY
                            );
                    }
                    else
                    {
                        runOrigin = LSRun.UVToXY(
                            Draw.LineOrigin,
                            Draw.VectorToLineOrigin,
                            currentLine.LSLineUToParagraphU(lsrunOrigin.x),
                            lsrunOrigin.y + lsrun.BaselineMoveOffset,
                            currentLine
                            );
                    }
                }



                
                char[] charString = new char[cchText];
                IList<double> charWidths;

                bool isRightToLeft = (lsrun.BidiLevel & 1) != 0;

                if (textFormatterImp.TextFormattingMode == TextFormattingMode.Ideal)
                {
                    charWidths = new ThousandthOfEmRealDoubles(textFormatterImp.IdealToReal(lsrun.EmSize), cchText);
                    for (int i = 0; i < cchText; i++)
                    {
                        charString[i] = pwchText[i];
                        charWidths[i] = textFormatterImp.IdealToReal(piCharAdvances[i]);
                    }
                }
                else
                {
                    if (justify)
                    {
                        AdjustMetricsForDisplayModeJustifiedText(
                            pwchText,
                            piCharAdvances,
                            cchText,
                            isRightToLeft,
                            nominalX,
                            nominalY,
                            out runOrigin,
                            out charWidths
                            );
                    }
                    else
                    {
                        charWidths = new List<double>(cchText);
                        for (int i = 0; i < cchText; i++)
                        {
                            charWidths.Add(textFormatterImp.IdealToReal(piCharAdvances[i]));
                        }
                    }
                    for (int i = 0; i < cchText; i++)
                    {
                        charString[i] = pwchText[i];
                    }
                }

                

                glyphRun = lsrun.Shapeable.ComputeUnshapedGlyphRun(
                    runOrigin,
                    charString,
                    charWidths
                    );
            }            

            return glyphRun;
        } 
        internal unsafe LsErr EnumText(        
            IntPtr                      pols,                           // ls context
            Plsrun                      plsrun,                         // plsrun
            int                         cpFirst,                        // first cp of the ls dnode
            int                         dcp,                            // dcp of the dnode
            char                        *pwchText,                      // characters for glyph run
            int                         cchText,                        // length of characters 
            LsTFlow                     lstFlow,                        // flow direction
            int                         fReverseOrder,                  // flag for reverse order enumeration
            int                         fGeometryProvided,              // flag for providing geometry 
            ref LSPOINT                 pptStart,                       // [in] logical start of the run
            ref LsHeights               pheights,                       // [in] height (iff geometryProvided)
            int                         dupRun,                         // width of the run
            int                         glyphBaseRun,                   // flag for glyph based run
            int                         *piCharAdvances,                // character advance widths (iff !glyphBaseRun)
            ushort                      *puClusterMap,                  // cluster map (iff glyphBaseRun)
            ushort                      *characterProperties,           // character properties (iff glyphBaseRun)
            ushort                      *puGlyphs,                      // glyph indices (iff glyphBaseRun)
            int                         *piJustifiedGlyphAdvances,      // glyph advances (iff glyphBaseRun)
            GlyphOffset                 *piiGlyphOffsets,               // glyph offsets (iff glyphBaseRun)
            uint                        *piGlyphProperties,             // glyph properties (iff glyphProperties)
            int                         glyphCount                      // glyph count
            )
        {
            Debug.Assert(fGeometryProvided == 0, "Line enumeration doesn't need geometry information");

            if (cpFirst < 0)
            {

                return LsErr.None;
            }
            
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try                 
            {   
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);
                GlyphRun glyphRun = null;
                if (glyphBaseRun != 0)
                {

                    if (glyphCount > 0)
                    {

                        glyphRun = ComputeShapedGlyphRun(
                            lsrun, 
                            currentLine.Formatter,
                            false,      // glyph run origin not provided
                            pptStart, 
                            cchText, 
                            pwchText, 
                            puClusterMap, 
                            glyphCount, 
                            puGlyphs,
                            piJustifiedGlyphAdvances, 
                            piiGlyphOffsets,
                            currentLine.IsJustified
                            );
                    }
                }
                else if (cchText > 0)
                {

                    dupRun = 0;
                    for (int i = 0; i < cchText; i++)
                    {
                        dupRun += piCharAdvances[i];
                    }

                    

                    glyphRun = ComputeUnshapedGlyphRun(
                        lsrun, 
                        lstFlow, 
                        currentLine.Formatter,
                        false,      // glyph run origin not provided at enumeration
                        pptStart, 
                        dupRun, 
                        cchText, 
                        pwchText, 
                        piCharAdvances,
                        currentLine.IsJustified
                        );
                }
                
                if (glyphRun != null)
                {





                    IndexedGlyphRuns.Add(
                        new IndexedGlyphRun(
                           currentLine.GetExternalCp(cpFirst),
                           dcp,
                           glyphRun
                           )
                    );
                }                   
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("EnumText", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            
            return lserr;
        }
        internal unsafe LsErr EnumTab(
            IntPtr              pols,               // pointer to context
            Plsrun              plsrun,             // plsrun
            int                 cpFirst,            // first cp of the dnode run
            char                *pwchText,          // a single tab character
            char                tabLeader,          // a single tab leader character
            LsTFlow             lstFlow,            // flow direction
            int                 fReverseOrder,      // flag for reverse order enumeration
            int                 fGeometryProvided,  // flag for providing geometry information
            ref LSPOINT         pptStart,           // [in] logical start of the run (iff geometryProvided)
            ref LsHeights       heights,            // [in] height (iff geometryProvided)
            int                 dupRun              // width of the run
            )
        {       
            if (cpFirst < 0)
            {

                return LsErr.None;
            }
        
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try 
            {
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);
                GlyphRun glyphRun = null;                
                
                if (lsrun.Type == Plsrun.Text)
                {         


                    int charWidth = 0;
                    lsrun.Shapeable.GetAdvanceWidthsUnshaped(
                        &tabLeader, 
                        1,
                        TextFormatterImp.ToIdeal, 
                        &charWidth
                        );                

                    glyphRun = ComputeUnshapedGlyphRun(
                        lsrun, 
                        lstFlow,
                        currentLine.Formatter,
                        false,      // glyph run origin not provided at enumeration time
                        pptStart, 
                        charWidth, 
                        1, 
                        &tabLeader, 
                        &charWidth,
                        currentLine.IsJustified
                        );

                }

                if (glyphRun != null)
                {                    
                    IndexedGlyphRuns.Add(
                        new IndexedGlyphRun(
                           currentLine.GetExternalCp(cpFirst),
                           1,       // dcp is 1 for a Tab character
                           glyphRun
                           )
                    );                    
                }                
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("EnumTab", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }

            return lserr;
        }
        internal unsafe LsErr DrawGlyphs(
            IntPtr                      pols,                       // Line Layout context
            Plsrun                      plsrun,                     // plsrun
            char*                       pwchText,                   // character string
            ushort*                     puClusterMap,               // character-to-cluster map
            ushort*                     puCharProperties,           // character properties
            int                         charCount,                  // character count
            ushort*                     puGlyphs,                   // glyph indices
            int*                        piJustifiedGlyphAdvances,   // justified glyph advances
            int*                        piGlyphAdvances,            // original ideal glyph advances
            GlyphOffset*                piiGlyphOffsets,            // glyph offsets
            uint*                       piGlyphProperties,          // glyph properties
            LsExpType*                  plsExpType,                 // glyph expansion types
            int                         glyphCount,                 // glyph count
            LsTFlow                     textFlow,                   // text flow
            uint                        displayMode,                // draw transparent or opaque
            ref LSPOINT                 ptRun,                      // [in] display position (at baseline)
            ref LsHeights               lsHeights,                  // [in] run height metrics
            int                         runWidth,                   // run overall advance width
            ref LSRECT                  clippingRect                // [in] clipping rectangle if any applied
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);

                Debug.Assert(TextStore.IsContent(plsrun) && lsrun.Shapeable != null);

                GlyphRun glyphRun = ComputeShapedGlyphRun(
                    lsrun,
                    currentLine.Formatter,
                    true,           // origin of the glyph run provided at drawing time
                    ptRun,
                    charCount,
                    pwchText,
                    puClusterMap,
                    glyphCount,
                    puGlyphs,
                    piJustifiedGlyphAdvances,
                    piiGlyphOffsets,
                    currentLine.IsJustified
                    );

                if (glyphRun != null)
                {
                    DrawingContext drawingContext = Draw.DrawingContext;

                    Draw.SetGuidelineY(glyphRun.BaselineOrigin.Y);

                    try 
                    {
                        _boundingBox.Union(
                            lsrun.DrawGlyphRun(
                                drawingContext, 
                                null,     // draw with the run's foreground
                                glyphRun
                                )
                            );
                    }
                    finally 
                    {
                        Draw.UnsetGuidelineY();
                    }

                }
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("DrawGlyphs", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
 internal static extern LsErr LoEnumLine(
     IntPtr                  ploline,
     bool                    reverseOder,
     bool                    fGeometryneeded,
     ref LSPOINT             pt
     );
        private Rect DrawTextDecoration(
            LSRun           lsrun,           // lsrun
            Brush           foregroundBrush, // default brush if text decoration has no pen
            LSPOINT         ptOrigin,        // drawing origin
            int             ulLength,        // underline length
            int             ulThickness,     // underline thickness
            LsTFlow         textFlow,        // text flow direction
            TextDecoration  textDecoration   //TextDecoration to be draw (add to sublinecollection
            )
        {
            switch (textFlow)
            {
                case LsTFlow.lstflowWS:
                case LsTFlow.lstflowNE:
                case LsTFlow.lstflowNW:

                    ptOrigin.x -= ulLength;
                    break;
            }

            TextMetrics.FullTextLine currentLine = Draw.CurrentLine;

            if (currentLine.RightToLeft)
            {
                ptOrigin.x = -ptOrigin.x;
            }

            int u = currentLine.LSLineUToParagraphU(ptOrigin.x);

            Point baselineOrigin = LSRun.UVToXY(
                Draw.LineOrigin,
                Draw.VectorToLineOrigin,
                u,
                currentLine.BaselineOffset,
                currentLine
                );

            Point lineOrigin = LSRun.UVToXY(
                Draw.LineOrigin,
                Draw.VectorToLineOrigin,
                u,
                ptOrigin.y + lsrun.BaselineMoveOffset,
                currentLine
                );



            double penThickness = 1.0;
            if (textDecoration.Pen != null)
            {
                penThickness = textDecoration.Pen.Thickness;
            }


            switch (textDecoration.PenThicknessUnit)
            {
                case TextDecorationUnit.FontRecommended:

                    penThickness = currentLine.Formatter.IdealToReal(ulThickness * penThickness);
                    break;

                case TextDecorationUnit.FontRenderingEmSize:
                    penThickness = currentLine.Formatter.IdealToReal(penThickness * lsrun.EmSize);
                    break;

                case TextDecorationUnit.Pixel:

                    break;

                default:
                    Debug.Assert(false, "Not supported TextDecorationUnit");
                    break;
            }


            penThickness = Math.Abs(penThickness);            




            double unitValue = 1.0;
            switch (textDecoration.PenOffsetUnit)
            {
                case TextDecorationUnit.FontRecommended:

                    unitValue = (lineOrigin.Y - baselineOrigin.Y);
                    break;

                case TextDecorationUnit.FontRenderingEmSize:
                    unitValue = currentLine.Formatter.IdealToReal(lsrun.EmSize);
                    break;

                case TextDecorationUnit.Pixel:
                    unitValue = 1.0;
                    break;

                default:
                    Debug.Assert(false, "Not supported TextDecorationUnit");
                    break;
            }


            double lineLength = currentLine.Formatter.IdealToReal(ulLength);

            DrawingContext drawingContext = Draw.DrawingContext;

            if (drawingContext != null)
            {      
                
                


                double drawingPenThickness = penThickness;



                Point  drawingLineOrigin   = lineOrigin;

                bool animated = !textDecoration.CanFreeze && (unitValue != 0);

                int pushCount = 0; // counter for the number of explicit DrawingContext.Push()
                

                Draw.SetGuidelineY(baselineOrigin.Y);                
                
                try 
                {
                    if (animated)
                    {                    









                        ScaleTransform scaleTransform = new ScaleTransform(
                            1.0,        // X scale
                            unitValue,  // y scale 
                            drawingLineOrigin.X,  // reference point of scaling
                            drawingLineOrigin.Y  // reference point of scaling
                            );
                           
                        TranslateTransform yTranslate = new TranslateTransform(
                            0,                       // x translate
                            textDecoration.PenOffset // y translate
                            );


                        drawingPenThickness = drawingPenThickness / Math.Abs(unitValue);
                                                    

                        drawingContext.PushTransform(scaleTransform);
                        pushCount++;
                        drawingContext.PushTransform(yTranslate);
                        pushCount++;

                    }
                    else
                    {

                        drawingLineOrigin.Y += unitValue * textDecoration.PenOffset;
                    }                    







                    drawingContext.PushGuidelineY2(baselineOrigin.Y, drawingLineOrigin.Y - drawingPenThickness * 0.5 - baselineOrigin.Y);
                    pushCount++;









                    if (textDecoration.Pen == null)
                    {

                        drawingContext.DrawRectangle(
                            foregroundBrush,               // fill using foreground
                            null,                          // null pen for rectangle stroke
                            new Rect(
                                drawingLineOrigin.X,
                                drawingLineOrigin.Y - drawingPenThickness * 0.5,
                                lineLength,
                                drawingPenThickness
                                )                    
                            );                    
                    }
                    else                    
                    {





                        Pen textDecorationPen = textDecoration.Pen.CloneCurrentValue();
                        if (Object.ReferenceEquals(textDecoration.Pen, textDecorationPen))
                        {

                            textDecorationPen = textDecoration.Pen.Clone();
                        }
                        
                        textDecorationPen.Thickness = drawingPenThickness;                  
                        

                        drawingContext.DrawLine(
                            textDecorationPen,
                            drawingLineOrigin,
                            new Point(drawingLineOrigin.X + lineLength, drawingLineOrigin.Y)
                            );
                    }
                }
                finally 
                {               
                    for (int i = 0; i < pushCount; i++)
                    {
                        drawingContext.Pop(); 
                    }

                    Draw.UnsetGuidelineY();
                }
            }
            
            return new Rect(
                lineOrigin.X,
                lineOrigin.Y + unitValue * textDecoration.PenOffset - penThickness * 0.5,
                lineLength,
                penThickness
                );
        }
        internal unsafe LsErr DrawTextRun(
            IntPtr          pols,               // Line Layout context
            Plsrun          plsrun,             // plsrun
            ref LSPOINT     ptText,             // [in] text origin
            char*           pwchText,           // character string
            int*            piCharAdvances,     // char advance widths
            int             cchText,            // text length
            LsTFlow         textFlow,           // text flow
            uint            displayMode,        // draw in transparent or opaque
            ref LSPOINT     ptRun,              // [in] run origin
            ref LsHeights   lsHeights,          // [in] run height
            int             dupRun,             // run length
            ref LSRECT      clipRect            // [in] from DisplayLine's clip rectangle param
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);

                GlyphRun glyphRun = ComputeUnshapedGlyphRun(
                    lsrun, 
                    textFlow, 
                    currentLine.Formatter,
                    true,       // origin of the glyph run provided at drawing time                    
                    ptText, 
                    dupRun, 
                    cchText, 
                    pwchText, 
                    piCharAdvances,
                    currentLine.IsJustified
                    );

                if (glyphRun != null)
                {
                    DrawingContext drawingContext = Draw.DrawingContext;

                    Draw.SetGuidelineY(glyphRun.BaselineOrigin.Y);                    

                    try 
                    {
                        _boundingBox.Union(
                            lsrun.DrawGlyphRun(
                                drawingContext, 
                                null,   // draw with the run's foreground brush
                                glyphRun
                                )
                            );
                    }
                    finally
                    {
                        Draw.UnsetGuidelineY();
                    }
                }
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("DrawTextRun", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        internal LsErr DrawUnderline(
            IntPtr pols,                // Line Layout context
            Plsrun      plsrun,         // plsrun
            uint        ulType,         // kind of underline
            ref LSPOINT ptOrigin,       // [in] drawing origin
            int         ulLength,       // underline length
            int         ulThickness,    // underline thickness
            LsTFlow     textFlow,       // text flow direction
            uint        displayMode,    // display mode
            ref LSRECT  clipRect        // [in] clipping rectangle
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;
            try
            {
                if (!TextStore.IsContent(plsrun))
                {

                    return LsErr.None;
                }

                lsrun = Draw.CurrentLine.GetRun(plsrun);

                const uint locationMask = (1U << (int)TextDecorationLocation.Underline);

                DrawTextDecorations(
                    lsrun,
                    locationMask,
                    ptOrigin.x,   // left
                    ptOrigin.y,   // underline top from LS
                    0,            // overline top; not used
                    0,            // strikethrough top; not used
                    0,            // baseline top; not used
                    ulLength,
                    ulThickness,
                    textFlow
                    );
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("DrawUnderline", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        internal LsErr DrawStrikethrough(
            IntPtr          pols,           // Line Layout context
            Plsrun          plsrun,         // plsrun
            uint            stType,         // kind of strike
            ref LSPOINT     ptOrigin,       // [in] drawing origin
            int             stLength,       // strike length
            int             stThickness,    // strike thickness
            LsTFlow         textFlow,       // text flow direction
            uint            displayMode,    // display mode
            ref LSRECT      clipRect        // [in] clipping rectangle
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;
            try
            {
                if (!TextStore.IsContent(plsrun))
                {

                    return LsErr.None;
                }

                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);

                double strikeThroughPositionInEm;
                double strikeThroughThicknessInEm;

                GetLSRunStrikethroughMetrics(lsrun, out strikeThroughPositionInEm, out strikeThroughThicknessInEm);

                int baselineTop = ptOrigin.y + (int)Math.Round(lsrun.EmSize * strikeThroughPositionInEm);
                int overlineTop = baselineTop - (lsrun.BaselineOffset - (int)Math.Round(lsrun.EmSize * strikeThroughThicknessInEm));

                const uint locationMask = 
                    (1U << (int)TextDecorationLocation.OverLine) |
                    (1U << (int)TextDecorationLocation.Strikethrough) |
                    (1U << (int)TextDecorationLocation.Baseline);

                DrawTextDecorations(
                    lsrun,
                    locationMask,
                    ptOrigin.x,   // left
                    0,            // underline top; not used
                    overlineTop,
                    ptOrigin.y,   // strikethrough top from LS
                    baselineTop,
                    stLength,
                    stThickness,
                    textFlow
                    );
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("DrawStrikethrough", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
 internal static extern LsErr LoQueryLinePointPcp(
     IntPtr                  ploline,
     ref LSPOINT             ptQuery,        //  use POINT as POINTUV
     int                     depthQueryMax,
     IntPtr                  pSubLineInfo,   // passing raw pinned pointer for out array
     out int                 actualDepthQuery,
     out LsTextCell          lsTextCell
     );
            private void QueryLinePointPcp(
                Point               ptQuery,
                LsQSubInfo[]        subLineInfo,
                out int             actualDepthQuery,
                out LsTextCell      lsTextCell
                )
            {
                Debug.Assert(_ploline.Value != IntPtr.Zero);

                LsErr lserr = LsErr.None;
                lsTextCell = new LsTextCell();
                unsafe
                {
                    fixed(LsQSubInfo* plsqsubl = subLineInfo)
                    {
                        LSPOINT pt = new LSPOINT((int)ptQuery.X, (int)ptQuery.Y);
                        lserr = UnsafeNativeMethods.LoQueryLinePointPcp(
                            _ploline.Value,
                            ref pt,
                            subLineInfo.Length,
                            (System.IntPtr)plsqsubl,
                            out actualDepthQuery,
                            out lsTextCell
                            );
                    }
                }

                if(lserr != LsErr.None)
                {
                    TextFormatterContext.ThrowExceptionFromLsError(SR.Get(SRID.QueryLineFailure, lserr), lserr);
                }

                if (lsTextCell.lscpEndCell < lsTextCell.lscpStartCell)
                {
                    // When hit-testing is done on a generated hyphen of a hyphenated word, LS can only tell
                    // the start LSCP and not the end LSCP. Argurably this is LS bug. In such situation they
                    // should assume the end LSCP being the last LSCP of the line.
                    //
                    // However our code assumes that LS must tell both and the text cell must have size greater
                    // than one codepoint. We count on that to reliably advance the caret position.
                    //
                    // The LSPTS bug#1005 has been filed and while we are still debating, we need to unblock
                    // ourselves. What we can do is to assume that the next caret stop in this case is always
                    // the next codepoint.
                    lsTextCell.lscpEndCell = lsTextCell.lscpStartCell;
                }
            }
        internal LsErr InlineDraw(
            System.IntPtr   pols,           // Line Layout context
            Plsrun          plsrun,         // plsrun
            ref LSPOINT     ptRun,          // [in] pen position at which to render the object
            LsTFlow         textFlow,       // text flow direction
            int             runWidth        // object width
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);

                LSPOINT lsrunOrigin = ptRun;

                Debug.Assert(lsrun.Type == Plsrun.InlineObject);

                int baseDirection = currentLine.RightToLeft ? 1 : 0;
                int runDirection = (int)(lsrun.BidiLevel & 1);

                if (baseDirection != 0)
                {
                    lsrunOrigin.x = -lsrunOrigin.x;
                }

                TextEmbeddedObject textObject = lsrun.TextRun as TextEmbeddedObject;

                Debug.Assert(textObject != null);
                Debug.Assert(textFlow != LsTFlow.lstflowWS || runDirection != 0);

                if ((baseDirection ^ runDirection) != 0)
                {

                    lsrunOrigin.x -= runWidth;
                }


                Point baselineOrigin = new Point(
                    currentLine.Formatter.IdealToReal(currentLine.LSLineUToParagraphU(lsrunOrigin.x))+ Draw.VectorToLineOrigin.X,
                    currentLine.Formatter.IdealToReal((lsrunOrigin.y + lsrun.BaselineMoveOffset)) + Draw.VectorToLineOrigin.Y
                    );


                Rect objectBounds = textObject.ComputeBoundingBox(
                     baseDirection != 0, // rightToLeft
                    false  // no sideway support yet
                    );

                if (!objectBounds.IsEmpty)
                {


                    objectBounds.X += baselineOrigin.X;
                    objectBounds.Y += baselineOrigin.Y;
                }


                _boundingBox.Union(
                    new Rect(

                    LSRun.UVToXY(
                            Draw.LineOrigin,
                            new Point(),
                            objectBounds.Location.X,
                            objectBounds.Location.Y,
                            currentLine
                            ),

                    LSRun.UVToXY(
                            Draw.LineOrigin,
                            new Point(),
                            objectBounds.Location.X + objectBounds.Size.Width,
                            objectBounds.Location.Y + objectBounds.Size.Height,
                            currentLine
                            )
                        )
                    );

                DrawingContext drawingContext = Draw.DrawingContext;                
                
                if (drawingContext != null)
                {

                    Draw.SetGuidelineY(baselineOrigin.Y);

                    try 
                    {                    
                        if (Draw.AntiInversion == null)
                        {

                            textObject.Draw(
                                drawingContext,
                                LSRun.UVToXY(
                                    Draw.LineOrigin,
                                    new Point(),
                                    baselineOrigin.X,
                                    baselineOrigin.Y,
                                    currentLine
                                    ),
                                baseDirection != 0,
                                false
                                );
                        }
                        else
                        {




                            drawingContext.PushTransform(Draw.AntiInversion);
                            try 
                            {
                                textObject.Draw(drawingContext, baselineOrigin, baseDirection != 0, false);
                            } 
                            finally
                            {
                                drawingContext.Pop();
                            }
                        }
                    }
                    finally 
                    {
                        Draw.UnsetGuidelineY();
                    }
                }
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("InlineDraw", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
            private void DrawTextLine(
                DrawingContext      drawingContext,
                Point               origin,
                MatrixTransform     antiInversion
                )
            {
                Rect boundingBox = Rect.Empty;

                if (_ploline.Value != System.IntPtr.Zero)
                {
                    TextFormatterContext context;
                    LsErr lserr = LsErr.None;
                    LSRECT rect = new LSRECT(0, 0, _metrics._textWidthAtTrailing, _metrics._height);

                    // DrawingState needs to be properly disposed after performing actual drawing operations.
                    using (DrawingState drawingState = new DrawingState(drawingContext, origin, antiInversion, this))
                    {
                        context = _metrics._formatter.AcquireContext(
                            drawingState,
                            _ploc.Value
                            );

                        // set the collector and send the line to LS to draw

                        context.EmptyBoundingBox();

                        // LS line reference origin
                        LSPOINT lsRefOrigin = new LSPOINT(0, _metrics._baselineOffset);

                        lserr = UnsafeNativeMethods.LoDisplayLine(
                            _ploline.Value,
                            ref lsRefOrigin,
                            1,      // 0 - opaque, 1 - transparent
                            ref rect
                            );
                    }

                    boundingBox = context.BoundingBox;

                    // get the exception in context before it is released
                    Exception callbackException = context.CallbackException;

                    context.Release();

                    if(lserr != LsErr.None)
                    {
                        if(callbackException != null)
                        {
                            // rethrow exception thrown in callbacks
                            throw callbackException;
                        }
                        else
                        {
                            // throw with LS error codes
                            TextFormatterContext.ThrowExceptionFromLsError(SR.Get(SRID.CreateLineFailure, lserr), lserr);
                        }
                    }

                    // keep context alive at least til here
                    GC.KeepAlive(context);
                }

                if (_collapsingSymbol != null)
                {
                    // draw collapsing symbol if any
                    Point vectorToOrigin = new Point();
                    if (antiInversion != null)
                    {
                        vectorToOrigin = origin;
                        origin.X = origin.Y = 0;
                    }

                    boundingBox.Union(DrawCollapsingSymbol(drawingContext, origin, vectorToOrigin));
                }

                BuildOverhang(origin, boundingBox);
                _statusFlags |= StatusFlags.BoundingBoxComputed;
            }
 internal static extern LsErr LoDisplayLine(
     IntPtr                  ploline,
     ref LSPOINT             pt,
     uint                    displayMode,
     ref LSRECT              clipRect
     );