1

The scenario is that I have a printer EMF file. I'm wanting to overlay some data on it before sending it to the printer. The printer file is 300dpi. It's important to keep the same print quality.

I'm converting the emf file using this code ...

  b:=TBitmap.create;
  MyMetaFile.LoadFromFile(emf);
  MyCanvas:= TMetafileCanvas.Create(MyMetaFile, 0);
  try
    Width:=MyMetaFile.Width;
    Height:=MyMetaFile.Height;

    b.width:=width;
    b.height:=height;
    b.Canvas.Draw(0, 0, MyMetaFile);
    b.SaveToFile('c:\emftest.bmp');

That kind of works except you only get the top left corner of the emf file. If you look at the properties of the EMF file in windows, it says 2551x3301. But width and height here are set to 613x792. If I override the b.width and b.height with these values it's all ok, but I obviously don't want to do that. The EMF file is 300dpi, the screen dpi is about 100, yet the error is a factor of 4.16. Any idea what is going on here?

Thanks Terry

4

2 回答 2

2

You have to use StrechDraw instead of Draw here, in order to scale the content to the expected bitmap size.

For instance:

function MetafileToBitmap(Source: TMetafile; ScaleX,ScaleY: integer): TBitmap;
var R: TRect;
begin
  result := nil;
  if Source=nil then // self=nil is OK below
    Exit;
  R.Left := 0;
  R.Right := (Source.Width*ScaleX)div 100;
  R.Top := 0;
  R.Bottom := (Source.Height*ScaleY)div 100;
  result := TBitmap.Create;
  result.Width := R.Right;
  result.Height := R.Bottom;
  Dec(R.Right);  // Metafile rect includes right and bottom coords
  Dec(R.Bottom);
  PlayEnhMetaFile(Result.Canvas.Handle,Source.Handle,R);
end;

If you want anti-aliased drawing using GDI+, you will need to enumerate the metafile content then render it with our Open Source SynGdiPlus unit:

function LoadFrom(const MetaFile: TMetaFile): TBitmap;
于 2013-03-12T10:33:20.770 回答
1

(Thanks to Arnaud for his comments.)

When working with matafiles, only the bitmap data will be rendered when you load the file unless you use the Windows GDI methods to render (or play) the vector content. I use a 3rd party library (GDI+) to do the work for me. I also use Graphics32 whenever I work with images.

GDI+

The author's website doesn't seem to have the source code anymore. However, I found it here by searching for gdipobj.pas. I found it on code.google.com as well (included in several projects, like this one).

Graphics32

The source for Graphics32 is on SourceForge here. The SVN command is:

svn co https://graphics32.svn.sourceforge.net/svnroot/graphics32/trunk graphics32

My Solution

It took a while to figure out how to properly render the Metafile, at the right size, etc. Here is the function that I use.

uses
  GR32, GDIPOBJ, ActiveX;

procedure RenderWMF(var WMF: TMemoryStream; OutputBitmap: TBitmap32; const ScreenResolution: Integer = 96);
var
  gph: TGPGraphics;
  img: TGPImage;
  bmap: TBitmap;
  ms: IStream;
  pLi: PLongint;
  iPos: Int64;
  tmpms: TMemoryStream;
begin
  WMF.Position := 0;
  ms := TStreamAdapter.Create(WMF, soReference);
  try
    pLi := nil;
    ms.Write(WMF.Memory, WMF.Size, pLi);
    bmap := TBitmap.Create;
    try
      ms.Seek(0, soFromBeginning, iPos);
      img := TGPImage.Create(ms);
      try
        bmap.width := Trunc(img.GetWidth / img.GetHorizontalResolution * ScreenResolution + 0.5);
        bmap.height := Trunc(img.GetHeight / img.GetVerticalResolution * ScreenResolution + 0.5);
        gph := TGPGraphics.Create(bmap.Canvas.Handle);
        try
          gph.DrawImage(img, 0, 0, bmap.Width, bmap.Height);
          tmpms := TMemoryStream.Create;
          try
            bmap.SaveToStream(tmpms);
            tmpms.Position := 0;
            OutputBitmap.LoadFromStream(tmpms);
          finally
            tmpms.Free;
          end;
        finally
          gph.Free;
        end;
      finally
        img.Free;
      end;
    finally
      bmap.Free;
    end;
  finally
    ms := nil;
  end;
end;

I call it with code like this:

var
  bm32: TBitmap32;
  wmf: TMemoryStream;
begin
  bm32 := TBitmap32.Create;
  try
    wmf := TMemoryStream.Create;
    try
      wmf.LoadFromFile('metafile.emf');
      RenderWMF(wmf, bm32, Screen.PixelsPerInch);
      // Do something with 'bm32'
    finally
      wmf.Free;
    end;
  finally
    bm32.Free;
  end;
end;

I know this answer is much broader than how to properly set the canvas size. I hope it is helpful.

于 2013-03-12T07:40:20.623 回答