예제 #1
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;
			nfloat ascent = 0;
			nfloat descent = 0;
			nfloat 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;
			}
		}
예제 #2
0
        partial void MeasureCharacterRangeImp(string str, Font font, StringFormat format, CharacterRange range, ref RectangleF ret)
        {
            var    attributedString = CreateAttributedString(str, font, format, null);
            CTLine line             = new CTLine(attributedString);

            CTRun[] runArray = line.GetGlyphRuns();
            if (runArray.Length > 0)
            {
                context.TextPosition = new CGPoint();
                ret = runArray[0].GetImageBounds(context, new NSRange(range.First, range.Length)).ToRectangle();
            }
        }
예제 #3
0
        public void GetBaseAdvancesAndOrigins()
        {
            TestRuntime.AssertXcodeVersion(11, 0);

            using (var attributedString = new NSAttributedString("Hello world."))
                using (var line = new CTLine(attributedString)) {
                    var runs = line.GetGlyphRuns();
                    Assert.That(runs.Length, Is.EqualTo(1), "runs");
                    runs [0].GetBaseAdvancesAndOrigins(new NSRange(0, 10), out var advances, out var origins);
                    Assert.IsNotNull(advances, "advances");
                    Assert.IsNotNull(origins, "origins");
                }
        }
예제 #4
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;
            nfloat ascent      = 0;
            nfloat descent     = 0;
            nfloat 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;
            }
        }
예제 #5
0
        public override void DrawRect(CGRect 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);
            CTLine line = new CTLine(AttributedString);

            int glyphCount = (int)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.SetStrokeColor(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.
             */
            CGPoint textPosition = new CGPoint(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.
                var    description = NSFontDescriptor.FromNameSize(runFont.FamilyName, runFont.Size);
                NSFont rrunFont    = NSFont.FromDescription(description, runFont.Size);
                // 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 CGPoint(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.SetFillColor(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.SetStrokeColor(0, 0, 1, 1);
                        context.StrokeRect(glyphBounds);
                    }

                    // Draw the bounding boxes defined by the line metrics
                    if (ShowsLineMetrics)
                    {
                        var    lineMetrics = new CGRect();
                        nfloat ascent      = 0;
                        nfloat descent     = 0;
                        nfloat leading     = 0;

                        run.GetTypographicBounds(glyphRange, out ascent, out descent, out leading);

                        // The glyph is centered around the y-axis
                        lineMetrics.Location = new CGPoint(-(float)halfGlyphWidth, positionForThisGlyph.Y - descent);
                        lineMetrics.Size     = new CGSize(glyphWidth, ascent + descent);
                        context.SetStrokeColor(0, 1, 0, 1);
                        context.StrokeRect(lineMetrics);
                    }
                }

                glyphOffset += (int)runGlyphCount;
            }

            context.RestoreState();
        }
