public override Size GetSize(object backend) { LayoutInfo li = (LayoutInfo)backend; using (CTFrame frame = CreateFrame(li)) { if (frame == null) { return(Size.Zero); } Size result = Size.Zero; CTLine [] lines = frame.GetLines(); float lineHeight = li.Font.Ascender - li.Font.Descender + li.Font.Leading; // try to approximate Pango's layout foreach (var line in lines) { result.Width = Math.Max(result.Width, line.GetTypographicBounds()); result.Height += lineHeight; // clean up after ourselves as we go line.Dispose(); } // CoreText throws away trailing line breaks.. if (li.Text.EndsWith("\n")) { result.Height += lineHeight; } result.Width = Math.Ceiling(result.Width); result.Height = Math.Ceiling(result.Height); return(result); } }
public static RectangleF GetTextSize(CTFrame frame) { var minY = float.MaxValue; var maxY = float.MinValue; float width = 0; var lines = frame.GetLines(); var origins = new CGPoint[lines.Length]; frame.GetLineOrigins(new NSRange(0, 0), origins); for (var i = 0; i < lines.Length; i++) { var line = lines[i]; var lineWidth = (float)line.GetTypographicBounds(out var ascent, out var descent, out var leading); if (lineWidth > width) { width = lineWidth; } var origin = origins[i]; minY = (float)Math.Min(minY, origin.Y - ascent); maxY = (float)Math.Max(maxY, origin.Y + descent); lines[i].Dispose(); } return(new RectangleF(0f, minY, width, Math.Max(0, maxY - minY))); }
// Helper method for drawing the current selection range (as a simple filled rect) void DrawRangeAsSelection(NSRange selectionRange) { // If not in editing mode, we do not draw selection rects if (!IsEditing) { return; } // If selection range empty, do not draw if (selectionRange.Length == 0 || selectionRange.Location == NSRange.NotFound) { return; } // set the fill color to the selection color SelectionColor.SetFill(); // Iterate over the lines in our CTFrame, looking for lines that intersect // with the given selection range, and draw a selection rect for each intersection var lines = frame.GetLines(); for (int i = 0; i < lines.Length; i++) { CTLine line = lines [i]; NSRange lineRange = line.StringRange; NSRange range = new NSRange(lineRange.Location, lineRange.Length); NSRange intersection = RangeIntersection(range, selectionRange); if (intersection.Location != NSRange.NotFound && intersection.Length > 0) { // The text range for this line intersects our selection range nfloat xStart = line.GetOffsetForStringIndex(intersection.Location); nfloat xEnd = line.GetOffsetForStringIndex(intersection.Location + intersection.Length); var origin = new CGPoint [lines.Length]; // Get coordinate and bounds information for the intersection text range frame.GetLineOrigins(new NSRange(i, 0), origin); nfloat ascent, descent, leading; line.GetTypographicBounds(out ascent, out descent, out leading); // Create a rect for the intersection and draw it with selection color CGRect selectionRect = new CGRect(xStart, origin [0].Y - descent, xEnd - xStart, ascent + descent); UIGraphics.RectFill(selectionRect); } } }
public override Size GetSize(object backend) { LayoutInfo li = (LayoutInfo)backend; using (CTFrame frame = CreateFrame(li)) { if (frame == null) { return(Size.Zero); } Size result = Size.Zero; CTLine [] lines = frame.GetLines(); nfloat lineHeight = li.Font.Ascender - li.Font.Descender + li.Font.Leading; CTLine ellipsis = null; bool ellipsize = li.Width.HasValue && li.TextTrimming == TextTrimming.WordElipsis; if (ellipsize) { ellipsis = new CTLine(CreateAttributedString(li, "...")); } // try to approximate Pango's layout foreach (var line in lines) { var l = line; if (ellipsize) // we need to create a new CTLine here because the framesetter already truncated the text for the line { l = new CTLine(CreateAttributedString(li, li.Text.Substring((int)line.StringRange.Location))) .GetTruncatedLine(li.Width.Value, CTLineTruncation.End, ellipsis); line.Dispose(); } result.Width = Math.Max(result.Width, l.GetTypographicBounds()); result.Height += lineHeight; // clean up after ourselves as we go l.Dispose(); } // CoreText throws away trailing line breaks.. if (li.Text.EndsWith("\n")) { result.Height += lineHeight; } result.Width = Math.Ceiling(result.Width); result.Height = Math.Ceiling(result.Height); return(result); } }
internal static void Draw(CGContext ctx, object layout, double x, double y) { LayoutInfo li = (LayoutInfo)layout; using (CTFrame frame = CreateFrame(li)) { if (frame == null) { return; } CTLine ellipsis = null; bool ellipsize = li.Width.HasValue && li.TextTrimming == TextTrimming.WordElipsis; if (ellipsize) { ellipsis = new CTLine(CreateAttributedString(li, "...")); } nfloat lineHeight = li.Font.Ascender - li.Font.Descender + li.Font.Leading; ctx.SaveState(); ctx.TextMatrix = CGAffineTransform.MakeScale(1f, -1f); ctx.TranslateCTM((float)x, (float)y + li.Font.Ascender); foreach (var line in frame.GetLines()) { ctx.TextPosition = CGPoint.Empty; // Determine final line var ln = line; if (ellipsize) { // we need to create a new CTLine here because the framesetter already truncated the text for the line ln = new CTLine(CreateAttributedString(li, li.Text.Substring((int)line.StringRange.Location))) .GetTruncatedLine(li.Width.Value, CTLineTruncation.End, ellipsis); line.Dispose(); } else if (li.Width.HasValue && li.TextAlignment != Alignment.Start) { var tx = li.Width.Value - GetLineWidth(ln); if (li.TextAlignment == Alignment.Center) { tx /= 2d; } ctx.TextPosition = new CGPoint((nfloat)tx, 0); } ln.Draw(ctx); ctx.TranslateCTM(0, lineHeight); ln.Dispose(); } ctx.RestoreState(); } }