我正在创建一些.wmf文件,但其中一些似乎已损坏,无法在任何元文件查看器中显示.经过一些反复试验,我发现问题是由它们的尺寸引起的.如果我按比例缩放相同的图形以减小尺寸,则会显示.
现在,我想知道绘图的大小是否存在限制,或者问题是否是其他问题.我知道这些文件有16位数据结构,所以我猜每个维度的限制是2 ^ 16个单位,(如果签名则为2 ^ 15).但在我的测试中它大约是25,000.所以我不能依赖这个值,因为限制可以是任何东西(宽度*高度可能,或者图纸的分辨率可能会影响它).我找不到关于描述这个的.wmf文件的可靠资源.
以下是显示问题的示例代码:
procedure DrawWMF(const Rect: TRect; const Scale: Double; FileName: string); var Metafile: TMetafile; Canvas: TMetafileCanvas; W, H: Integer; begin W := Round(Rect.Width * Scale); H := Round(Rect.Height * Scale); Metafile := TMetafile.Create; Metafile.SetSize(W, H); Canvas := TMetafileCanvas.Create(Metafile, 0); Canvas.LineTo(W, H); Canvas.Free; Metafile.SaveToFile(FileName); Metafile.Free; end; procedure TForm1.Button1Click(Sender: TObject); const Dim = 40000; begin DrawWMF(Rect(0, 0, Dim, Dim), 1.0, 'Original.wmf'); DrawWMF(Rect(0, 0, Dim, Dim), 0.5, 'Scaled.wmf'); try Image1.Picture.LoadFromFile('Original.wmf'); except Image1.Picture.Assign(nil); end; try Image2.Picture.LoadFromFile('Scaled.wmf'); except Image2.Picture.Assign(nil); end; end;
PS:我知道,设置Metafile.Enhanced
于True
并将其保存为一个文件的.emf就能解决问题,但我生成文件目标应用程序不支持增强型图元文件.
编辑: 如下面的答案所述,这里有两个不同的问题:
主要问题是关于文件本身,它对每个维度有2 ^ 15的限制.如果图形的宽度或高度超过此值,delphi将写入损坏的文件.您可以在Sertac的答案中找到更多详细信息.
第二个问题是关于加载文件TImage
.当您想要在delphi VCL应用程序中显示图像时,还有另一个限制.这个是系统相关的,并且与将要绘制绘图的DC的dpi相关.汤姆的回答详细描述了这一点.传递0.7如Scale
来DrawWMF
(上面的代码示例)抄录我的电脑上这种情况.生成的文件正常,可以与其他Metafile查看器(我使用MS Office Picture Manager)查看,但VCL无法显示它,但是,加载文件时不会引发异常.
你的限制是32767.
跟踪VCL代码,输出文件被破坏TMetafile.WriteWMFStream
.VCL写入WmfPlaceableFileHeader
(TMetafileHeader
在VCL中)记录,然后调用GetWinMetaFileBits
将'emf'记录转换为'wmf'记录.如果边界矩形的任何维度(在调用时使用CreateEnhMetaFile
)大于32767,则此函数将失败.不检查返回值,VCL不会引发任何异常并仅使用22个字节关闭文件 - 仅具有"可放置标头" .
即使对于小于32767的维度,"可放置的标题"也可能有错误的值(阅读有关汤姆答案的原因和含义的详细信息以及对答案的评论),但稍后会详细介绍......
我使用下面的代码来查找限制.请注意,GetWinMetaFileBits
在VCL代码中不会使用增强的图元文件进行调用.
function IsDimOverLimit(W, H: Integer): Boolean; var Metafile: TMetafile; RefDC: HDC; begin Metafile := TMetafile.Create; Metafile.SetSize(W, H); RefDC := GetDC(0); TMetafileCanvas.Create(Metafile, RefDC).Free; Result := GetWinMetaFileBits(MetaFile.Handle, 0, nil, MM_ANISOTROPIC, RefDC) > 0; ReleaseDC(0, RefDC); Metafile.Free; end; procedure TForm1.Button1Click(Sender: TObject); var i: Integer; begin for i := 20000 to 40000 do if not IsDimOverLimit(100, i) then begin ShowMessage(SysErrorMessage(GetLastError)); // ReleaseDc and freeing meta file does not set any last error Break; end; end;
错误是534("算术结果超过32位").显然有一些有符号整数溢出.某些"mf3216.dll"("32位到16位图元文件转换DLL")在调用GetWinMetaFileBits
其导出ConvertEmfToWmf
函数期间设置错误,但这不会导致有关溢出的任何文档.关于我可以找到的wmf限制的唯一官方文档就是这个(它的主要观点是"仅在16位可执行文件中使用wmf" :)).
如前所述,伪造的"可放置标题"结构可能具有"伪造"值,这可能会阻止VCL正确播放元文件.具体来说,元文件的维度,如VCL所知,可能会溢出.加载图像后,您可以执行简单的健全性检查,以便正确显示:
var Header: TEnhMetaHeader; begin DrawWMF(Rect(0, 0, Dim, Dim), 1.0, 'Original.wmf'); DrawWMF(Rect(0, 0, Dim, Dim), 0.5, 'Scaled.wmf'); try Image1.Picture.LoadFromFile('Original.wmf'); if (TMetafile(Image1.Picture.Graphic).Width < 0) or (TMetafile(Image1.Picture.Graphic).Height < 0) then begin GetEnhMetaFileHeader(TMetafile(Image1.Picture.Graphic).Handle, SizeOf(Header), @Header); TMetafile(Image1.Picture.Graphic).Width := MulDiv(Header.rclFrame.Right, Header.szlDevice.cx, Header.szlMillimeters.cx * 100); TMetafile(Image1.Picture.Graphic).Height := MulDiv(Header.rclFrame.Bottom, Header.szlDevice.cy, Header.szlMillimeters.cy * 100); end; ...