예제 #6
0
        /// <summary>
        /// Draws text along a given line.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="color"></param>
        /// <param name="size"></param>
        /// <param name="text">Text.</param>
        protected override void DrawLineText(Target2DWrapper <CGContextWrapper> target, double[] xa, double[] ya, string text, int color,
                                             double size, int?haloColor, int?haloRadius, string fontName)
        {
            float textSize = this.ToPixels(size) * _scaleFactor;

            // transform first.
            double[] xTransformed = new double[xa.Length];
            double[] yTransformed = new double[ya.Length];
            for (int idx = 0; idx < xa.Length; idx++)
            {
                double[] transformed = this.Transform(xa[idx], ya[idx]);
                xTransformed[idx] = transformed[0];
                yTransformed[idx] = transformed[1];
            }

            // set the fill color as the regular text-color.
            target.Target.CGContext.InterpolationQuality = CGInterpolationQuality.High;
            target.Target.CGContext.SetAllowsFontSubpixelQuantization(true);
            target.Target.CGContext.SetAllowsFontSmoothing(true);
            target.Target.CGContext.SetAllowsAntialiasing(true);
            target.Target.CGContext.SetAllowsSubpixelPositioning(true);
            target.Target.CGContext.SetShouldAntialias(true);

            // get the glyhps/paths from the font.
            CTFont             font             = this.GetFont(fontName, textSize);
            CTStringAttributes stringAttributes = new CTStringAttributes
            {
                ForegroundColorFromContext = true,
                Font = font
            };
            NSAttributedString attributedString = new NSAttributedString(text, stringAttributes);
            CTLine             line             = new CTLine(attributedString);
            RectangleF         textBounds       = line.GetBounds(CTLineBoundsOptions.UseOpticalBounds);

            CTRun[] runs       = line.GetGlyphRuns();
            var     lineLength = Polyline2D.Length(xTransformed, yTransformed);

            // set the correct tranformations to draw the resulting paths.
            target.Target.CGContext.SaveState();
            //target.Target.CGContext.TranslateCTM (xPixels, yPixels);
            //target.Target.CGContext.ConcatCTM (new CGAffineTransform (1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f));
            foreach (CTRun run in runs)
            {
                ushort[] glyphs          = run.GetGlyphs();
                PointF[] positions       = run.GetPositions();
                float[]  characterWidths = new float[glyphs.Length];
                float    previous        = 0;
                float    textLength      = (float)positions[positions.Length - 1].X;
                //float textLength = (float)this.FromPixels(_target, _view, positions [positions.Length - 1].X);
                if (lineLength > textLength * 1.2f)
                {
                    for (int idx = 0; idx < characterWidths.Length - 1; idx++)
                    {
                        //characterWidths [idx] = (float)this.FromPixels(_target, _view, positions [idx + 1].X - previous);
                        characterWidths[idx] = (float)(positions[idx + 1].X - previous);
                        previous             = positions[idx + 1].X;
                    }
                    characterWidths[characterWidths.Length - 1] = characterWidths[characterWidths.Length - 2];
                    float characterHeight = textBounds.Height;

                    this.DrawLineTextSegment(target, xTransformed, yTransformed, glyphs, color, haloColor, haloRadius,
                                             lineLength / 2f, characterWidths, textLength, characterHeight, font);
                }
            }

            target.Target.CGContext.RestoreState();
        }
