现在,我想知道绘图的大小是否有限制,或者问题是其他的.我知道这些文件有一个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,Dim,Dim),1.0,'Original.wmf'); DrawWMF(Rect(0,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’s answer找到更多的细节.
第二个问题是将文件加载到TImage中.当您想在delphi VCL应用程序中显示图像时,还有另一个限制.这一个是系统依赖的,并且与图形将被绘制的DC的dpi相关. Tom’s answer详细描述.传递0.7作为缩放DrawWMF(上面的代码示例)在我的电脑上再现这种情况.生成的文件可以使用其他图元文件查看器(我使用MS Office Picture Manager)查看,但VCL无法显示,但在加载文件时不会出现异常.
解决方法
跟踪VCL代码,输出文件在TMetafile.WriteWMFStream中被破坏. VCL写入一个WmfPlaceableFileHeader
(TMetafileHeader在VCL)记录,然后调用GetWinMetaFileBits
将’emf’记录转换为’wmf’记录.如果边界矩形(调用CreateEnhMetaFile时使用)的任何维度大于32767,则此函数将失败.不检查返回值,VCL不会引发任何异常,只关闭22个字节的文件 – 只有“可放置的头”.
即使尺寸小于32767,“可放置的标题”可能有错误的值(从Tom’s answer读取详细的原因和含义,并给出了答案),但更多的是在以后…
我使用下面的代码找到限制.请注意,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,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限制的唯一官方文档是this(其主要内容是“仅在16位可执行文件中使用wmf”)).
如前所述,伪造的“可置换标题”结构可能具有“伪造”值,这可能会阻止VCL正确播放图元文件.具体来说,像VCL知道它们的元文件的维度可能会溢出.您可以在加载图像以便正确显示之后执行简单的理智检查:
var Header: TEnhMetaHeader; begin DrawWMF(Rect(0,'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; ...