none
Scaling of System.Drawing.Imaging.Metafile image leads to content losing in a scaled image RRS feed

  • Question

  • Hi All,

    Requirement:

    We are providing a functionality to convert a Microsoft Word document to an PDF from C# code behind, like Microsoft.Office.Interop.Word. In our end level requirement, we need to draw a string in System.Drawing.Imaging.Metafile image with System.Drawing.Bitmap graphics measured size. And then we have to draw this meta-file image into an new bitmap image with 3X scaling factor.

    So, to achieve this requirement, we have drawn a "TD0UJSV" text with "Barcode 39HR" font in a small meta-file image, then we draw the same meta-file image with 3X scaling factor in a bitmap image. In the resultant bitmap image, text "TD0UJSV" is preserved as "TD0UJS", here a character "V" is missing in a resultant bitmap image.

    Here the issue is "V" character missing in resultant bitmap image. This issue will not exist if we scale the same meta-file image up-to 2.59f. Also, this issue is not exist with some other fonts such as "Calibri", "Times New Roman".


    Things we are done while drawing a text in Meta-file:

    1. Measure a text size with required font by using bitmap graphics. (We cannot change this approach since MS Word measure all the text with bitmap graphics)
    2. Drawn a text with required font in meta-file image with meta graphics.

    Things we are done while drawing a meta-file image in a bitmap image:

    1. Created a large bitmap image to draw the scaled meta-file image.
    2. Drawn a meta-file image with 3X scaling factor.

    Reason for the issue:

    Measuring a text with Bitmap Graphics return a different width than measuring the same text with meta-file graphics for this particular font "Barcode 39HR". Such as meta-file graphics return a "TD0UJSV" text with as "73.92383" but bitmap graphics return "73.92". We suspect this value difference is causes of this issue, because this issue will not exist if we draw the text with meta-file graphics measured width.

    Could you please suggest us a solution to avoid the content "V" losing issue in a resultant bitmap image with out changing a above mandatory process.

    Please find a code that we are using in our end,

            /// <summary>
            /// Graphics of bitmap image
            /// </summary>
            internal Graphics GraphicsBmp
            {
                get
                {
                    Bitmap bmp = new Bitmap(1, 1);
                    Graphics m_graphicsBmp = System.Drawing.Graphics.FromImage(bmp);
                    bmp.SetResolution(120, 120);
                    m_graphicsBmp.PageUnit = GraphicsUnit.Point;
                    return m_graphicsBmp;
                }
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                //Required metafile image size
                int metaImageWidth = 793;
                int metaImageHeight = 1122;
    
                //Create a metafile image with specified size
                Image metaImage = CreateMetafile(metaImageWidth, metaImageHeight);
    
                //Create a brush and string format to measure and draw the text
                Brush brush = Brushes.Black;
                Font font = new Font("Barcode 39HR", 14);
                StringFormat stringFormat = new StringFormat(StringFormat.GenericTypographic);
                stringFormat.FormatFlags &= ~StringFormatFlags.LineLimit;
                stringFormat.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
                stringFormat.Trimming = StringTrimming.Word;
                //Text to be drawn
                string text = "TD0UJSV";
    
                //Get the grapics of metafile image to draw the text
                using (Graphics g = Graphics.FromImage(metaImage))
                {
                    g.PageUnit = GraphicsUnit.Point;
                    g.Clear(Color.White);
    
                    //Measure the text size with Bitmap graphics
                    SizeF size = GraphicsBmp.MeasureString(text, font, new PointF(0, 0), stringFormat); //Retrun text width as 73.92
    
                    //Issue will not exist if we measure the same text with metafile graphics like below
                    //SizeF size = g.MeasureString(text, font, new PointF(0, 0), stringFormat); //Return text width as 73.92383
    
                    //Draw the text with Metafile graphics
                    g.DrawString(text, font, brush, new RectangleF(100, 100, size.Width, size.Height), stringFormat);
                    //Reset and Dispose a metafile graphics
                    g.ResetTransform();
                    g.Dispose();
                }
    
                //Create a bitmap image to draw the above created metafile image with 3X scaling factor
                Image bitmapImage = new Bitmap(metaImage.Width * 3, metaImage.Height * 3);
                //Get the graphics of bitmap image to draw the metafile image
                using (Graphics graphics = Graphics.FromImage(bitmapImage))
                {                
                    float factor = 3f; //Issue will not exist if we scale the metafile image upto 2.59f.
                    //Draw the above created metafile image with 3X scaling factor
                    graphics.DrawImage(metaImage, 0, 0, metaImage.Width * factor, metaImage.Height * factor);
                    //Dispose the bitmap image graphics
                    graphics.Dispose();
                }
    
                //Save the bitmap image
                bitmapImage.Save("Bitmap_Output.jpg", ImageFormat.Jpeg);
                //Dispose the bitmap image
                bitmapImage.Dispose();
                System.Diagnostics.Process.Start("Bitmap_Output.jpg");
            }
    
            /// <summary>
            /// Creates new meta file with specified size.
            /// </summary>
            private Image CreateMetafile(float width, float height)
            {
                Image result;
                MemoryStream stream = new MemoryStream();
                using (Bitmap bitmap = new Bitmap((int)width, (int)height))
                {
                    using (Graphics g = Graphics.FromImage(bitmap))
                    {
                        IntPtr hdc = g.GetHdc();
                        System.Drawing.RectangleF rect = new System.Drawing.RectangleF(0, 0, width, height);
                        result = new System.Drawing.Imaging.Metafile(stream, hdc, rect, MetafileFrameUnit.Pixel, EmfType.EmfPlusDual);
                        g.ReleaseHdc(hdc);
                        g.Dispose();
                    }
                }
                return result;
            }




    Monday, July 22, 2019 6:50 AM

