3

I am working on a program in Delphi XE2 which needs to be able to convert Windows enhanced metafiles to bitmaps. The following code is used to perform the conversion:

procedure TForm1.Button8Click(Sender: TObject);
var
  Bitmap : TBitmap;
  Metafile : TMetafile;
begin
  Metafile := TMetafile.Create();
  Bitmap := TBitmap.Create;
  try
    Metafile.LoadFromFile(Edit1.Text);
    Bitmap.Width := Metafile.Width;
    Bitmap.height:= Metafile.Height;
    Bitmap.Canvas.Draw(0,0,Metafile);
    Bitmap.SaveToFile(ChangeFileExt(Edit1.Text, '.bmp'));
  finally
    Bitmap.Free();
    Metafile.Free();
  end;
end;

With certain image files the text, which was quite clear in the original metafile, appears somewhat blurry in the final bitmap. Unfortunately I cannot post an example image here because I don't have sufficient reputation points, however you can see the sort of thing I am talking about if you compare the two images in the following question:

when rendering metafile, the texts are too large

I have tested this on two machines (both Windows 7; one 32-bit, the other 64-bit). The problem only occurs on the 64-bit machine; converting exactly the same image file on the 32-bit machine results in a bitmap with normal-looking text.

Things I have tried so far:

  • Installed all fonts that were present on the 32-bit machine but not on the 64-bit machine onto the 64-bit machine. The text in the generated bitmap was still blurry.

  • Tried performing the conversion using the SynGdiPlus library instead of the above code. The text in the generated bitmap was still blurry.

  • Tried opening the original image file in EMF Explorer. Regardless of whether GDI+ is enabled or not, the text displayed there is non-blurry.

Does anybody have any suggestions as to how I could solve this problem?

Here are the two images:

The version made on the 64 bit machine:

enter image description here

The version made on the 32 bit machine:

enter image description here

For the scenario I am dealing with, I prefer the second image, that made on the 32 bit machine.

Community
  • 1
  • 1
