{ ***************************************************************************** See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** Authors: Luís Rodrigues, Alexander Klenin } unit TADrawerWMF; {$H+} interface uses Windows, Classes, Graphics, TADrawerCanvas, TAGraph; type { TMetafile } TMetafile = class(TGraphic) private FImageHandle: HENHMETAFILE; FMMHeight: Integer; // are in 0.01 mm logical pixels FMMWidth: Integer; // are in 0.01 mm logical pixels FImagePxHeight: Integer; // in device pixels FImagePxWidth: Integer; // in device pixels procedure DeleteImage; function GetAuthor: String; function GetDescription: String; function GetHandle: HENHMETAFILE; procedure SetHandle(AValue: HENHMETAFILE); procedure SetMMHeight(AValue: Integer); procedure SetMMWidth(AValue: Integer); protected procedure Draw(ACanvas: TCanvas; const ARect: TRect); override; function GetEmpty: Boolean; override; function GetHeight: Integer; override; function GetTransparent: Boolean; override; function GetWidth: Integer; override; procedure SetHeight(AValue: Integer); override; procedure SetTransparent(AValue: Boolean); override; procedure SetWidth(AValue: Integer); override; public constructor Create; override; destructor Destroy; override; public procedure Assign(ASource: TPersistent); override; procedure Clear; override; procedure CopyToClipboard; procedure LoadFromFile(const AFileName: String); override; procedure LoadFromStream(AStream: TStream); override; function ReleaseHandle: HENHMETAFILE; procedure SaveToFile(const AFileName: String); override; procedure SaveToStream(AStream: TStream); override; property CreatedBy: String read GetAuthor; property Description: String read GetDescription; property Empty: boolean read GetEmpty; property Handle: HENHMETAFILE read GetHandle write SetHandle; property MMHeight: Integer read FMMHeight write SetMMHeight; property MMWidth: Integer read FMMWidth write SetMMWidth; end; { TMetafileCanvas } TMetafileCanvas = class(TCanvas) strict private FMetafile: TMetafile; public constructor Create(AMetafile: TMetafile; AReferenceDevice: HDC); constructor CreateWithComment( AMetafile: TMetafile; AReferenceDevice: HDC; const ACreatedBy, ADescription: String); destructor Destroy; override; end; { TWindowsMetafileDrawer } TWindowsMetafileDrawer = class(TCanvasDrawer) strict private FFileName: String; FMetafile: TMetafile; public constructor Create(const AFileName: String); reintroduce; destructor Destroy; override; public procedure DrawingBegin(const ABoundingBox: TRect); override; procedure DrawingEnd; override; function GetCanvas: TCanvas; override; end; { TWMFChartHelper } TWMFChartHelper = class helper for TChart procedure CopyToClipboardMetafile; procedure SaveToWMF(const AFileName: String); end; implementation uses SysUtils, clipbrd, TAChartUtils; { TWindowsMetafileDrawer } constructor TWindowsMetafileDrawer.Create(const AFileName: String); begin FFileName := AFileName; FMetafile := TMetafile.Create; inherited Create(nil); end; destructor TWindowsMetafileDrawer.Destroy; begin FreeAndNil(FCanvas); FreeAndNil(FMetafile); inherited Destroy; end; procedure TWindowsMetafileDrawer.DrawingBegin(const ABoundingBox: TRect); begin inherited DrawingBegin(ABoundingBox); FreeAndNil(FCanvas); FMetafile.Width := ABoundingBox.Right - ABoundingBox.Left; FMetafile.Height := ABoundingBox.Bottom - ABoundingBox.Top; if FCanvas = nil then FCanvas := TMetafileCanvas.Create(FMetafile, 0); end; procedure TWindowsMetafileDrawer.DrawingEnd; begin FreeAndNil(FCanvas); if FFileName = '' then // Clipboard.Assign(FMetaFile) FMetafile.CopyToClipboard else FMetafile.SaveToFile(FFileName); end; function TWindowsMetafileDrawer.GetCanvas: TCanvas; begin if FCanvas = nil then FCanvas := TMetafileCanvas.Create(FMetafile, 0); Result := FCanvas; end; { TMetafile } procedure TMetafile.DeleteImage; begin if FImageHandle <> 0 then DeleteEnhMetafile(FImageHandle); FImageHandle := 0; end; function TMetafile.GetAuthor: String; var authorLength: Integer; begin Result := ''; if FImageHandle = 0 then exit; authorLength := GetEnhMetafileDescription(FImageHandle, 0, nil); if authorLength <= 0 then exit; SetLength(Result, authorLength); GetEnhMetafileDescription(FImageHandle, authorLength, PChar(Result)); SetLength(Result, StrLen(PChar(Result))); end; function TMetafile.GetDescription: String; var descLength: Integer; begin Result := ''; if FImageHandle = 0 then Exit; descLength := GetEnhMetafileDescription(FImageHandle, 0, nil); if descLength <= 0 then exit; SetLength(Result, descLength); GetEnhMetafileDescription(FImageHandle, descLength, PChar(Result)); SetLength(Result, StrLen(PChar(Result))); end; function TMetafile.GetEmpty: Boolean; begin Result := FImageHandle = 0; end; function TMetafile.GetHandle: HENHMETAFILE; begin Result := FImageHandle; end; procedure TMetafile.SetHandle(AValue: HENHMETAFILE); var emfHeader: TEnhMetaHeader; begin if (AValue <> 0) and (GetEnhMetafileHeader(AValue, sizeof(emfHeader), @emfHeader) = 0) then raise EInvalidImage.Create('Invalid Metafile');; if FImageHandle <> 0 then DeleteImage; FImageHandle := AValue; FImagePxWidth := 0; FImagePxHeight := 0; FMMWidth := emfHeader.rclFrame.Right - emfHeader.rclFrame.Left; FMMHeight := emfHeader.rclFrame.Bottom - emfHeader.rclFrame.Top; end; procedure TMetafile.SetMMHeight(AValue: Integer); begin FImagePxHeight := 0; if FMMHeight <> AValue then FMMHeight := AValue; end; procedure TMetafile.SetMMWidth(AValue: Integer); begin FImagePxWidth := 0; if FMMWidth <> AValue then FMMWidth := AValue; end; procedure TMetafile.SetTransparent(AValue: Boolean); begin if AValue then raise EComponentError.Create('Not implemented'); end; procedure TMetafile.Draw(ACanvas: TCanvas; const ARect: TRect); var r: TRect; begin if FImageHandle = 0 then exit; r := ARect; PlayEnhMetaFile(ACanvas.Handle, FImageHandle, r); end; function TMetafile.GetHeight: Integer; var emfHeader: TEnhMetaHeader; begin if FImageHandle = 0 then exit(FImagePxHeight); // convert 0.01mm units to device pixels GetEnhMetaFileHeader(FImageHandle, Sizeof(emfHeader), @emfHeader); Result := MulDiv( FMMHeight, // metafile height in 0.01mm emfHeader.szlDevice.cy, // device height in pixels emfHeader.szlMillimeters.cy * 100); // device height in mm end; function TMetafile.GetTransparent: Boolean; begin Result := false; end; function TMetafile.GetWidth: Integer; var emfHeader: TEnhMetaHeader; begin if FImageHandle = 0 then exit(FImagePxWidth); // convert 0.01mm units to device pixels GetEnhMetaFileHeader(FImageHandle, Sizeof(emfHeader), @emfHeader); Result := MulDiv( FMMWidth, // metafile width in 0.01mm emfHeader.szlDevice.cx, // device width in pixels emfHeader.szlMillimeters.cx * 100); // device width in 0.01mm end; procedure TMetafile.SetHeight(AValue: Integer); var emfHeader: TEnhMetaHeader; begin if FImageHandle = 0 then FImagePxHeight := AValue else begin // convert device pixels to 0.01mm units GetEnhMetaFileHeader(FImageHandle, Sizeof(emfHeader), @emfHeader); MMHeight := MulDiv(AValue, // metafile height in pixels emfHeader.szlMillimeters.cy * 100, // device height in 0.01mm emfHeader.szlDevice.cy); // device height in pixels end; end; procedure TMetafile.SetWidth(AValue: Integer); var emfHeader: TEnhMetaHeader; begin if FImageHandle = 0 then FImagePxWidth := AValue else begin // convert device pixels to 0.01mm units GetEnhMetaFileHeader(FImageHandle, Sizeof(emfHeader), @emfHeader); MMWidth := MulDiv(AValue, // metafile width in pixels emfHeader.szlMillimeters.cx * 100, // device width in mm emfHeader.szlDevice.cx); // device width in pixels end; end; constructor TMetafile.Create; begin inherited Create; FImageHandle := 0; end; destructor TMetafile.Destroy; begin DeleteImage; inherited Destroy; end; procedure TMetafile.Assign(ASource: TPersistent); begin if (ASource = nil) or (ASource is TMetafile) then begin if FImageHandle <> 0 then DeleteImage; if Assigned(ASource) then begin FImageHandle := TMetafile(ASource).Handle; FMMWidth := TMetafile(ASource).MMWidth; FMMHeight := TMetafile(ASource).MMHeight; FImagePxWidth := TMetafile(ASource).Width; FImagePxHeight := TMetafile(ASource).Height; end end else inherited Assign(ASource); end; procedure TMetafile.Clear; begin DeleteImage; end; procedure TMetafile.CopyToClipboard; // http://www.olivierlanglois.net/metafile-clipboard.html var Format: Word; Data: THandle; begin if FImageHandle = 0 then exit; OpenClipboard(0); try EmptyClipboard; Format := CF_ENHMETAFILE; SetClipboardData(Format, FImageHandle); //Data); finally CloseClipboard; end; end; procedure TMetafile.LoadFromFile(const AFileName: String); begin Unused(AFileName); raise EComponentError.Create('Not Implemented'); end; procedure TMetafile.SaveToFile(const AFileName: String); var outFile: HENHMETAFILE; begin if FImageHandle = 0 then exit; outFile := CopyEnhMetaFile(FImageHandle, PChar(AFileName)); if outFile = 0 then RaiseLastWin32Error; DeleteEnhMetaFile(outFile); end; procedure TMetafile.LoadFromStream(AStream: TStream); begin Unused(AStream); raise EComponentError.Create('Not Implemented'); end; procedure TMetafile.SaveToStream(AStream: TStream); begin Unused(AStream); raise EComponentError.Create('Not Implemented'); end; function TMetafile.ReleaseHandle: HENHMETAFILE; begin DeleteImage; Result := FImageHandle; FImageHandle := 0; end; { TMetafileCanvas } constructor TMetafileCanvas.Create(AMetafile: TMetafile; AReferenceDevice: HDC); begin CreateWithComment( AMetafile, AReferenceDevice, AMetafile.CreatedBy, AMetafile.Description); end; constructor TMetafileCanvas.CreateWithComment( AMetafile: TMetafile; AReferenceDevice: HDC; const ACreatedBy, ADescription: String); var refDC: HDC; r: TRect; temp: HDC; p: PChar; w, h: Integer; begin inherited Create; FMetafile := AMetafile; refDC := AReferenceDevice; if refDC = 0 then refDC := GetDC(0); try if FMetafile.MMWidth = 0 then begin w := GetDeviceCaps(refDC, HORZSIZE) * 100; if FMetafile.Width = 0 then // if no width get refDC width FMetafile.MMWidth := w else // else convert FMetafile.MMWidth := MulDiv( FMetafile.Width, w, GetDeviceCaps(refDC, HORZRES)); end; if FMetafile.MMHeight = 0 then begin h := GetDeviceCaps(refDC, VERTSIZE) * 100; if FMetafile.Height = 0 then // if no height get refDC height FMetafile.MMHeight := h else // else convert FMetafile.MMHeight := MulDiv( FMetafile.Height, h, GetDeviceCaps(refDC, VERTRES)); end; r := Rect(0, 0, FMetafile.MMWidth, FMetafile.MMHeight); // lpDescription stores both author and description if (Length(ACreatedBy) > 0) or (Length(ADescription) > 0) then p := PChar(ACreatedBy+#0+ADescription+#0#0) else p := nil; temp := CreateEnhMetafile(refDC, nil, @r, p); if temp = 0 then raise EOutOfResources.Create('Out of Resources');; Handle := temp; finally if AReferenceDevice = 0 then ReleaseDC(0, refDC); end; end; destructor TMetafileCanvas.Destroy; begin FMetafile.Handle := CloseEnhMetafile(Handle); inherited Destroy; end; { TWMFChartHelper } procedure TWMFChartHelper.CopyToClipboardMetafile; begin Draw(TWindowsMetafileDrawer.Create(''), Rect(0, 0, Width, Height)); end; procedure TWMFChartHelper.SaveToWMF(const AFileName: String); begin Draw(TWindowsMetafileDrawer.Create(AFilename), Rect(0, 0, Width, Height)); end; end.