How can I render curved text into a Bitmap?

前端 未结 3 536
无人及你
无人及你 2020-12-05 22:01

I am currently dynamically creating a bitmap and using the graphics object from the bitmap to draw a string on it like so:

System.Drawing.Graphics graph = Sy         


        
相关标签:
3条回答
  • 2020-12-05 22:28

    Unfortunatelly in GDI+ there is no way to attach Strings to a path (this is what you would be looking for).

    So the only way to do this is doing it "by hand". That means splitting up the string into characters and placing them based on your own path calculations.

    Unless you want to put a lot of work into this you should try to find a library (potentially complete GDI+ replacement) to do this or give up on your rainbow.

    With WPF you can render text on a path (see link for a howto)

    0 讨论(0)
  • 2020-12-05 22:45

    I recently had this problem (I was rendering text for printing onto CDs), so here's my solution:

    private void DrawCurvedText(Graphics graphics, string text, Point centre, float distanceFromCentreToBaseOfText, float radiansToTextCentre, Font font, Brush brush)
    {
        // Circumference for use later
        var circleCircumference = (float)(Math.PI * 2 * distanceFromCentreToBaseOfText);
    
        // Get the width of each character
        var characterWidths = GetCharacterWidths(graphics, text, font).ToArray();
    
        // The overall height of the string
        var characterHeight = graphics.MeasureString(text, font).Height;
    
        var textLength = characterWidths.Sum();
    
        // The string length above is the arc length we'll use for rendering the string. Work out the starting angle required to 
        // centre the text across the radiansToTextCentre.
        float fractionOfCircumference = textLength / circleCircumference;
    
        float currentCharacterRadians = radiansToTextCentre + (float)(Math.PI * fractionOfCircumference);
    
        for (int characterIndex = 0; characterIndex < text.Length; characterIndex++)
        {
            char @char = text[characterIndex];
    
            // Polar to cartesian
            float x = (float)(distanceFromCentreToBaseOfText * Math.Sin(currentCharacterRadians));
            float y = -(float)(distanceFromCentreToBaseOfText * Math.Cos(currentCharacterRadians));
    
            using (GraphicsPath characterPath = new GraphicsPath())
            {
                characterPath.AddString(@char.ToString(), font.FontFamily, (int)font.Style, font.Size, Point.Empty,
                                        StringFormat.GenericTypographic);
    
                var pathBounds = characterPath.GetBounds();
    
                // Transformation matrix to move the character to the correct location. 
                // Note that all actions on the Matrix class are prepended, so we apply them in reverse.
                var transform = new Matrix();
    
                // Translate to the final position
                transform.Translate(centre.X + x, centre.Y + y);
    
                // Rotate the character
                var rotationAngleDegrees = currentCharacterRadians * 180F / (float)Math.PI - 180F;
                transform.Rotate(rotationAngleDegrees);
    
                // Translate the character so the centre of its base is over the origin
                transform.Translate(-pathBounds.Width / 2F, -characterHeight);
    
                characterPath.Transform(transform);
    
                // Draw the character
                graphics.FillPath(brush, characterPath);
            }
    
            if (characterIndex != text.Length - 1)
            {
                // Move "currentCharacterRadians" on to the next character
                var distanceToNextChar = (characterWidths[characterIndex] + characterWidths[characterIndex + 1]) / 2F;
                float charFractionOfCircumference = distanceToNextChar / circleCircumference;
                currentCharacterRadians -= charFractionOfCircumference * (float)(2F * Math.PI);
            }
        }
    }
    
    private IEnumerable<float> GetCharacterWidths(Graphics graphics, string text, Font font)
    {
        // The length of a space. Necessary because a space measured using StringFormat.GenericTypographic has no width.
        // We can't use StringFormat.GenericDefault for the characters themselves, as it adds unwanted spacing.
        var spaceLength = graphics.MeasureString(" ", font, Point.Empty, StringFormat.GenericDefault).Width;
    
        return text.Select(c => c == ' ' ? spaceLength : graphics.MeasureString(c.ToString(), font, Point.Empty, StringFormat.GenericTypographic).Width);
    }
    

    screenshot of curved text in program output

    0 讨论(0)
  • 2020-12-05 22:46

    I think the only way is to render each character individually and use the

    Graphics.RotateTransform
    

    to rotate the text. You'll need to work out the rotation angle and rendering offset yourself. You can use the

    Graphics.MeasureCharacterRanges
    

    to get the size of each character.

    0 讨论(0)
提交回复
热议问题