public CTLine GetTruncatedLine (double width, CTLineTruncation truncationType, CTLine truncationToken) { var h = CTLineCreateTruncatedLine (Handle, width, truncationType, truncationToken == null ? IntPtr.Zero : truncationToken.Handle); if (h == IntPtr.Zero) return null; return new CTLine (h, true); }
public override double GetBaseline(object backend) { LayoutInfo li = (LayoutInfo)backend; using (var line = new CTLine (CreateAttributedString (li))) { nfloat ascent, descent, leading; line.GetTypographicBounds (out ascent, out descent, out leading); return (double)ascent; } }
public static ABC GetCharWidthABC(char ch, Font font, System.Drawing.Graphics gr) { ABC[] _temp = new ABC[1]; var nativFont = CreateFont (font.Name, font.Size, font.Style, font.GdiCharSet, font.GdiVerticalFont); var atts = buildAttributedString(ch.ToString(), nativFont); // for now just a line not sure if this is going to work CTLine line = new CTLine(atts); float ascent; float descent; float leading; _temp[0].abcB = (uint)line.GetTypographicBounds(out ascent, out descent, out leading); return _temp[0]; }
static void PrepareGlyphArcInfo (CTLine line, long glyphCount, GlyphArcInfo[] glyphArcInfo) { var runArray = line.GetGlyphRuns (); // Examine each run in the line, updating glyphOffset to track how far along the run is // in terms of glyphCount. long glyphOffset = 0; float ascent = 0; float descent = 0; float leading = 0; foreach (var run in runArray) { var runGlyphCount = run.GlyphCount; // Ask for the width of each glyph in turn. var runGlyphIndex = 0; for (; runGlyphIndex < runGlyphCount; runGlyphIndex++) { glyphArcInfo[runGlyphIndex + glyphOffset].width = (float)run.GetTypographicBounds (new NSRange (runGlyphIndex, 1), out ascent, out descent, out leading); } glyphOffset += runGlyphCount; } var lineLength = line.GetTypographicBounds (out ascent, out descent, out leading); var prevHalfWidth = glyphArcInfo[0].width / 2.0; glyphArcInfo[0].angle = (float)((prevHalfWidth / lineLength) * Math.PI); var lineGlyphIndex = 1; for (; lineGlyphIndex < glyphCount; lineGlyphIndex++) { var halfWidth = glyphArcInfo[lineGlyphIndex].width / 2.0; var prevCenterToCenter = prevHalfWidth + halfWidth; glyphArcInfo[lineGlyphIndex].angle = (float)((prevCenterToCenter / lineLength) * Math.PI); prevHalfWidth = halfWidth; } }
public SizeF MeasureString(string textg, Font font, RectangleF rect) { // As per documentation // https://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_text/dq_text.html#//apple_ref/doc/uid/TP30001066-CH213-TPXREF101 // // * Note * Not sure if we should save off the graphic state, set context transform to identity // and restore state to do the measurement. Something to be looked into. // context.TextPosition = rect.Location; // var startPos = context.TextPosition; // context.SelectFont ( font.nativeFont.PostScriptName, // font.SizeInPoints, // CGTextEncoding.MacRoman); // context.SetTextDrawingMode(CGTextDrawingMode.Invisible); // // context.SetCharacterSpacing(1); // var textMatrix = CGAffineTransform.MakeScale(1f,-1f); // context.TextMatrix = textMatrix; // // context.ShowTextAtPoint(rect.X, rect.Y, textg, textg.Length); // var endPos = context.TextPosition; // // var measure = new SizeF(endPos.X - startPos.X, font.nativeFont.CapHeightMetric); var atts = buildAttributedString(textg, font); // for now just a line not sure if this is going to work CTLine line = new CTLine(atts); var lineBounds = line.GetImageBounds(context); // Create and initialize some values from the bounds. float ascent; float descent; float leading; double lineWidth = line.GetTypographicBounds(out ascent, out descent, out leading); var measure = new SizeF((float)lineWidth, ascent + descent); return measure; }
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; 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 (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, "...")); float 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 = PointF.Empty; if (ellipsize) // we need to create a new CTLine here because the framesetter already truncated the text for the line new CTLine (CreateAttributedString (li, li.Text.Substring (line.StringRange.Location))) .GetTruncatedLine (li.Width.Value, CTLineTruncation.End, ellipsis).Draw (ctx); else line.Draw (ctx); ctx.TranslateCTM (0, lineHeight); } ctx.RestoreState (); } }
internal static void Draw(CGContext ctx, object layout, double x, double y) { LayoutInfo li = (LayoutInfo)layout; if (li.Framesetter == null) return; CGPath path = new CGPath (); path.AddRect (new RectangleF (0, 0, li.Width ?? float.MaxValue, li.Height ?? float.MaxValue)); CTFrame frame = li.Framesetter.GetFrame (new NSRange (0, li.Text.Length), path, null); CTLine ellipsis = null; bool ellipsize = li.Width.HasValue && li.TextTrimming == TextTrimming.WordElipsis; if (ellipsize) ellipsis = new CTLine (CreateAttributedString (li, "...")); float lineHeight = layoutManager.DefaultLineHeightForFont (li.Font); 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 = PointF.Empty; if (ellipsize) // we need to create a new CTLine here because the framesetter already truncated the text for the line new CTLine (CreateAttributedString (li, li.Text.Substring (line.StringRange.Location))) .GetTruncatedLine (li.Width.Value, CTLineTruncation.End, ellipsis).Draw (ctx); else line.Draw (ctx); ctx.TranslateCTM (0, lineHeight); } ctx.RestoreState (); }
public CTLine GetTruncatedLine(double width, CTLineTruncation truncationType, CTLine truncationToken) { var h = CTLineCreateTruncatedLine(Handle, width, truncationType, truncationToken == null ? IntPtr.Zero : truncationToken.Handle); if (h == IntPtr.Zero) { return(null); } return(new CTLine(h, true)); }
internal static CCSize MeasureString (string textg, CTFont font, CCRect rect) { var atts = buildAttributedString(textg, font); // for now just a line not sure if this is going to work CTLine line = new CTLine(atts); // Create and initialize some values from the bounds. nfloat ascent; nfloat descent; nfloat leading; double lineWidth = line.GetTypographicBounds(out ascent, out descent, out leading); var measure = new CCSize((float)(lineWidth + leading), (float)(ascent + descent)); return measure; }
// This only handles one character for right now internal static void GetCharABCWidthsFloat (char characters, CTFont font, out ABCFloat[] abc) { var atts = buildAttributedString(characters.ToString(), font); // for now just a line not sure if this is going to work CTLine line = new CTLine(atts); nfloat ascent; nfloat descent; nfloat leading; abc = new ABCFloat[1]; abc[0].abcfB = (float)line.GetTypographicBounds(out ascent, out descent, out leading); abc [0].abcfB += (float)leading; }
/// <summary> /// Measures the text. /// </summary> /// <param name="text">The text.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="fontWeight">The font weight.</param> /// <returns> /// The size of the text. /// </returns> public override OxySize MeasureText(string text, string fontFamily, double fontSize, double fontWeight) { if (string.IsNullOrEmpty (text) || fontFamily == null) { return OxySize.Empty; } var fontName = GetActualFontName (fontFamily, fontWeight); var font = this.GetCachedFont (fontName, (float)fontSize); using (var attributedString = new NSAttributedString (text, new CTStringAttributes { ForegroundColorFromContext = true, Font = font })) { using (var textLine = new CTLine (attributedString)) { float lineHeight, delta; this.GetFontMetrics (font, out lineHeight, out delta); // the text position must be set to get the correct bounds this.gctx.TextPosition = new PointF (0, 0); var bounds = textLine.GetImageBounds (this.gctx); var width = bounds.Left + bounds.Width; return new OxySize (width, lineHeight); } } }
/// <summary> /// Draws the text. /// </summary> /// <param name="p">The position of the text.</param> /// <param name="text">The text.</param> /// <param name="fill">The fill color.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotation angle.</param> /// <param name="halign">The horizontal alignment.</param> /// <param name="valign">The vertical alignment.</param> /// <param name="maxSize">The maximum size of the text.</param> public override void DrawText(ScreenPoint p, string text, OxyColor fill, string fontFamily, double fontSize, double fontWeight, double rotate, HorizontalAlignment halign, VerticalAlignment valign, OxySize? maxSize) { if (string.IsNullOrEmpty (text)) { return; } var fontName = GetActualFontName (fontFamily, fontWeight); var font = this.GetCachedFont (fontName, fontSize); using (var attributedString = new NSAttributedString (text, new CTStringAttributes { ForegroundColorFromContext = true, Font = font })) { using (var textLine = new CTLine (attributedString)) { float width; float height; this.gctx.TextPosition = new PointF (0, 0); float lineHeight, delta; this.GetFontMetrics (font, out lineHeight, out delta); var bounds = textLine.GetImageBounds (this.gctx); var x0 = 0; var y0 = delta; if (maxSize.HasValue || halign != HorizontalAlignment.Left || valign != VerticalAlignment.Bottom) { width = bounds.Left + bounds.Width; height = lineHeight; } else { width = height = 0f; } if (maxSize.HasValue) { if (width > maxSize.Value.Width) { width = (float)maxSize.Value.Width; } if (height > maxSize.Value.Height) { height = (float)maxSize.Value.Height; } } var dx = halign == HorizontalAlignment.Left ? 0d : (halign == HorizontalAlignment.Center ? -width * 0.5 : -width); var dy = valign == VerticalAlignment.Bottom ? 0d : (valign == VerticalAlignment.Middle ? height * 0.5 : height); this.SetFill (fill); this.SetAlias (false); this.gctx.SaveState (); this.gctx.TranslateCTM ((float)p.X, (float)p.Y); if (!rotate.Equals (0)) { this.gctx.RotateCTM ((float)(rotate / 180 * Math.PI)); } this.gctx.TranslateCTM ((float)dx + x0, (float)dy + y0); this.gctx.ScaleCTM (1f, -1f); if (maxSize.HasValue) { var clipRect = new RectangleF (-x0, y0, (float)Math.Ceiling (width), (float)Math.Ceiling (height)); this.gctx.ClipToRect (clipRect); } textLine.Draw (this.gctx); this.gctx.RestoreState (); } } }
public override void DrawRect (RectangleF dirtyRect) { // Don't draw if we don't have a font or a title. if (Font == null || Title == string.Empty) return; // Initialize the text matrix to a known value CGContext context = NSGraphicsContext.CurrentContext.GraphicsPort; context.TextMatrix = CGAffineTransform.MakeIdentity (); // Draw a white background NSColor.White.Set (); context.FillRect (dirtyRect); //CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)self.attributedString); CTLine line = new CTLine (AttributedString); int glyphCount = line.GlyphCount; if (glyphCount == 0) return; GlyphArcInfo[] glyphArcInfo = new GlyphArcInfo[glyphCount]; PrepareGlyphArcInfo (line, glyphCount, glyphArcInfo); // Move the origin from the lower left of the view nearer to its center. context.SaveState (); context.TranslateCTM (dirtyRect.GetMidX (), dirtyRect.GetMidY () - Radius / 2); // Stroke the arc in red for verification. context.BeginPath (); context.AddArc (0, 0, Radius, (float)Math.PI, 0, true); context.SetRGBStrokeColor (1, 0, 0, 1); context.StrokePath (); // Rotate the context 90 degrees counterclockwise. context.RotateCTM ((float)PI_2); // Now for the actual drawing. The angle offset for each glyph relative to the previous // glyph has already been calculated; with that information in hand, draw those glyphs // overstruck and centered over one another, making sure to rotate the context after each // glyph so the glyphs are spread along a semicircular path. PointF textPosition = new PointF (0, Radius); context.TextPosition = textPosition; var runArray = line.GetGlyphRuns (); var runCount = runArray.Count (); var glyphOffset = 0; var runIndex = 0; for (; runIndex < runCount; runIndex++) { var run = runArray[runIndex]; var runGlyphCount = run.GlyphCount; bool drawSubstitutedGlyphsManually = false; CTFont runFont = run.GetAttributes ().Font; // Determine if we need to draw substituted glyphs manually. Do so if the runFont is not // the same as the overall font. NSFont rrunFont = new NSFont (runFont.Handle); // used for comparison if (DimsSubstitutedGlyphs && Font != rrunFont) { drawSubstitutedGlyphsManually = true; } var runGlyphIndex = 0; for (; runGlyphIndex < runGlyphCount; runGlyphIndex++) { var glyphRange = new NSRange (runGlyphIndex, 1); context.RotateCTM (-(glyphArcInfo[runGlyphIndex + glyphOffset].angle)); // Center this glyph by moving left by half its width. var glyphWidth = glyphArcInfo[runGlyphIndex + glyphOffset].width; var halfGlyphWidth = glyphWidth / 2.0; var positionForThisGlyph = new PointF (textPosition.X - (float)halfGlyphWidth, textPosition.Y); // Glyphs are positioned relative to the text position for the line, so offset text position leftwards by this glyph's // width in preparation for the next glyph. textPosition.X -= glyphWidth; CGAffineTransform textMatrix = run.TextMatrix; textMatrix.x0 = positionForThisGlyph.X; textMatrix.y0 = positionForThisGlyph.Y; context.TextMatrix = textMatrix; if (!drawSubstitutedGlyphsManually) run.Draw (context, glyphRange); else { // We need to draw the glyphs manually in this case because we are effectively applying a graphics operation by // setting the context fill color. Normally we would use kCTForegroundColorAttributeName, but this does not apply // as we don't know the ranges for the colors in advance, and we wanted demonstrate how to manually draw. var cgFont = runFont.ToCGFont (); var glyph = run.GetGlyphs (glyphRange); var position = run.GetPositions (glyphRange); context.SetFont (cgFont); context.SetFontSize (runFont.Size); context.SetRGBFillColor (0.25f, 0.25f, 0.25f, 1); context.ShowGlyphsAtPositions (glyph, position, 1); } // Draw the glyph bounds if (ShowsGlyphBounds) { var glyphBounds = run.GetImageBounds (context, glyphRange); context.SetRGBStrokeColor (0, 0, 1, 1); context.StrokeRect (glyphBounds); } // Draw the bounding boxes defined by the line metrics if (ShowsLineMetrics) { var lineMetrics = new RectangleF (); float ascent = 0; float descent = 0; float leading = 0; run.GetTypographicBounds (glyphRange, out ascent, out descent, out leading); // The glyph is centered around the y-axis lineMetrics.Location = new PointF (-(float)halfGlyphWidth, positionForThisGlyph.Y - descent); lineMetrics.Size = new SizeF (glyphWidth, ascent + descent); context.SetRGBStrokeColor (0, 1, 0, 1); context.StrokeRect (lineMetrics); } } glyphOffset += runGlyphCount; } context.RestoreState (); }