예제 #1
0
        internal void DrawString(DrawStringCache.Entry c, PointF offset)
        {
            // TODO: Consider units

            // First we call the brush with a fill of false so the brush can setup the stroke color
            // that the text will be using.
            // For LinearGradientBrush this will setup a TransparentLayer so the gradient can
            // be filled at the end.  See comments.

            var savedSmoothingMode = SmoothingMode;

            SmoothingMode = SmoothingMode.Default;

            c.brush.Setup(this, false);             // Stroke

            if ((c.format.FormatFlags & StringFormatFlags.NoClip) == 0 && c.layoutAvailable)
            {
                context.SaveState();
                if ((c.format.FormatFlags & StringFormatFlags.DirectionVertical) == StringFormatFlags.DirectionVertical)
                {
                    context.ClipToRect(new CGRect(offset.X, offset.Y, c.boundsHeight, c.boundsWidth));
                }
                else
                {
                    context.ClipToRect(new CGRect(offset.X, offset.Y, c.boundsWidth, c.boundsHeight));
                }
            }

            if ((c.format.FormatFlags & StringFormatFlags.DirectionVertical) == StringFormatFlags.DirectionVertical)
            {
                c.verticalMatrix = CGAffineTransform.MakeTranslation(-offset.X, -offset.Y);
                c.verticalMatrix.Rotate((float)NMath.PI * 0.5f);
                c.verticalMatrix.Translate(offset.X, offset.Y);
                if (c.layoutAvailable)
                {
                    c.verticalMatrix.Translate(c.layoutRectangle.Width, 0);
                }
                else
                {
                    c.verticalMatrix.Translate((c.lines.Count * c.lineHeight), 0);
                }

                context.ConcatCTM(c.verticalMatrix);
            }

            var textPosition = new PointF(c.textPosition.X + offset.X, c.textPosition.Y + offset.Y);             // Let's not change pre-calculated value

            foreach (var line in c.lines)
            {
                // Make sure that the line still exists, it may be null if it's too small to render any text and wants StringTrimming
                if (line != null)
                {
                    nfloat ascent, descent, leading;
                    var    lineWidth = line.GetTypographicBounds(out ascent, out descent, out leading);

                    // Calculate the string format if need be
                    nfloat x = textPosition.X;
                    if (c.layoutAvailable)
                    {
                        if (c.format.Alignment == StringAlignment.Far)
                        {
                            x += (float)line.GetPenOffsetForFlush(1.0f, c.boundsWidth);
                        }
                        else if (c.format.Alignment == StringAlignment.Center)
                        {
                            x += (float)line.GetPenOffsetForFlush(0.5f, c.boundsWidth);
                        }
                        if ((c.format.FormatFlags & StringFormatFlags.MeasureTrailingSpaces) != 0)
                        {
                            if (c.format.Alignment == StringAlignment.Far)
                            {
                                x -= (float)line.TrailingWhitespaceWidth;
                            }
                            else if (c.format.Alignment == StringAlignment.Center)
                            {
                                x -= (float)line.TrailingWhitespaceWidth * 0.5f;
                            }
                        }
                    }
                    else
                    {
                        // We were only passed in a point so we need to format based on the point.
                        if ((c.format.FormatFlags & StringFormatFlags.MeasureTrailingSpaces) != 0)
                        {
                            lineWidth += line.TrailingWhitespaceWidth;
                        }
                        if (c.format.Alignment == StringAlignment.Far)
                        {
                            x -= (float)lineWidth;
                        }
                        else if (c.format.Alignment == StringAlignment.Center)
                        {
                            x -= (float)lineWidth / 2.0f;
                        }
                    }

                    // initialize our Text Matrix or we could get trash in here
                    context.TextMatrix = new CGAffineTransform(1, 0, 0, -1, x, textPosition.Y + c.font.nativeFont.AscentMetric);
                    line.Draw(context);
                    //line.Dispose ();

                    // Currently we support strikethrough only at the font level (for a whole text, not for ranges). It seems CoreText does not handle it for us.
                    if (c.font.Strikeout)
                    {
                        var sy = textPosition.Y + ascent - c.font.nativeFont.XHeightMetric / (nfloat)2.0;
                        context.MoveTo(x, sy);
                        context.AddLineToPoint(x + (nfloat)lineWidth, sy);
                        context.SetStrokeColor(lastBrushColor.ToCGColor());
                        context.SetLineWidth(1);
                        context.StrokePath();
                    }
                }

                // Move the index beyond the line break.
                textPosition.Y += c.lineHeight;
            }

            if ((c.format.FormatFlags & StringFormatFlags.NoClip) == 0 && c.layoutAvailable)
            {
                context.RestoreState();
            }
            else if ((c.format.FormatFlags & StringFormatFlags.DirectionVertical) == StringFormatFlags.DirectionVertical)
            {
                context.ConcatCTM(c.verticalMatrix.Invert());
            }

            // Now we call the brush with a fill of true so the brush can do the fill if need be
            // For LinearGradientBrush this will draw the Gradient and end the TransparentLayer.
            // See comments.
            c.brush.Setup(this, true);             // Fill

            SmoothingMode = savedSmoothingMode;
        }