예제 #7
0
        /// <summary>
        /// Draws text.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="text"></param>
        /// <param name="size"></param>
        /// <param name="color">Color.</param>
        protected override void DrawText(Target2DWrapper <CGContextWrapper> target, double x, double y, string text, int color, double size,
                                         int?haloColor, int?haloRadius, string fontName)
        {
            double[] transformed = this.Transform(x, y);
            float    xPixels     = (float)transformed[0];
            float    yPixels     = (float)transformed[1];
            float    textSize    = this.ToPixels(size) * _scaleFactor;

            // get the glyhps/paths from the font.
            CTFont             font             = this.GetFont(fontName, textSize);
            CTStringAttributes stringAttributes = new CTStringAttributes
            {
                ForegroundColorFromContext = true,
                Font = font
            };
            NSAttributedString attributedString = new NSAttributedString(text, stringAttributes);
            CTLine             line             = new CTLine(attributedString);

            CTRun[] runs = line.GetGlyphRuns();

            // set the correct tranformations to draw the resulting paths.
            target.Target.CGContext.SaveState();
            target.Target.CGContext.TranslateCTM(xPixels, yPixels);
            target.Target.CGContext.ConcatCTM(new CGAffineTransform(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f));
            foreach (CTRun run in runs)
            {
                ushort[] glyphs    = run.GetGlyphs();
                PointF[] positions = run.GetPositions();

                float previousOffset = 0;
                for (int idx = 0; idx < glyphs.Length; idx++)
                {
                    CGPath path = font.GetPathForGlyph(glyphs[idx]);
                    target.Target.CGContext.TranslateCTM(positions[idx].X - previousOffset, 0);
                    previousOffset = positions[idx].X;
                    if (haloRadius.HasValue && haloColor.HasValue)
                    {                     // also draw the halo.
                        using (CGPath haloPath = path.CopyByStrokingPath(
                                   haloRadius.Value * 2, CGLineCap.Round, CGLineJoin.Round, 0))
                        {
                            SimpleColor haloSimpleColor = SimpleColor.FromArgb(haloColor.Value);
                            target.Target.CGContext.SetFillColor(haloSimpleColor.R / 256.0f, haloSimpleColor.G / 256.0f, haloSimpleColor.B / 256.0f,
                                                                 haloSimpleColor.A / 256.0f);
                            target.Target.CGContext.BeginPath();
                            target.Target.CGContext.AddPath(haloPath);
                            target.Target.CGContext.DrawPath(CGPathDrawingMode.Fill);
                        }
                    }

                    // set the fill color as the regular text-color.
                    SimpleColor simpleColor = SimpleColor.FromArgb(color);
                    target.Target.CGContext.SetFillColor(simpleColor.R / 256.0f, simpleColor.G / 256.0f, simpleColor.B / 256.0f,
                                                         simpleColor.A / 256.0f);

                    // draw the text paths.
                    target.Target.CGContext.BeginPath();
                    target.Target.CGContext.AddPath(path);
                    target.Target.CGContext.DrawPath(CGPathDrawingMode.Fill);
                }
            }

            target.Target.CGContext.RestoreState();
        }
        private List <CTLine> CreateLines(Font font, NSAttributedString attributedString, SizeF layoutBox, StringFormat format, float lineHeight)
        {
            bool noWrap     = (format.FormatFlags & StringFormatFlags.NoWrap) != 0;
            bool wholeLines = (format.FormatFlags & StringFormatFlags.LineLimit) != 0;

            using (var typesetter = new CTTypesetter(attributedString))
            {
                var   lines  = new List <CTLine>();
                int   start  = 0;
                int   length = (int)attributedString.Length;
                float y      = 0;
                while (start < length && y < layoutBox.Height && (!wholeLines || y + lineHeight <= layoutBox.Height))
                {
                    if (format.Trimming != StringTrimming.None)
                    {
                        // Keep the last line in full when trimming is enabled
                        bool lastLine;
                        if (!wholeLines)
                        {
                            lastLine = y + lineHeight >= layoutBox.Height;
                        }
                        else
                        {
                            lastLine = y + lineHeight + lineHeight > layoutBox.Height;
                        }
                        if (lastLine)
                        {
                            noWrap = true;
                        }
                    }

                    // 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"
                    var count = (int)typesetter.SuggestLineBreak(start, noWrap ? double.MaxValue : layoutBox.Width);
                    var line  = typesetter.GetLine(new NSRange(start, count));

                    // Note: trimming may return a null line i.e. not enough space for any characters
                    var trim = line;
                    switch (format.Trimming)
                    {
                    case StringTrimming.Character:
                        trim = line.GetTruncatedLine(noWrap ? nfloat.MaxValue : layoutBox.Width, CTLineTruncation.End, null);
                        break;

                    case StringTrimming.EllipsisWord:                             // Fall thru for now
                    case StringTrimming.EllipsisCharacter:
                        using (CTLine ellipsisToken = EllipsisToken(font, format))
                        {
                            // Trimming when not necessary causes bad results
                            if (line.GetBounds(CTLineBoundsOptions.UseOpticalBounds).Width <= layoutBox.Width)
                            {
                                break;
                            }

                            trim = line.GetTruncatedLine(layoutBox.Width, CTLineTruncation.End, ellipsisToken);

                            //Put back the first letter if we got ellipsis only.
                            if (trim == null || trim.GlyphCount == 1 && trim.GetGlyphRuns()[0].GetGlyphs()[0] == ellipsisToken.GetGlyphRuns()[0].GetGlyphs()[0])
                            {
                                var plain      = attributedString.Value.Substring(0, 1) + "\u2026";
                                var attributed = buildAttributedString(plain, font, format, lastBrushColor);
                                trim = new CTLine(attributed);
                            }
                        }
                        break;

                    case StringTrimming.EllipsisPath:
                        using (CTLine ellipsisToken = EllipsisToken(font, format))
                            trim = line.GetTruncatedLine(layoutBox.Width, CTLineTruncation.Middle, ellipsisToken) ?? line;
                        break;
                    }

                    if (trim != null)
                    {
                        lines.Add(trim);
                    }

                    if (line != trim)
                    {
                        line.Dispose();
                    }

                    start += (int)count;
                    y     += (float)lineHeight;
                }
                return(lines);
            }
        }