Tim
  • 375
  • 1
  • 4
  • 18
  • Use an upload site to upload the metafile and some images. We can edit the question to include the image. – David Heffernan Jun 02 '13 at 09:28
  • Thanks David, here are the images: http://files.etvdzs.info/loremipsum.wmf http://files.etvdzs.info/loremipsum32bit.bmp http://files.etvdzs.info/loremipsum64bit.bmp – Tim Jun 02 '13 at 09:40
  • OK, I edited the post. Did I understand right? Are my captions accurate? Now, as to what's happening, clearly one version uses anti-aliasing (probably ClearType), and the other version, the bad one, does not have any anti-aliasing. Not sure why, mind you. Metafiles and GDI are categorically not an area where I claim expertise. – David Heffernan Jun 02 '13 at 10:14
  • Thanks for editing the post. Actually the version made on the 32-bit machine is the "good" one (from my point of view), the version made on the 64-bit machine is the "bad" one. Thanks for the tip about anti-aliasing, I'll look into it. – Tim Jun 02 '13 at 10:23
  • @Tim: The '64-bit' version looks much better than the '32-bit' version. Antialiasing is a feature, not a bug, and a great one, too. On my system, the text in the 64-bit image looks like all other (non-bitmapped) text on my system, while the 32-bit image looks like a relic from the Windows 9x era. – Andreas Rejbrand Jun 02 '13 at 11:23
  • Anyhow, I don't find it particularly strange that the versions differ. After all, a metafile is only a collection of GDI instructions, like 'draw this string here' and 'draw a line there', so details are to some extent up to the OS running the application. – Andreas Rejbrand Jun 02 '13 at 11:34
  • Thanks Andreas for the reply. Unfortunately the bitmap generated on the 64-bit machine is problematic for us because of the presence of colored pixels. If you zoom in closely on the two images, you will see that the text in the "64-bit" version contains brown, blue, purple etc pixels, whereas the text in the "32-bit" version contains only black pixels. I don't have anything against anti-aliasing in general, but is there a way to disable it in this case? – Tim Jun 02 '13 at 12:36
  • @Tim - You can set your bitmap's `PixelFormat` to 'pf1bit' before drawing on its canvas. – Sertac Akyuz Jun 02 '13 at 13:07
  • @Tim , Did you not check if ClearType is disabled on your 32-bit machine as David suggested? Check if ClearType is enable : Control Panel > Display > Adjust ClearType. – Peter Jun 02 '13 at 13:08
  • @SertacAkyuz Will that disable anti-aliasing. I guess it must do so. However, it may be easier to disable it a more explicit way. – David Heffernan Jun 02 '13 at 14:12
  • @David - It will definitely disable anti-alising, however the result might not be exactly the same when the text has been drawn without anti-aliasing (and clear type). A test output here shows the resulting bitmap is similar to the 32bit bitmap in the question, but I'm not sure it will be the same everywhere. I know of no way of disabling anti-aliasing/clear type during a PlayEnhMetaFile call. The proper way to handle this may be to create a palette for the meta file. – Sertac Akyuz Jun 02 '13 at 14:19
  • 1
    @SertacAkyuz You could use `EnumEnhMetaFile` to play the metafile onto a device. And modify all the `LOGFONT` records to set the quality accordingly. – David Heffernan Jun 02 '13 at 14:21
  • 1
    @PeterVonča Quite right, I hadn't checked that, thanks for the instructions! Disabling ClearType on the 64-bit machine does indeed fix the problem. Is there a way to disable ClearType on a per-application basis? I don't think our customers will accept having to disable ClearType on a system-wide basis just so that our program works ;) – Tim Jun 02 '13 at 14:22
  • @David - Yeah, that would be a better option as the file may contain also graphics etc.. – Sertac Akyuz Jun 02 '13 at 14:23
  • @Tim You don't want to disable it for your entire app. That will make all the text in your app look dire. You only want to contemplate disabling it for the metafile output. Which you can do the way I mentioned above. But there may be a cleaner way. – David Heffernan Jun 02 '13 at 14:25
  • Thanks for the replies. Just to be clear: I am not trying to generate a black-and-white image. If the original metafile contains e.g. green text, then this should be represented with green pixels in the final bitmap. If the original metafile contains black text, then this should be represented with black pixels (some grey pixels would also be acceptable, but not green, red, blue etc) in the final bitmap. Thanks for the suggestion with EnumEnhMetaFile, I'll look into it. – Tim Jun 03 '13 at 02:19
  • @Tim: off-topic, but are you Tasmanian? Your user icon looks familiar :) – David Jun 03 '13 at 09:08

1 Answers1

2

{1} Edit: Since we've established that you're not the one creating the original meta after this answer has been posted, please refer to section Retrieving records from an existing MetaFile.

{2} Edit: Per your second problem with identifying font settings, please refer to second update section Retrieving font structure record.

ClearType is quite a pickle. Anyone can change the intensity of the blending color as they wish, thanks to the integrated ClearType tuner. With Images in mind you therefor cannot rely on the ClearType settings of each individual system.

AFAIK the only real solution is to ignore the custom ClearType rendering and use a pre-configured one.


Edit 1 : Retrieving records from an existing MetaFile

You can modify an existing Metafile through Enhanced Metafile Operations more specifically through EnumEnhMetaFile function which has a callback function EnhMetaFileProc that you can use to process the records.

Use the PlayEnhMetaFileRecord function to parse through and examine each record at a time. More on how to edit & modify a specific record, see here.

At some point down the line you will have to use the code below to modify the existing font rendering.


Edit 2 : Retrieving font structure record

Just like you can retrieve the position and text via EMREXTTEXTOUTA structure, you can also retrieve the Font settings used via the EMREXTCREATEFONTINDIRECTW structure. This structure will allow you to get the font record of LOGFONT defined type which contains most info regarding the font, except for the brush used.

If you look at my original answer code you can see that the color of the font is defined by the brush used. so equally you have to use a different structure to obtain that info, the EMRCREATEBRUSHINDIRECT structure. The LOGBRUSH32 typed member contains info about the color and style of the brush used.


