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