예제 #9
0
        void setupTextLayer(string l)
        {
            clearLayer();

            var singleLetter = l.Length == 1;

            var fontSize = singleLetter ? letterFontSize : messageFontSize;

            var font = new CTFont(new CTFontDescriptor(new CTFontDescriptorAttributes {
                Name = UIFont.SystemFontOfSize(fontSize).Name, Size = (float?)fontSize
            }), fontSize);

            var attrStr = new NSAttributedString(l, singleLetter ? letterStringAttributes : messageStringAttributes);

            var line = new CTLine(attrStr);

            var runArray = line.GetGlyphRuns();

            var letters = new CGPath();

            for (int runIndex = 0; runIndex < runArray.Length; runIndex++)
            {
                var run = runArray [runIndex];

                for (int runGlyphIndex = 0; runGlyphIndex < run.GlyphCount; runGlyphIndex++)
                {
                    var thisGlyphRange = new NSRange(runGlyphIndex, 1);

                    var glyph = run.GetGlyphs(thisGlyphRange).FirstOrDefault();

                    var position = run.GetPositions(thisGlyphRange).FirstOrDefault();

                    var letter = font.GetPathForGlyph(glyph);

                    var t = CGAffineTransform.MakeTranslation(position.X, position.Y);

                    if (letter != null)
                    {
                        letters.AddPath(t, letter);
                    }
                }
            }

            var path = new UIBezierPath();

            path.MoveTo(CGPoint.Empty);

            path.AppendPath(UIBezierPath.FromPath(letters));

            var layer = new CAShapeLayer();

            layer.Frame           = new CGRect(((animationLayer.Bounds.Width - path.Bounds.Width) / 2) - 10, (animationLayer.Bounds.Height - path.Bounds.Height) / 2, path.Bounds.Width, path.Bounds.Height);
            layer.GeometryFlipped = true;
            layer.Path            = path.CGPath;
            layer.StrokeColor     = UIColor.Blue.CGColor;
            layer.FillColor       = null;
            layer.LineWidth       = 3;
            layer.LineJoin        = CAShapeLayer.JoinBevel;

            animationLayer?.AddSublayer(layer);

            pathLayer = layer;
        }
예제 #10
0
		public override void DrawRect (CGRect 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);
			CTLine line = new CTLine (AttributedString);

			int glyphCount = (int)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.SetStrokeColor (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.
			*/
			CGPoint textPosition = new CGPoint (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.
				var description = NSFontDescriptor.FromNameSize (runFont.FamilyName, runFont.Size);
				NSFont rrunFont = NSFont.FromDescription (description, runFont.Size);
				// 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 CGPoint (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.SetFillColor (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.SetStrokeColor (0, 0, 1, 1);
						context.StrokeRect (glyphBounds);
					}

					// Draw the bounding boxes defined by the line metrics
					if (ShowsLineMetrics) {
						var lineMetrics = new CGRect ();
						nfloat ascent = 0;
						nfloat descent = 0;
						nfloat leading = 0;

						run.GetTypographicBounds (glyphRange, out ascent, out descent, out leading);

						// The glyph is centered around the y-axis
						lineMetrics.Location = new CGPoint (-(float)halfGlyphWidth, positionForThisGlyph.Y - descent);
						lineMetrics.Size = new CGSize (glyphWidth, ascent + descent);
						context.SetStrokeColor (0, 1, 0, 1);
						context.StrokeRect (lineMetrics);
					}
				}

				glyphOffset += (int)runGlyphCount;
			}

			context.RestoreState ();
		}