예제 #2
0
        internal DrawStringCache.Entry CreateCacheEntry(string s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format)
        {
            if (font == null)
            {
                throw new ArgumentNullException(nameof(font));
            }
            if (brush == null)
            {
                throw new ArgumentNullException(nameof(brush));
            }

            layoutRectangle.Location = PointF.Empty;
            var c = new DrawStringCache.Entry(s, font, brush, layoutRectangle, format);

            brush.Setup(this, false);             // Stroke
            var attributedString = buildAttributedString(s, font, format, lastBrushColor);

            // Work out the geometry
            c.layoutAvailable = true;
            var insetBounds = layoutRectangle;

            if (insetBounds.Size == SizeF.Empty)
            {
                insetBounds.Width  = float.MaxValue;
                insetBounds.Height = float.MaxValue;
                c.layoutAvailable  = false;
            }

            c.lineHeight     = font.nativeFont.GetLineHeight();
            c.lines          = new List <CTLine>();
            c.verticalMatrix = default(CGAffineTransform);

            // Calculate the lines
            // If we are drawing vertical direction then we need to rotate our context transform by 90 degrees
            if ((format.FormatFlags & StringFormatFlags.DirectionVertical) == StringFormatFlags.DirectionVertical)
            {
                // Swap out the width and height and calculate the lines
                c.lines        = CreateLines(font, attributedString, new SizeF(insetBounds.Height, insetBounds.Width), format, c.lineHeight);
                c.boundsWidth  = insetBounds.Height;
                c.boundsHeight = insetBounds.Width;
            }
            else
            {
                c.lines        = CreateLines(font, attributedString, insetBounds.Size, format, c.lineHeight);
                c.boundsWidth  = insetBounds.Width;
                c.boundsHeight = insetBounds.Height;
            }

            c.textPosition = new PointF(insetBounds.X + .5f, insetBounds.Y + .5f);
            if (c.layoutAvailable)
            {
                if (format.LineAlignment == StringAlignment.Far)
                {
                    c.textPosition.Y += c.boundsHeight - (c.lines.Count * c.lineHeight);
                }
                else if (format.LineAlignment == StringAlignment.Center)
                {
                    c.textPosition.Y += (c.boundsHeight - (c.lines.Count * c.lineHeight)) / 2;
                }
            }
            else
            {
                // Precalculate maximum width to allow for aligning lines
                if (format.Alignment != StringAlignment.Near)
                {
                    float maxLineWidth = 0;
                    foreach (var line in c.lines)
                    {
                        if (line != null)
                        {
                            nfloat ascent, descent, leading;
                            var    lineWidth = line.GetTypographicBounds(out ascent, out descent, out leading);
                            if ((format.FormatFlags & StringFormatFlags.MeasureTrailingSpaces) != 0)
                            {
                                lineWidth += line.TrailingWhitespaceWidth;
                            }
                            maxLineWidth = Math.Max(maxLineWidth, (float)NMath.Ceiling((float)lineWidth));
                        }
                    }
                    c.boundsWidth = maxLineWidth;
                }
                if (format.LineAlignment == StringAlignment.Far)
                {
                    c.textPosition.Y -= c.lineHeight * c.lines.Count;
                }
                else if (format.LineAlignment == StringAlignment.Center)
                {
                    c.textPosition.Y -= (c.lineHeight * c.lines.Count) / 2;
                }
            }
            return(c);
        }