public void FillEllipse(Brush brush, RectangleF rect) { if (brush == null) { throw new ArgumentNullException("brush"); } brush.Setup(this, true); context.FillEllipseInRect(rect); }
void FillBrush(Brush brush, FillMode fillMode = FillMode.Alternate) { brush.Setup(this, true); if (fillMode == FillMode.Alternate) { context.EOFillPath(); } else { context.FillPath(); } }
internal void Setup(Graphics graphics, bool fill) { brush.Setup(graphics, fill); // TODO: apply matrix if (graphics.LastPen == this && !changed) { return; } graphics.context.SetLineWidth(width); // miter limit // join // cap // dashes changed = false; graphics.LastPen = this; }
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); }
public void DrawString(string s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format = null) { if (font == null) throw new ArgumentNullException ("font"); if (brush == null) throw new ArgumentNullException ("brush"); if (s == null || s.Length == 0) return; if (format == null) { format = StringFormat.GenericDefault; } // TODO: Take into consideration units // Not sure we need the Save and Restore around this yet. context.SaveState(); // TextMatrix is not part of the Graphics State and Restore var saveMatrix = context.TextMatrix; bool layoutAvailable = true; // context.SelectFont ( font.nativeFont.PostScriptName, // font.SizeInPoints, // CGTextEncoding.MacRoman); // // context.SetCharacterSpacing(1); // context.SetTextDrawingMode(CGTextDrawingMode.Fill); // 5 // // // Setup both the stroke and the fill ? // brush.Setup(this, true); // brush.Setup(this, false); // // var textMatrix = font.nativeFont.Matrix; // // textMatrix.Scale(1,-1); // context.TextMatrix = textMatrix; // // context.ShowTextAtPoint(layoutRectangle.X, // layoutRectangle.Y + font.nativeFont.CapHeightMetric, s); // // // 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. brush.Setup(this, false); // Stroke // I think we only Fill the text with no Stroke surrounding context.SetTextDrawingMode(CGTextDrawingMode.Fill); var attributedString = buildAttributedString(s, font, format, lastBrushColor); // Work out the geometry RectangleF insetBounds = layoutRectangle; if (insetBounds.Size == SizeF.Empty) { insetBounds.Width = boundingBox.Width; insetBounds.Height = boundingBox.Height; layoutAvailable = false; } PointF textPosition = new PointF(insetBounds.X, insetBounds.Y); float boundsWidth = insetBounds.Width; // Calculate the lines int start = 0; int length = attributedString.Length; float baselineOffset = 0; var typesetter = new CTTypesetter(attributedString); // First we need to calculate the offset for Vertical Alignment if we // are using anything but Top if (layoutAvailable && format.LineAlignment != StringAlignment.Near) { while (start < length) { int count = typesetter.SuggestLineBreak (start, boundsWidth); var line = typesetter.GetLine (new NSRange(start, count)); // Create and initialize some values from the bounds. float ascent; float descent; float leading; line.GetTypographicBounds (out ascent, out descent, out leading); baselineOffset += (float)Math.Ceiling (ascent + descent + leading + 1); // +1 matches best to CTFramesetter's behavior line.Dispose (); start += count; } //textPosition.Y += baselineOffset; } // If we are drawing vertial direction then we need to rotate our context transform by 90 degrees if ((format.FormatFlags & StringFormatFlags.DirectionVertical) == StringFormatFlags.DirectionVertical) { //textMatrix.Rotate (ConversionHelpers.DegreesToRadians (90)); var verticalOffset = 0.0f; while (start < length) { int count = typesetter.SuggestLineBreak (start, boundsWidth); var line = typesetter.GetLine (new NSRange(start, count)); // Create and initialize some values from the bounds. float ascent; float descent; float leading; line.GetTypographicBounds (out ascent, out descent, out leading); verticalOffset += (float)Math.Ceiling (ascent + descent + leading + 1); // +1 matches best to CTFramesetter's behavior line.Dispose (); start += count; } context.TranslateCTM (layoutRectangle.X, layoutRectangle.Y); context.RotateCTM (ConversionHelpers.DegreesToRadians (90)); context.TranslateCTM (-layoutRectangle.X, -layoutRectangle.Y); context.TranslateCTM (0, -verticalOffset); start = 0; } start = 0; while (start < length && textPosition.Y < insetBounds.Bottom) { // Now we ask the typesetter to break off a line for us. // This also will take into account line feeds embedded in the text. // Example: "This is text \n with a line feed embedded inside it" int count = typesetter.SuggestLineBreak(start, boundsWidth); var line = typesetter.GetLine(new NSRange(start, count)); // Create and initialize some values from the bounds. float ascent; float descent; float leading; double lineWidth = line.GetTypographicBounds(out ascent, out descent, out leading); // Calculate the string format if need be var penFlushness = 0.0f; if (format != null) { if (layoutAvailable) { if (format.Alignment == StringAlignment.Far) penFlushness = (float)line.GetPenOffsetForFlush(1.0f, boundsWidth); else if (format.Alignment == StringAlignment.Center) penFlushness = (float)line.GetPenOffsetForFlush(0.5f, boundsWidth); } else { // We were only passed in a point so we need to format based // on the point. if (format.Alignment == StringAlignment.Far) penFlushness -= (float)lineWidth; else if (format.Alignment == StringAlignment.Center) penFlushness -= (float)lineWidth / 2.0f; } } // initialize our Text Matrix or we could get trash in here var textMatrix = new CGAffineTransform ( 1, 0, 0, -1, 0, ascent); if (format.LineAlignment == StringAlignment.Near) textMatrix.Translate (penFlushness + textPosition.X, textPosition.Y); if (format.LineAlignment == StringAlignment.Center) textMatrix.Translate (penFlushness + textPosition.X, textPosition.Y + ((insetBounds.Height / 2) - (baselineOffset / 2)) ); if (format.LineAlignment == StringAlignment.Far) textMatrix.Translate(penFlushness + textPosition.X, textPosition.Y + ((insetBounds.Height) - (baselineOffset))); context.TextMatrix = textMatrix; // and draw the line line.Draw(context); // Move the index beyond the line break. start += count; textPosition.Y += (float)Math.Ceiling(ascent + descent + leading + 1); // +1 matches best to CTFramesetter's behavior line.Dispose(); } // 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. brush.Setup(this, true); // Fill context.TextMatrix = saveMatrix; context.RestoreState(); }
void FillBrush(Brush brush, FillMode fillMode = FillMode.Alternate) { brush.Setup (this, true); if (fillMode == FillMode.Alternate) context.EOFillPath (); else context.FillPath (); }
public void DrawString(string s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format = null) { if (font == null) { throw new ArgumentNullException("font"); } if (brush == null) { throw new ArgumentNullException("brush"); } if (s == null || s.Length == 0) { return; } if (format == null) { format = StringFormat.GenericDefault; } // TODO: Take into consideration units // Not sure we need the Save and Restore around this yet. context.SaveState(); // TextMatrix is not part of the Graphics State and Restore var saveMatrix = context.TextMatrix; bool layoutAvailable = true; // context.SelectFont ( font.nativeFont.PostScriptName, // font.SizeInPoints, // CGTextEncoding.MacRoman); // // context.SetCharacterSpacing(1); // context.SetTextDrawingMode(CGTextDrawingMode.Fill); // 5 // // // Setup both the stroke and the fill ? // brush.Setup(this, true); // brush.Setup(this, false); // // var textMatrix = font.nativeFont.Matrix; // // textMatrix.Scale(1,-1); // context.TextMatrix = textMatrix; // // context.ShowTextAtPoint(layoutRectangle.X, // layoutRectangle.Y + font.nativeFont.CapHeightMetric, s); // // // 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. brush.Setup(this, false); // Stroke // I think we only Fill the text with no Stroke surrounding context.SetTextDrawingMode(CGTextDrawingMode.Fill); var attributedString = buildAttributedString(s, font, format, lastBrushColor); // Work out the geometry RectangleF insetBounds = layoutRectangle; if (insetBounds.Size == SizeF.Empty) { insetBounds.Width = boundingBox.Width; insetBounds.Height = boundingBox.Height; layoutAvailable = false; if (format.LineAlignment != StringAlignment.Near) { insetBounds.Size = MeasureString(s, font); } } PointF textPosition = new PointF(insetBounds.X, insetBounds.Y); float boundsWidth = insetBounds.Width; // Calculate the lines int start = 0; int length = (int)attributedString.Length; float baselineOffset = 0; var typesetter = new CTTypesetter(attributedString); // First we need to calculate the offset for Vertical Alignment if we // are using anything but Top if (layoutAvailable && format.LineAlignment != StringAlignment.Near) { while (start < length) { int count = (int)typesetter.SuggestLineBreak(start, boundsWidth); var line = typesetter.GetLine(new NSRange(start, count)); // Create and initialize some values from the bounds. nfloat ascent; nfloat descent; nfloat leading; line.GetTypographicBounds(out ascent, out descent, out leading); baselineOffset += (float)Math.Ceiling(ascent + descent + leading + 1); // +1 matches best to CTFramesetter's behavior line.Dispose(); start += count; } start = 0; } // If we are drawing vertial direction then we need to rotate our context transform by 90 degrees if ((format.FormatFlags & StringFormatFlags.DirectionVertical) == StringFormatFlags.DirectionVertical) { //textMatrix.Rotate (ConversionHelpers.DegreesToRadians (90)); var verticalOffset = 0.0f; while (start < length) { int count = (int)typesetter.SuggestLineBreak(start, boundsWidth); var line = typesetter.GetLine(new NSRange(start, count)); // Create and initialize some values from the bounds. nfloat ascent; nfloat descent; nfloat leading; line.GetTypographicBounds(out ascent, out descent, out leading); verticalOffset += (float)Math.Ceiling(ascent + descent + leading + 1); // +1 matches best to CTFramesetter's behavior line.Dispose(); start += count; } context.TranslateCTM(layoutRectangle.X, layoutRectangle.Y); context.RotateCTM(ConversionHelpers.DegreesToRadians(90)); context.TranslateCTM(-layoutRectangle.X, -layoutRectangle.Y); context.TranslateCTM(0, -verticalOffset); start = 0; } start = 0; while (start < length && textPosition.Y < insetBounds.Bottom) { // Now we ask the typesetter to break off a line for us. // This also will take into account line feeds embedded in the text. // Example: "This is text \n with a line feed embedded inside it" int count = (int)typesetter.SuggestLineBreak(start, boundsWidth); var line = typesetter.GetLine(new NSRange(start, count)); // Create and initialize some values from the bounds. nfloat ascent; nfloat descent; nfloat leading; double lineWidth = line.GetTypographicBounds(out ascent, out descent, out leading); // Calculate the string format if need be var penFlushness = 0.0f; if (format != null) { if (layoutAvailable) { if (format.Alignment == StringAlignment.Far) { penFlushness = (float)line.GetPenOffsetForFlush(1.0f, boundsWidth); } else if (format.Alignment == StringAlignment.Center) { penFlushness = (float)line.GetPenOffsetForFlush(0.5f, boundsWidth); } } else { // We were only passed in a point so we need to format based // on the point. if (format.Alignment == StringAlignment.Far) { penFlushness -= (float)lineWidth; } else if (format.Alignment == StringAlignment.Center) { penFlushness -= (float)lineWidth / 2.0f; } } } // initialize our Text Matrix or we could get trash in here var textMatrix = new CGAffineTransform( 1, 0, 0, -1, 0, ascent); if (format.LineAlignment == StringAlignment.Near) { textMatrix.Translate(penFlushness + textPosition.X, textPosition.Y); } if (format.LineAlignment == StringAlignment.Center) { if (layoutAvailable) { textMatrix.Translate(penFlushness + textPosition.X, textPosition.Y + ((insetBounds.Height / 2) - (baselineOffset / 2))); } else { textMatrix.Translate(penFlushness + textPosition.X, textPosition.Y - ((insetBounds.Height / 2) - (baselineOffset / 2))); } } if (format.LineAlignment == StringAlignment.Far) { if (layoutAvailable) { textMatrix.Translate(penFlushness + textPosition.X, textPosition.Y + ((insetBounds.Height) - (baselineOffset))); } else { textMatrix.Translate(penFlushness + textPosition.X, textPosition.Y - ((insetBounds.Height) - (baselineOffset))); } } context.TextMatrix = textMatrix; // and draw the line line.Draw(context); // Move the index beyond the line break. start += count; textPosition.Y += (float)Math.Ceiling(ascent + descent + leading + 1); // +1 matches best to CTFramesetter's behavior line.Dispose(); } // 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. brush.Setup(this, true); // Fill context.TextMatrix = saveMatrix; context.RestoreState(); }
internal void Setup(Graphics graphics, bool fill) { CGContext context = graphics.context; brush.Setup(graphics, fill); // TODO: apply matrix if (graphics.LastPen == this && !changed) { return; } // A Width of 0 will result in the Pen drawing as if the Width were 1. width = width == 0 ? 1 : width; //width = graphics.GraphicsUnitConvertFloat (width); context.SetLineWidth(width); switch (startCap) { case LineCap.Flat: context.SetLineCap(width > 1f || dashStyle != DashStyle.Solid ? CGLineCap.Butt : CGLineCap.Square); break; case LineCap.Square: context.SetLineCap(CGLineCap.Square); break; case LineCap.Round: context.SetLineCap(CGLineCap.Round); break; // case LineCap.Triangle: // case LineCap.NoAnchor: // case LineCap.SquareAnchor: // case LineCap.RoundAnchor: // case LineCap.DiamondAnchor: // case LineCap.ArrowAnchor: // case LineCap.AnchorMask: // case LineCap.Custom: default: context.SetLineCap(CGLineCap.Butt); break; } switch (dashStyle) { case DashStyle.Custom: context.SetLineDash(dashOffset, setupMorseCode(dashPattern)); break; case DashStyle.Dash: context.SetLineDash(dashOffset, setupMorseCode(Dash)); break; case DashStyle.Dot: context.SetLineDash(dashOffset, setupMorseCode(Dot)); break; case DashStyle.DashDot: context.SetLineDash(dashOffset, setupMorseCode(DashDot)); break; case DashStyle.DashDotDot: context.SetLineDash(dashOffset, setupMorseCode(DashDotDot)); break; default: context.SetLineDash(0, new nfloat[0]); break; } // miter limit // join // cap // dashes changed = false; graphics.LastPen = this; }
public void DrawString(string s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format = null) { if (font == null) { throw new ArgumentNullException("font"); } if (brush == null) { throw new ArgumentNullException("brush"); } if (s == null || s.Length == 0) { return; } // TODO: Take into consideration units // Not sure we need the Save and Restore around this yet. context.SaveState(); // TextMatrix is not part of the Graphics State and Restore var saveMatrix = context.TextMatrix; bool layoutAvailable = true; // context.SelectFont ( font.nativeFont.PostScriptName, // font.SizeInPoints, // CGTextEncoding.MacRoman); // // context.SetCharacterSpacing(1); // context.SetTextDrawingMode(CGTextDrawingMode.Fill); // 5 // // // Setup both the stroke and the fill ? // brush.Setup(this, true); // brush.Setup(this, false); // // var textMatrix = font.nativeFont.Matrix; // // textMatrix.Scale(1,-1); // context.TextMatrix = textMatrix; // // context.ShowTextAtPoint(layoutRectangle.X, // layoutRectangle.Y + font.nativeFont.CapHeightMetric, s); // // // 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. brush.Setup(this, false); // Stroke // I think we only Fill the text with no Stroke surrounding context.SetTextDrawingMode(CGTextDrawingMode.Fill); var attributedString = buildAttributedString(s, font, lastBrushColor); // Work out the geometry RectangleF insetBounds = layoutRectangle; if (insetBounds.Size == SizeF.Empty) { insetBounds.Width = boundingBox.Width; insetBounds.Height = boundingBox.Height; layoutAvailable = false; } PointF textPosition = new PointF(insetBounds.X, insetBounds.Y); float boundsWidth = insetBounds.Width; // Calculate the lines int start = 0; int length = attributedString.Length; var typesetter = new CTTypesetter(attributedString); while (start < length && textPosition.Y < insetBounds.Bottom) { // Now we ask the typesetter to break off a line for us. // This also will take into account line feeds embedded in the text. // Example: "This is text \n with a line feed embedded inside it" int count = typesetter.SuggestLineBreak(start, boundsWidth); var line = typesetter.GetLine(new NSRange(start, count)); // Create and initialize some values from the bounds. float ascent; float descent; float leading; double lineWidth = line.GetTypographicBounds(out ascent, out descent, out leading); // Calculate the string format if need be var penFlushness = 0.0f; if (format != null) { if (layoutAvailable) { if (format.Alignment == StringAlignment.Far) { penFlushness = (float)line.GetPenOffsetForFlush(1.0f, boundsWidth); } else if (format.Alignment == StringAlignment.Center) { penFlushness = (float)line.GetPenOffsetForFlush(0.5f, boundsWidth); } } else { // We were only passed in a point so we need to format based // on the point. if (format.Alignment == StringAlignment.Far) { penFlushness -= (float)lineWidth; } else if (format.Alignment == StringAlignment.Center) { penFlushness -= (float)lineWidth / 2.0f; } } } // initialize our Text Matrix or we could get trash in here var textMatrix = CGAffineTransform.MakeIdentity(); // flip us over or things just do not look good textMatrix.Scale(1, -1); context.TextMatrix = textMatrix; // move us to our graphics baseline var textViewPos = textPosition; textViewPos.Y += (float)Math.Floor(ascent - 1); // take into account our justification textViewPos.X += penFlushness; // setup our text position context.TextPosition = textViewPos; // and draw the line line.Draw(context); // Move the index beyond the line break. start += count; textPosition.Y += (float)Math.Ceiling(ascent + descent + leading + 1); // +1 matches best to CTFramesetter's behavior line.Dispose(); } // 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. brush.Setup(this, true); // Fill context.TextMatrix = saveMatrix; context.RestoreState(); }