예제 #11
0
        /// <summary>
        /// Draws text along a given line.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="color"></param>
        /// <param name="size"></param>
        /// <param name="text">Text.</param>
        protected override void DrawLineText(Target2DWrapper <CGContextWrapper> target, double[] x, double[] y, string text, int color,
                                             double size, int?haloColor, int?haloRadius, string fontName)
        {
            double[] transformed = this.Tranform(x[0], y[0]);
            float    xPixels     = (float)transformed[0];
            float    yPixels     = (float)transformed[1];
            float    textSize    = this.ToPixels(size);

            // set the fill color as the regular text-color.
            SimpleColor simpleColor = SimpleColor.FromArgb(color);

            target.Target.CGContext.InterpolationQuality = CGInterpolationQuality.High;
            target.Target.CGContext.SetAllowsFontSubpixelQuantization(true);
            target.Target.CGContext.SetAllowsFontSmoothing(true);
            target.Target.CGContext.SetFillColor(simpleColor.R / 256.0f, simpleColor.G / 256.0f, simpleColor.B / 256.0f,
                                                 simpleColor.A / 256.0f);
            if (haloColor.HasValue)               // set the stroke color as the halo color.
            {
                SimpleColor haloSimpleColor = SimpleColor.FromArgb(haloColor.Value);
                target.Target.CGContext.SetStrokeColor(haloSimpleColor.R / 256.0f, haloSimpleColor.G / 256.0f, haloSimpleColor.B / 256.0f,
                                                       haloSimpleColor.A / 256.0f);
            }
            if (haloRadius.HasValue)               // set the halo radius as line width.
            {
                target.Target.CGContext.SetLineWidth(haloRadius.Value);
            }

            // get the glyhps/paths from the font.
            if (string.IsNullOrWhiteSpace(fontName))
            {
                fontName = "Arial";
            }
            CTFont             font             = new CTFont(fontName, textSize);
            CTStringAttributes stringAttributes = new CTStringAttributes {
                ForegroundColorFromContext = true,
                Font = font
            };
            NSAttributedString attributedString = new NSAttributedString(text, stringAttributes);
            CTLine             line             = new CTLine(attributedString);
            RectangleF         textBounds       = line.GetBounds(CTLineBoundsOptions.UseOpticalBounds);

            CTRun[] runs       = line.GetGlyphRuns();
            var     lineLength = Polyline2D.Length(x, y);

            // set the correct tranformations to draw the resulting paths.
            target.Target.CGContext.SaveState();
            //target.Target.CGContext.TranslateCTM (xPixels, yPixels);
            //target.Target.CGContext.ConcatCTM (new CGAffineTransform (1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f));
            foreach (CTRun run in runs)
            {
                ushort[] glyphs          = run.GetGlyphs();
                PointF[] positions       = run.GetPositions();
                float[]  characterWidths = new float[glyphs.Length];
                float    previous        = 0;
                float    textLength      = (float)this.FromPixels(_target, _view, positions [positions.Length - 1].X);
                if (lineLength > textLength * 1.2f)
                {
                    for (int idx = 0; idx < characterWidths.Length - 1; idx++)
                    {
                        characterWidths [idx] = (float)this.FromPixels(_target, _view, positions [idx + 1].X - previous);
                        previous = positions [idx + 1].X;
                    }
                    characterWidths [characterWidths.Length - 1] = characterWidths[characterWidths.Length - 2];
                    float characterHeight = textBounds.Height;

                    this.DrawLineTextSegment(target, x, y, glyphs, color, haloColor, haloRadius,
                                             lineLength / 2f, characterWidths, textLength, characterHeight, font);
                }
            }

            target.Target.CGContext.RestoreState();
        }