Best fit label

May 20, 2015 at 9:51 PM
Edited May 21, 2015 at 8:57 AM
Single line text is not best fitted when Read("label:...") (fontpointsize's default value(0) is not changed) method is used to render text centered on an image, there are a lot of spaces at left, right, top and bottom of text. Image size is 10.000x5.000 px and text is a word of 3-6 chars. So, how could i get text best fitted to the edge of image without those paddings? As a solution, after rendering text, resizing image to get the text at desired size will not be a good idea because it will distort font i think.
Thanks
Coordinator
May 21, 2015 at 7:20 AM
Can you post a code example also? I don't understand what you are asking me.
May 21, 2015 at 8:39 AM
Edited May 21, 2015 at 9:06 AM
So, i have the following code, but the bounding box loop in CalculateFontPointSize iterates generally 7-8 times(may be less may be more) to find best font size so its too slow for big images. I am looking for an alternative with better performance.
                Rectangle rect2 = new Rectangle(6163, 3049, 4321, 2433);
                imgLabel = new MagickImage(Color.Transparent, rect2.Width, rect2.Height);
                
                imgLabel.Font = "Arial";
                imgLabel.BackgroundColor = white;
                imgLabel.FillColor = black;
                imgLabel.TextGravity = Gravity.Center;
                CalculateFontPointSize(imgLabel, text2, 100);

                imgLabel.Read("label:" + text2);

                imgOrig.Composite(imgLabel, new MagickGeometry(rect2));


        private void CalculateFontPointSize(MagickImage image, string text, double initialFontPointSize)
        {
            double calculatedWidth = 0;
            double calculatedHeight = 0;
            var metrics = image.FontTypeMetrics(text);
            double sizeInc = 500;
            double fontPointsize = initialFontPointSize, prevFontPointsize = 0,
                lastFitFontPointsize = initialFontPointSize;
            while (true)
            {
                prevFontPointsize = fontPointsize;
                fontPointsize += sizeInc;
                image.FontPointsize = fontPointsize;
                metrics = image.FontTypeMetrics(text);
                calculatedWidth = metrics.TextWidth;
                calculatedHeight = metrics.TextHeight;

                // Height calculation by FontTypeMetrics is not accurate so just use width.
                if (calculatedWidth > image.Width)
                {
                    fontPointsize = prevFontPointsize;
                    if (sizeInc > 100)
                        sizeInc -= 100;
                    else if (sizeInc > 10)
                        sizeInc -= 10;
                    else if (sizeInc > 1)
                        sizeInc--;
                    else // sizeInc = 1
                    {
                        image.FontPointsize = lastFitFontPointsize;
                        break;
                    }
                }
                if (calculatedWidth <= image.Width && calculatedHeight <= image.Height)
                {
                    lastFitFontPointsize = fontPointsize;
                }
            }

            int threshold = 10;
            if (calculatedWidth >= image.Width - threshold && calculatedWidth <= image.Width && 
                calculatedHeight >= image.Height - threshold && calculatedHeight <= image.Height)
                return;
            using (MagickImage tempImg = new MagickImage(image))
            {
                sizeInc = 200;
                
                fontPointsize = image.FontPointsize;
                prevFontPointsize = 0;

                tempImg.BackgroundColor = Color.White;
                tempImg.FillColor = Color.Black;
                MagickGeometry boundingBox = new MagickGeometry(0, 0);
                while (true)
                {
                    prevFontPointsize = fontPointsize;
                    fontPointsize += sizeInc;
                    tempImg.FontPointsize = fontPointsize;

                    tempImg.Read("label:" + text);
                    boundingBox = tempImg.BoundingBox;

                    if (boundingBox.Width > image.Width - threshold || boundingBox.Height > image.Height - threshold)
                    {
                        tempImg.Draw(new DrawableFillColor(Color.White),
                            new DrawableRectangle(new Rectangle(0, 0, tempImg.Width, tempImg.Height)));

                        fontPointsize = prevFontPointsize;
                        if (sizeInc > 100)
                            sizeInc -= 100;
                        else if (sizeInc > 50)
                            sizeInc -= 50;
                        else // sizeInc = 50
                        {
                            image.FontPointsize = lastFitFontPointsize;
                            break;
                        }
                    }
                    else
                    {
                        lastFitFontPointsize = fontPointsize;
                    }
                }
            }
        }
Coordinator
May 21, 2015 at 10:52 AM
Edited May 21, 2015 at 10:52 AM
You can let ImageMagick calculate the size for you if you specify the width and height when you read the label.
var imgLabel = new MagickImage();

imgLabel.Font = "Arial";
imgLabel.BackgroundColor = white;
imgLabel.FillColor = black;
imgLabel.TextGravity = Gravity.Center;

imgLabel.Read("label:" + text2, rect2.Width, rect2.Height);
May 21, 2015 at 11:41 AM
However, it does not make any difference to specify width and height as parameters or not. There are still 600px vert. and 400px horiz. paddings.
Coordinator
May 21, 2015 at 11:55 AM
What are you using as the text?
May 21, 2015 at 11:56 AM
"abc" for example.