Original Answer

In order to accomplish this you have to resort to using GDI+ since the delphi encapsulation of the Win32 Enhanced metafile is not complete. Use the Delphi GDI+ library.

uses 
 GDIPlus,GDIPlusHelpers

const
  Deftext = 'Lorem ipsum dolor sit amet,'
  +sLineBreak+'consectetur adipisicing elit, sed do eiusmod tempor incididunt'
  +sLineBreak+'ut labore et dolore magna aliqua.';

procedure CreateEmF(const EmfFileName : TFileName);
var
  Graphics : IGPGraphics;
  xBrush: IGPBrush;
  xFontFamily: IGPFontFamily;
  xFont: IGPFont;
  DC: HDC;
  Metafile: IGPMetafile;

begin

  xBrush := TGPSolidBrush.Create(TGPColor.Create(0, 0, 0));
  xFontFamily := TGPFontFamily.Create('Segoe UI');
  xFont := TGPFont.Create(xFontFamily, 12, FontStyleRegular, UnitPoint{UnitPixel});

  DC := GetDC(0);

  try

    Metafile := TGPMetafile.Create(EmfFileName, DC);
    Graphics := TGPGraphics.Create(Metafile);

    {
      Use Presets instead of the DefaultSystemRendering 

      TextRenderingHintAntiAliasGridFit - Preset ClearType Rendering
      TextRenderingHintSingleBitPerPixelGridFit - Preset Normal Rendering
    }

    Graphics.TextRenderingHint := TextRenderingHintAntiAliasGridFit;
    Graphics.DrawString(Deftext, xFont, TGPPointF.Create(50, 50), xBrush);

    Graphics := nil;

  finally
    ReleaseDC(0, DC);
  end;

end;

procedure ConvertEmf2Bmp(const EMFFileName, BMPFileName: TFileName) ;
var
  MetaFile : TMetafile;
  Bitmap : TBitmap;
begin
  Metafile := TMetaFile.Create;
  Bitmap := TBitmap.Create;
  try
    MetaFile.LoadFromFile(EMFFileName);
    with Bitmap do
    begin
      SetSize(MetaFile.Width,MetaFile.Height);
      Canvas.Draw(0, 0, MetaFile) ;
      SaveToFile(BMPFileName) ;
    end;
  finally
    Bitmap.Free;
    MetaFile.Free;
  end;
end;
Peter
  • 2,977
  • 1
  • 17
  • 29
  • Thanks Peter for the code, it works nicely. However I'm not sure that I can use this approach in our program. The metafiles that we need to convert are extracted pages from a Windows printer spool (.spl) file, and we have no control over their content. The goal is to identify the amount of color used on each page, and the color pixels that are included in the bitmaps when ClearType is enabled interfere with this. I'll have a look at the Delphi GID Library, that could be useful. – Tim Jun 03 '13 at 02:28
  • @Tim , I've updated my answer now that we know that you're not directly creating the file yourself. My code is still valid and useful because somewhere down the line when you are editing an existing MetaFile you will have to call such code to change the drawing of the text. – Peter Jun 03 '13 at 07:31
  • Peter, I've tried to implement this along the lines you suggested. One thing I'm having trouble with is knowing which font settings to use in EnhMetafileProc. The position I can get from tagEMREXTTEXTOUTA.emrtext.rclBounds and the text itself I can extract using tagEMREXTTEXTOUTA.emrtext.offString and tagEMREXTTEXTOUTA.emrtext.nChars. But how do I extract the font type, size, color etc.? – Tim Jun 05 '13 at 07:10
  • @Tim, I've updated my answer again on how to retrieve font info, take a look at "Edit2 : Retrieving font structure record section", I hope you find it useful, cheers. – Peter Jun 05 '13 at 10:12
  • Thanks very much Peter! Due to time constraints we ended up solving the problem a different way. But I think I can see now how one *could* implement this. I have marked your answer as accepted. Best regards, Tim. – Tim Jun 10 '13 at 00:35