mono / libgdiplus

C-based implementation of the GDI+ API
http://www.mono-project.com/
MIT License
334 stars 171 forks source link

Graphics.MeasureCharacterRanges does not work correctly on Linux #619

Open Yaroslav-Kuznetsov opened 4 years ago

Yaroslav-Kuznetsov commented 4 years ago

I'm using the System.Drawing.Common NuGet package and the latest version of libgdiplus (v6.0.x). On Windows, increasing the Bitmap resolution increases the accuracy at which the MeasureCharacterRanges method calculates text dimensions. On Linux, it actually scales the result which is not correct or expected. Bellow is the code snippet illustrating the difference:

static void Main(string[] args) {
            Bitmap bmp1 = new Bitmap(1, 1);
            bmp1.SetResolution(300, 300);
            Test(bmp1);

            Bitmap bmp2 = new Bitmap(1, 1);
            bmp2.SetResolution(600, 600);
            Test(bmp2);
        }
        static void Test(Bitmap bmp) {
            Graphics gr = Graphics.FromImage(bmp);
            gr.PageUnit = GraphicsUnit.Document;

            Font font = new Font("Tahoma", 9.75f, FontStyle.Regular);
            RectangleF bounds = new RectangleF(1000, 500, 200, 200); //rect in documents
            string text = "test";

            StringFormat sf = (StringFormat)StringFormat.GenericTypographic.Clone();
            sf.SetMeasurableCharacterRanges(new CharacterRange[] { new CharacterRange(0, 1) });
            sf.LineAlignment = StringAlignment.Center;
            Region[] regions = gr.MeasureCharacterRanges(text, font, bounds, sf);
            var letterBounds = regions[0].GetBounds(gr);
            Console.WriteLine($"image dpi = {bmp.HorizontalResolution} ; letterBounds = {letterBounds}");
        }

Here is the output this code produces:

//win result //image dpi = 300 ; letterBounds = {X=1000,Y=575.4775,Width=14.00451,Height=49.03564} //image dpi = 600 ; letterBounds = {X=1000,Y=575.5,Width=13.5,Height=49}

//linux docker result //image dpi = 300 ; letterBounds = {X=1000,Y=592,Width=5,Height=15} //image dpi = 600 ; letterBounds = {X=2000,Y=1392,Width=5,Height=15}

tig commented 4 years ago

Pretty sure MeasureString uses MeasureCharacterRanges. Therefore, it's entirely likely/probable that this is related to https://github.com/mono/libgdiplus/issues/623

Notice the values on Linux are always rounded?