在Delphi XE2中将Windows元文件转换为位图时出现模糊文本

3

我正在使用Delphi XE2编写一个程序,需要将Windows增强型图元文件转换为位图。以下代码用于执行转换:

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;

对于某些图像文件,原始元文件中非常清晰的文本在最终位图中似乎有点模糊。不幸的是,我无法在此处发布示例图像,因为我没有足够的声望点数,但是如果您比较以下问题中的两个图像,就可以看到我所说的内容:

渲染元文件时,文本太大

我已在两台机器上进行了测试(均为Windows 7;一台32位,另一台64位)。该问题仅在64位机器上发生;在32位机器上转换完全相同的图像文件会生成具有正常外观文本的位图。

到目前为止,我尝试过以下事项:

  • 在64位机器上安装了32位机器上存在但64位机器上不存在的所有字体。生成的位图中的文本仍然模糊。

  • 尝试使用SynGdiPlus库而不是上述代码执行转换。生成的位图中的文本仍然模糊。

  • 尝试在EMF Explorer中打开原始图像文件。无论是否启用GDI+,显示的文本都是非模糊的。

有人有任何建议,可以解决这个问题吗?

以下是两个图像:

64位机器上制作的版本:

enter image description here

32位机器上制作的版本:

enter image description here

对于我处理的情况,我更喜欢第二个图像,即在32位机器上制作的图像。


使用上传网站上传元文件和一些图片。我们可以编辑问题以包含该图像。 - David Heffernan
1
@SertacAkyuz 您可以使用 EnumEnhMetaFile 将元文件播放到设备上。并修改所有的 LOGFONT 记录以相应地设置质量。 - David Heffernan
1
@PeterVonča 没错,我没有检查过,感谢你的指示!在64位机器上禁用ClearType确实可以解决问题。有没有一种方法可以按应用程序禁用ClearType?我认为我们的客户不会接受必须在整个系统范围内禁用ClearType才能使我们的程序正常工作;) - Tim
@David - 是的,那将是更好的选择,因为该文件可能还包含图形等内容。 - Sertac Akyuz
@Tim 你不想在整个应用程序中禁用它。那会让应用程序中的所有文本看起来很糟糕。你只想考虑禁用元文件输出。你可以按照我上面提到的方法来做。但可能有更简洁的方法。 - David Heffernan
显示剩余12条评论
1个回答

2

{1} 编辑:在此答案发布后,我们已经确定您不是创建原始元数据的人,请参阅“从现有MetaFile检索记录”部分。

{2} 编辑:针对您第二个识别字体设置的问题,请参阅第二个更新部分“检索字体结构记录”。

ClearType 是一个很麻烦的问题。由于集成了 ClearType 调整器,任何人都可以根据自己的意愿更改混合颜色的强度。因此,在考虑图像时,您不能依赖于每个系统的 ClearType 设置。

据我所知,唯一真正的解决方案是忽略自定义的 ClearType 渲染并使用预配置的渲染。


编辑 1:从现有 MetaFile 检索记录

您可以通过增强型 Metafile 操作更具体地说是通过EnumEnhMetaFile函数修改现有的 Metafile,该函数具有回调函数EnhMetaFileProc,您可以使用它来处理记录。

使用PlayEnhMetaFileRecord函数逐个解析和检查每个记录。有关如何编辑和修改特定记录的更多信息,请参见此处

在某个时刻,您将不得不使用下面的代码来修改现有的字体渲染。


编辑 2:检索字体结构记录

就像您可以通过EMREXTTEXTOUTA结构检索位置和文本一样,您也可以通过EMREXTCREATEFONTINDIRECTW结构检索使用的字体设置。此结构将允许您获取 LOGFONT 定义类型的字体记录,其中包含有关字体的大部分信息,但不包括使用的画刷。

如果您查看我的原始答案代码,您可以看到字体颜色是由所使用的笔刷定义的。因此,同样您必须使用不同的结构来获取该信息,即EMRCREATEBRUSHINDIRECT 结构。 LOGBRUSH32 类型成员包含有关所使用的笔刷的颜色和样式的信息。

原始答案

为了实现这一点,您必须使用 GDI+,因为 Delphi 封装的 Win32 增强型图元文件不完整。使用 Delphi GDI+ 库

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提供的代码,它运行得很好。但是我不确定我能在我们的程序中使用这种方法。我们需要转换的元文件是从Windows打印机池(.spl)文件中提取的页面,我们无法控制它们的内容。目标是识别每个页面上使用的颜色量,启用ClearType时包含在位图中的颜色像素会干扰此过程。我会看一下Delphi GID库,那可能会有用。 - Tim
@Tim,现在我们知道你不是直接创建文件,我已经更新了我的答案。我的代码仍然有效和有用,因为在某个时候当你编辑现有的MetaFile时,你将不得不调用这样的代码来改变文本的绘制。 - Peter
Peter,我已经按照你的建议尝试实现了这个功能。但是我遇到的一个问题是如何知道在EnhMetafileProc中使用哪些字体设置。我可以从tagEMREXTTEXTOUTA.emrtext.rclBounds获取位置,并且可以使用tagEMREXTTEXTOUTA.emrtext.offString和tagEMREXTTEXTOUTA.emrtext.nChars提取文本本身。但是如何提取字体类型、大小、颜色等呢? - Tim
@Tim,我又更新了我的答案,关于如何检索字体信息,请看“Edit2:检索字体结构记录部分”,希望你会觉得有用,干杯。 - Peter
非常感谢你,彼得!由于时间限制,我们最终用另一种方式解决了这个问题。但我现在认为我可以看到如何实现这个方法。我已将您的答案标记为已接受。最好的问候,蒂姆。 - Tim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接