static string RenderRectangle(XIR.Rect rectangle) { var base64 = string.Empty; var size = new CGSize(rectangle.Width, rectangle.Height); var origin = new CGPoint(rectangle.X, rectangle.Y); // We want the absolute values of the size var workSize = new CGSize(Math.Abs(size.Width), Math.Abs(size.Height)); // This is our scale factor for output var dstSize = new CGSize(100, 100); // Define our Height label variables var numHeightLabelBounds = CGSize.Empty; var heightLabelBounds = CGSize.Empty; var heightBounds = CGSize.Empty; // Obtain our label lines and bounding boxes of the labels var numHeightLine = GetLabel(string.Format("{0:0.########}", size.Height), out numHeightLabelBounds); var heightLine = GetSubLabel("Height", out heightLabelBounds); heightBounds.Width = NMath.Max(numHeightLabelBounds.Width, heightLabelBounds.Width); heightBounds.Height = NMath.Max(numHeightLabelBounds.Height, heightLabelBounds.Height); // Define our Width label variables var numWidthLabelBounds = CGSize.Empty; var widthLabelBounds = CGSize.Empty; var widthBounds = CGSize.Empty; // Obtain our label lines and bound boxes of the labels var numWidthLine = GetLabel(string.Format("{0:0.########}", size.Width), out numWidthLabelBounds); var widthLine = GetSubLabel("Width", out widthLabelBounds); widthBounds.Width = NMath.Max(numWidthLabelBounds.Width, widthLabelBounds.Width); widthBounds.Height = NMath.Max(numWidthLabelBounds.Height, widthLabelBounds.Height); // Define our Width label variables var originLabelBounds = CGSize.Empty; var xyLabelBounds = CGSize.Empty; var originBounds = CGSize.Empty; // Obtain our label lines and bound boxes of the labels var originLine = GetLabel(string.Format("({0:0.########}, {1:0.########})", origin.X, origin.Y), out originLabelBounds); var xyLine = GetSubLabel("x, y", out xyLabelBounds); originBounds.Width = NMath.Max(originLabelBounds.Width, xyLabelBounds.Width); originBounds.Height = NMath.Max(originLabelBounds.Height, xyLabelBounds.Height); // Calculate our scale based on our destination size var ratio = 1f; if (!workSize.IsEmpty) { if (workSize.Width > workSize.Height) { ratio = (float)workSize.Height / (float)workSize.Width; dstSize.Height = (int)(dstSize.Height * ratio); } else { ratio = (float)workSize.Width / (float)workSize.Height; dstSize.Width = (int)(dstSize.Width * ratio); } // Make sure we at least have something to draw if the values are very small dstSize.Width = NMath.Max(dstSize.Width, 4f); dstSize.Height = NMath.Max(dstSize.Height, 4f); } else { dstSize = CGSize.Empty; } // Define graphic element sizes and offsets var lineWidth = 2; var capSize = 8f; var vCapIndent = 3f; var separationSpace = 2f; var dotSize = new CGSize(6, 6); var extraBoundingSpaceWidth = (widthBounds.Width + separationSpace) * 2; var extraBoundingSpaceHeight = (heightBounds.Height + separationSpace) * 2; int width = (int)(dstSize.Width + lineWidth + capSize + vCapIndent + extraBoundingSpaceWidth); int height = (int)(dstSize.Height + lineWidth + capSize + extraBoundingSpaceHeight + originBounds.Height * 2); var bytesPerRow = 4 * width; using (var context = new CGBitmapContext( IntPtr.Zero, width, height, 8, bytesPerRow, CGColorSpace.CreateDeviceRGB(), CGImageAlphaInfo.PremultipliedFirst)) { // Clear the context with our background color context.SetFillColor(BackgroundColor.CGColor); context.FillRect(new CGRect(0, 0, width, height)); // Setup our matrices so our 0,0 is top left corner. Just makes it easier to layout context.ConcatCTM(context.GetCTM().Invert()); var matrix = new CGAffineTransform( 1, 0, 0, -1, 0, height); context.ConcatCTM(matrix); context.SetStrokeColor(pen.CGColor); context.SetFillColor(pen.CGColor); context.SetLineWidth(lineWidth); context.SaveState(); // We need to offset the drawing of our size segment rulers leaving room for labels var xOffSet = heightBounds.Width; var yOffset = ((height + originBounds.Height * 2) - extraBoundingSpaceHeight) / 2f - dstSize.Height / 2f; context.TranslateCTM(xOffSet, yOffset); context.AddEllipseInRect(new CGRect(vCapIndent - dotSize.Width / 2f, vCapIndent - dotSize.Height / 2f, dotSize.Width, dotSize.Height)); context.FillPath(); context.AddRect(new CGRect(vCapIndent, vCapIndent, dstSize.Width, dstSize.Height)); context.StrokePath(); context.RestoreState(); // Setup our text matrix var textMatrix = new CGAffineTransform( 1, 0, 0, -1, 0, 0); context.TextMatrix = textMatrix; // Draw the Origin labels context.TextPosition = new CGPoint((xOffSet + vCapIndent) - originLabelBounds.Width / 2, originLabelBounds.Height); originLine.Draw(context); context.TextPosition = new CGPoint((xOffSet + vCapIndent) - xyLabelBounds.Width / 2, originLabelBounds.Height * 2); xyLine.Draw(context); // Draw the Height label var heightCenter = yOffset + ((dstSize.Height / 2) + ((vCapIndent + lineWidth) * 2)); context.TextPosition = new CGPoint(heightBounds.Width / 2 - numHeightLabelBounds.Width / 2, heightCenter - heightBounds.Height / 2f); numHeightLine.Draw(context); context.TextPosition = new CGPoint(heightBounds.Width / 2 - heightLabelBounds.Width / 2, heightCenter + heightBounds.Height / 2f); heightLine.Draw(context); // Draw the Width label var widthOffsetX = heightBounds.Width + ((dstSize.Width / 2) - ((lineWidth + vCapIndent) * 2));// xOffSet - vCapIndent - lineWidth + dstSize.Width / 2f; context.TextPosition = new CGPoint(widthOffsetX + (widthBounds.Width / 2 - numWidthLabelBounds.Width / 2), height - widthBounds.Height - 2f); numWidthLine.Draw(context); context.TextPosition = new CGPoint(widthOffsetX + (widthBounds.Width / 2 - widthLabelBounds.Width / 2), height - widthLabelBounds.Height / 2f); widthLine.Draw(context); // Get rid of our lines numHeightLine.Dispose(); heightLine.Dispose(); numWidthLine.Dispose(); widthLine.Dispose(); originLine.Dispose(); xyLine.Dispose(); // Convert to base64 for display var bitmap = new NSBitmapImageRep(context.ToImage()); var data = bitmap.RepresentationUsingTypeProperties(NSBitmapImageFileType.Png); base64 = data.GetBase64EncodedString(NSDataBase64EncodingOptions.None); } return(String.Format( "<figure>" + "<figcatpion>" + "Rectangle: " + "<span class='var'>X</span> = <span class='value'>{0:0.########}</span>, " + "<span class='var'>Y</span> = <span class='value'>{1:0.########}</span>, " + "<span class='var'>Width</span> = <span class='value'>{2:0.########}</span>, " + "<span class='var'>Height</span> = <span class='value'>{3:0.########}</span>" + "</figcaption>" + "<img width='{4}' height='{5}' src='data:image/png;base64,{6}' />" + "</figure>", origin.X, origin.Y, size.Width, size.Height, (int)width, (int)height, base64 )); }