All replies

  • Hi Ramaraj Marimuthu Alagarsamy, 

    Thank you for posting here.

    I create a WinForm application and make a test based on your code, but I get the same result between ‘GraphicsBmp.MeasureString()’ method and ‘g. MeasureString()’ method.

    Here are results of my test:

    Result of GraphicsBmp.MeasureString():

    Result of g.MeasureString():

    Please provide more details about your question.

    We are waiting for your update.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.



    Tuesday, July 23, 2019 9:32 AM
    Moderator
  • Hi,

    The barcode font available from: https://www.1001freefonts.com/idautomation.com-code-39-barcode-font-free-version.font reproduces this issue.  The "V" is truncated.

    The font is called:  IDAutomationHC39M Free Version

    Another font available from: https://www.inflowinventory.com/blog/code-39-font-archon-free-barcode/ seems to behave properly.  The "V" is preserved.


    Ian Yates Technical Manager Medical IT Pty Ltd PO Box 501, Carina QLD 4152 Australia Web: www.medicalit.com.au

    Wednesday, July 24, 2019 3:51 AM
  • Dear Xingyu,

    Further to my note above...

    Even when using the Calibri font, and trying this code, I find that the measurement returned from the Graphics instance associated with a Bitmap, versus the Graphics instance associated with a metafile, return slightly different widths.

    However your screenshot shows they're exactly the same.

    I can see why.  Since you're running the code and leaving the font set to "Barcode 39HR", you're getting some system fallback font.  That does measure identically via the two methods.

    Just switch Barcode 39HR to "Calibri" and you'll see that the numbers are now slightly different.  On my Windows 10 PC (running 1809) I get

    50.35352  (for GraphicsBmp)

    vs

    49.98438   (for g - the metafile one)

    This is wrong in the opposite direction - in that for the barcode fonts, the bitmap calculated text size is slightly smaller, leading to truncated text when that size is used to write to the metafile.

    For calibri, the bitmap calculated size is slightly bigger, so this doesn't cause text truncation when that size is used to write to the metafile.

    Is this a text measurement that's influenced by anti-aliasing, subpixel rendering, pixel offsets, interpolation mode or other propeties that can be set on the Graphics instance?  I've experimented with these but couldn't find they made a difference, although I wasn't exhaustive with my tests.


    Ian Yates Technical Manager Medical IT Pty Ltd PO Box 501, Carina QLD 4152 Australia Web: www.medicalit.com.au

    Wednesday, July 24, 2019 6:55 AM
  • Hi Xingyu Zhao,

    Thanks for the reply.

    We suspect the required font "Barcode 39HR" is not installed in your machine. So, that font instance created with default font "Microsoft Sans Serif" (Which works perfectly in our end). Since i am a new user to MSDN forum, i cannot upload you a required font link to investigate this issue. 

    However, as suggested in Ian Yates previous update, please use the another 3rd party font "IDAutomationHC39M Free Version" to reproduce the same issue in your end.

    Note: You have to replace the font name "Barcode 39HR" with "IDAutomationHC39M Free Version" font name in the above code snippet.

    Please try this and provide a solution for this issue. Thanks in advance.

    Regards,
    Ramaraj Marimuthu.


    Wednesday, July 24, 2019 7:01 AM
  • Hi Ian Yates,

    Thank you for your interest in tracking the issue.

    Please share him a exact font "Barcode 39HR" if possible, to get the optimal solution from Microsoft team.

    Regards,
    Ramaraj Marimuthu.
    Wednesday, July 24, 2019 7:04 AM
  • I think I've found a potential fix.  It doesn't explain the difference in measurements, but the "V" is no longer dropped off

    After    

    g.Clear(Color.White);

    Add the line

    g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.TextRenderingHintAntiAlias;

    The "V" now appears.

    In fact, any TextRenderingHint values, apart from SystemDefault & ClearTypeGridFit, seem to work.

    Looking at the top of this Stackoverflow answer: https://stackoverflow.com/questions/1203087/why-is-graphics-measurestring-returning-a-higher-than-expected-number/6404811#6404811

    It seems that the right way to do this, in a resolution independent way, and avoid problems with grid fitting, is to use the anti alias hint since DrawString wraps GDI+ for drawing.

    That stackoverflow answer references a page that's no longer available, but the Wayback machine has it...

    https://web.archive.org/web/20100612192943/http://windowsclient.net/articles/gdiptext.aspx


    Ian Yates Technical Manager Medical IT Pty Ltd PO Box 501, Carina QLD 4152 Australia Web: www.medicalit.com.au

    Wednesday, July 24, 2019 7:31 AM
  • The exact font in question may be sourced from https://www.dox.com.au/DoxUpgrades/barcode39hr.zip

    However, as I said, the same GDI+ issue reproduces with the free font from ID Automation - both work well once the TextRenderingHint is set for AntiAlias.  Should that be the case?


    Ian Yates Technical Manager Medical IT Pty Ltd PO Box 501, Carina QLD 4152 Australia Web: www.medicalit.com.au

    Wednesday, July 24, 2019 1:32 PM
  • Hi Team,

    Any update on this.

    Thanks,
    Ramaraj Marimuthu.



    Monday, July 29, 2019 11:01 AM
  • Hi Ramaraj Marimuthu Alagarsamy,

    Apologies for the delayed response.

    I have downloaded "Barcode 39HR" font and reporduce your problem after making a test.

    I find that g.MeasureString() method returns correct result, and I suggest you use it to measure the string.

    If I have any misunderstanding, please provide more information.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, July 30, 2019 7:40 AM
    Moderator
  • Hi Xingyu Zhao,

    Thank you for the reply.

    Could you please explain us, how you state that  g.MeasureString() method returns a correct result. Because we need a clear explanation to proceed with your solution.

    Is any solution to resolve a reported issue with Bitmap graphics (GraphicsBmp.MeasureString()) instead of MetaFile graphics (g.MeasureString())?

    Thanks,
    Ramaraj Marimuthu


    Friday, August 2, 2019 10:31 AM
  • I've found a potential fix

    Rather than using g.MeasureString()  (or GraphicsBmp.MeasureString())

    Instead use MeasureCharacterRanges - it works identically, for me, for both bitmap & metafile graphics.

    That is, to get a SizeF, use this function

    public static SizeF MeasureDisplayString(Graphics graphics, string text, Font font, StringFormat format)
    {

      RectangleF rect = new RectangleF(0, 0, 0, 0);

      var ranges = new[] { new CharacterRange(0, text.Length)};
      var regions = new Region[1];

      format.SetMeasurableCharacterRanges(ranges);

      regions = graphics.MeasureCharacterRanges(text, font, rect, format);
      var result = regions[0].GetBounds(graphics);

      return result.Size;

    }

    Then invoke it like this in place of the calls to MeasureString

    SizeF size = MesaureDisplayString(GraphicsBmp, text, font, stringFormat);

    You get an identical result to when you call

    SizeF size = MesaureDisplayString(g, text, font, stringFormat);



    Ian Yates Technical Manager Medical IT Pty Ltd PO Box 501, Carina QLD 4152 Australia Web: www.medicalit.com.au

    Sunday, August 4, 2019 11:17 PM
  • Hi Ramaraj Marimuthu Alagarsamy,

    Thanks for your feedback.

    I have found another soultion may help you to measure the string.

    Here's my code:

    private void button1_Click(object sender, EventArgs e) { Font font = new Font("Barcode 39HR", 14); string text = "TD0UJSV"; TextFormatFlags flags = TextFormatFlags.Top | TextFormatFlags.Left | TextFormatFlags.NoPadding | TextFormatFlags.NoClipping; Size BitmapSize = TextRenderer.MeasureText(text, font, Size.Empty, flags); using (Bitmap bitmap = new Bitmap(BitmapSize.Width, BitmapSize.Height, PixelFormat.Format24bppRgb)) using (Graphics graphics = Graphics.FromImage(bitmap)) { BitmapSize = TextRenderer.MeasureText(graphics, text, font, new Size(bitmap.Width, bitmap.Height), flags); TextRenderer.DrawText(graphics, text, font, Point.Empty, Color.Black, Color.Black, flags); }

    System.Diagnostics.Process.Start("Bitmap_Output.jpg"); }

    Result:

    Besides, I find a related reference.

    Drawing a Long String on to a Bitmap results in Drawing Issues 

    Hope it can help you.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Wednesday, August 7, 2019 8:27 AM
    Moderator