{*******************************************************************} { } { Developer Express Visual Component Library } { ExpressPrinting System(tm) COMPONENT SUITE } { } { Copyright (C) 1998-2009 Developer Express Inc. } { ALL RIGHTS RESERVED } { } { The entire contents of this file is protected by U.S. and } { International Copyright Laws. Unauthorized reproduction, } { reverse-engineering, and distribution of all or any portion of } { the code contained in this file is strictly prohibited and may } { result in severe civil and criminal penalties and will be } { prosecuted to the maximum extent possible under the law. } { } { RESTRICTIONS } { } { THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES } { (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE } { SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS } { LICENSED TO DISTRIBUTE THE EXPRESSPRINTINGSYSTEM AND } { ALL ACCOMPANYING VCL CONTROLS AS PART OF AN } { EXECUTABLE PROGRAM ONLY. } { } { THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED } { FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE } { COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE } { AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT } { AND PERMISSION FROM DEVELOPER EXPRESS INC. } { } { CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON } { ADDITIONAL RESTRICTIONS. } { } {*******************************************************************} unit dxPrnDev; interface (*$HPPEMIT '#include ' *) {$I cxVer.inc} uses Windows, Classes, ImgList, Controls, SysUtils, Graphics, Messages, dxCore, cxControls, dxPSGlbl; type // Follow structures are supported only in W2K and upper // specifies the global default printer settings {$IFDEF CBUILDER5} {$EXTERNALSYM _PRINTER_INFO_8A} {$ENDIF} _PRINTER_INFO_8A = record DevMode: PDeviceModeA ; end; {$IFDEF CBUILDER5} {$EXTERNALSYM _PRINTER_INFO_8W} {$ENDIF} _PRINTER_INFO_8W = record DevMode: PDeviceModeW ; end; {$IFDEF CBUILDER5} {$EXTERNALSYM _PRINTER_INFO_8} {$ENDIF} _PRINTER_INFO_8 = _PRINTER_INFO_8A; TPrinterInfo8A = _PRINTER_INFO_8A; TPrinterInfo8W = _PRINTER_INFO_8W; TPrinterInfo8 = TPrinterInfo8A; {$IFDEF CBUILDER5} {$EXTERNALSYM PRINTER_INFO_8A} {$ENDIF} PRINTER_INFO_8A = _PRINTER_INFO_8A; {$IFDEF CBUILDER5} {$EXTERNALSYM PRINTER_INFO_8W} {$ENDIF} PRINTER_INFO_8W = _PRINTER_INFO_8W; {$IFDEF CBUILDER5} {$EXTERNALSYM PRINTER_INFO_8} {$ENDIF} PRINTER_INFO_8 = PRINTER_INFO_8A; PPrinterInfo8A = ^TPrinterInfo8A; PPrinterInfo8W = ^TPrinterInfo8W; PPrinterInfo8 = PPrinterInfo8A; // specifies the per-user default printer settings {$IFDEF CBUILDER5} {$EXTERNALSYM _PRINTER_INFO_9A} {$ENDIF} _PRINTER_INFO_9A = record DevMode: PDeviceModeA ; end; {$IFDEF CBUILDER5} {$EXTERNALSYM _PRINTER_INFO_9W} {$ENDIF} _PRINTER_INFO_9W = record DevMode: PDeviceModeW ; end; {$IFDEF CBUILDER5} {$EXTERNALSYM _PRINTER_INFO_8} {$ENDIF} _PRINTER_INFO_9 = _PRINTER_INFO_9A; TPrinterInfo9A = _PRINTER_INFO_9A; TPrinterInfo9W = _PRINTER_INFO_9W; TPrinterInfo9 = TPrinterInfo9A; {$IFDEF CBUILDER5} {$EXTERNALSYM PRINTER_INFO_9A} {$ENDIF} PRINTER_INFO_9A = _PRINTER_INFO_9A; {$IFDEF CBUILDER5} {$EXTERNALSYM PRINTER_INFO_9W} {$ENDIF} PRINTER_INFO_9W = _PRINTER_INFO_9W; {$IFDEF CBUILDER5} {$EXTERNALSYM PRINTER_INFO_9} {$ENDIF} PRINTER_INFO_9 = PRINTER_INFO_9A; PPrinterInfo9A = ^TPrinterInfo9A; PPrinterInfo9W = ^TPrinterInfo9W; PPrinterInfo9 = PPrinterInfo9A; TdxPrinterState = (psNoHandle, psHandleIC, psHandleDC); TdxDuplexMode = (dmSimplex, dmHorizontal, dmVertical); TdxPrinterOrientation = (poPortrait, poLandscape); TdxPrinterCapability = (pcCopies, pcOrientation, pcCollation);//, pcDuplex, pcColor); TdxPrinterCapabilities = set of TdxPrinterCapability; EdxPrintDevice = class(EdxException); TdxPrintDeviceInfo = class private FDevice: PChar ; FDriver: PChar ; FPort: PChar ; FIsDefault: Boolean; FIsNetwork: Boolean; public constructor Create(ADriver, ADevice, APort: PChar ; AnIsDefault, AnIsNetwork: Boolean); destructor Destroy; override; function IsEqual(ADriver, ADevice, APort: PChar ): Boolean; property Device: PChar read FDevice; property Driver: PChar read FDriver; property Port: PChar read FPort; property IsDefault: Boolean read FIsDefault write FIsDefault; property IsNetwork: Boolean read FIsNetwork; end; TdxPrintDevice = class private FAborted: Boolean; FAutoRefresh: Boolean; FBins: TStrings; FCanvas: TCanvas; FCapabilities: TdxPrinterCapabilities; FCurrentDevice: PChar ; FCurrentDriver: PChar ; FCurrentPort: PChar ; FDC: HDC; FDeviceHandle: THandle; FDeviceMode: PDeviceMode ; FDeviceModeChanged: Boolean; FHDeviceMode: THandle; FFileName: string; FFonts: TStrings; FIsDeviceModePersistent: Boolean; FMaxCopies: Integer; FMaxExtents: Integer; FMinExtents: Integer; FPageNumber: Integer; FPapers: TStrings; FPrinters: TStrings; FPrinterIndex: Integer; FPrinting: Boolean; FState: TdxPrinterState; FTitle: string; FWindowHandle: HWND; FOnNewPage: TNotifyEvent; FOnPrinterChange: TNotifyEvent; FOnRefresh: TNotifyEvent; function GetBinIndex: Integer; function GetBins: TStrings; function GetCanvas: TCanvas; function GetCollate: Boolean; function GetColorMode: Boolean; function GetCurrentDevice: PChar ; function GetCurrentDriver: PChar ; function GetCurrentPort: PChar ; function GetDefaultDMPaper: Integer; function GetDeviceMode: PDeviceMode; function GetDuplex: TdxDuplexMode; function GetFonts: TStrings; function GetHandle: HDC; function GetHDeviceMode: THandle; function GetIsDefault: Boolean; function GetIsInitialized: Boolean; function GetIsNetwork: Boolean; function GetMaxExtents(Index: Integer): Integer; function GetMinExtents(Index: Integer): Integer; function GetNumCopies: Integer; function GetOrientation: TdxPrinterOrientation; function GetPageHeight: Integer; function GetPageHeightLoMetric: Integer; function GetPageWidth: Integer; function GetPageWidthLoMetric: Integer; function GetPaperCount: Integer; function GetPaperIndex: Integer; function GetPapers: TStrings; function GetPhysOffset(Index: Integer): Integer; function GetPrinterCount: Integer; function GetPrinterIndex: Integer; function GetPrinterInfo(Index: Integer): TdxPrintDeviceInfo; function GetPrinters: TStrings; procedure SetBinIndex(Value: Integer); procedure SetCollate(Value: Boolean); procedure SetColorMode(Value: Boolean); procedure SetDuplex(Value: TdxDuplexMode); procedure SetIsDefault(Value: Boolean); procedure SetNumCopies(Value: Integer); procedure SetOrientation(Value: TdxPrinterOrientation); procedure SetPaperIndex(Value: Integer); procedure SetPrinterCapabilities(Value: Integer); procedure SetPrinterIndex(Value: Integer); procedure CheckPrinting(Value: Boolean); procedure ClosePrintDevice; procedure DeselectHandles; procedure FixMinMaxExtents; procedure FreeAndNilBins; procedure FreeAndNilCanvas; procedure FreeAndNilFonts; procedure FreeAndNilPapers; procedure FreeAndNilPrinters; procedure InternalSelectPaperBySize(var AWidth, AHeight: Integer); procedure OpenPrintDevice(AIndex: Integer); procedure SetState(Value: TdxPrinterState); procedure SetToDefaultPrintDevice; procedure WndProc(var Message: TMessage); protected procedure DoNewPage; dynamic; procedure DoPrinterChanged; dynamic; procedure DoRefresh; dynamic; public constructor Create; destructor Destroy; override; procedure Abort; function BeginDoc: Integer; {returns the print job identifier for the document} procedure EndDoc; procedure NewPage; function FindPrintDevice(ADevice, APort: PChar ): Integer; function IsDeviceModeChanged: Boolean; procedure Refresh; procedure ResetDC(IsForced: Boolean); procedure ResetPrintDevice; function FindBin(ABin: Integer): Integer; overload; function FindBin(const AName: string): Integer; overload; function IsAutoSelectBin(AIndex: Integer): Boolean; function IsUserPaperSource(AIndex: Integer): Boolean; function SelectBin(Value: Integer): Boolean; overload; function SelectBin(const AName: string): Boolean; overload; function FindPaper(ADMPaper: Integer): Integer; overload; function FindPaper(const AName: string): Integer; overload; function FindPaper(const ASize: TPoint): Integer; overload; function FindPaper(AWidth, AHeight: Integer): Integer; overload; function IsEnvelopePaper(AIndex: Integer): Boolean; function IsUserPaperSize(AIndex: Integer): Boolean; function SelectPaper(ADMPaper: Integer): Boolean; overload; function SelectPaper(const AName: string): Boolean; overload; function SelectPaper(var AWidth, AHeight: Integer): Boolean; overload; function IsSupportColoration: Boolean; function IsSupportDuplex: Boolean; property Aborted: Boolean read FAborted; property AutoRefresh: Boolean read FAutoRefresh write FAutoRefresh; property BinIndex: Integer read GetBinIndex write SetBinIndex; property Bins: TStrings read GetBins; property Canvas: TCanvas read GetCanvas; property Capabilities: TdxPrinterCapabilities read FCapabilities; property Collate: Boolean read GetCollate write SetCollate; property ColorMode: Boolean read GetColorMode write SetColorMode; property Copies: Integer read GetNumCopies write SetNumCopies; property CurrentDevice: PChar read GetCurrentDevice; property CurrentDriver: PChar read GetCurrentDriver; property CurrentPort: PChar read GetCurrentPort; property DefaultDMPaper: Integer read GetDefaultDMPaper; property DeviceMode: PDeviceMode read GetDeviceMode; property Duplex: TdxDuplexMode read GetDuplex write SetDuplex; property FileName: string read FFileName write FFileName; property Fonts: TStrings read GetFonts; property Handle: HDC read GetHandle; property HDeviceMode: THandle read GetHDeviceMode; property IsDefault: Boolean read GetIsDefault write SetIsDefault; property IsDeviceModePersistent: Boolean read FIsDeviceModePersistent write FIsDeviceModePersistent; property IsInitialized: Boolean read GetIsInitialized; property IsNetwork: Boolean read GetIsNetwork; property MaxCopies: Longint read FMaxCopies; property MaxExtentX: Integer index 0 read GetMaxExtents; property MaxExtentY: Integer index 1 read GetMaxExtents; property MinExtentX: Integer index 0 read GetMinExtents; property MinExtentY: Integer index 1 read GetMinExtents; property Orientation: TdxPrinterOrientation read GetOrientation write SetOrientation; property PageHeight: Integer read GetPageHeight; property PageHeightLoMetric: Integer read GetPageHeightLoMetric; property PageWidth: Integer read GetPageWidth; property PageWidthLoMetric: Integer read GetPageWidthLoMetric; property PageNumber: Integer read FPageNumber; property PaperIndex: Integer read GetPaperIndex write SetPaperIndex; property PaperCount: Integer read GetPaperCount; property Papers: TStrings read GetPapers; property PhysOffsetX: Integer index 0 read GetPhysOffset; property PhysOffsetY: Integer index 1 read GetPhysOffset; property PrinterCount: Integer read GetPrinterCount; property PrinterIndex: Integer read GetPrinterIndex write SetPrinterIndex; property PrinterInfos[Index: Integer]: TdxPrintDeviceInfo read GetPrinterInfo; property Printers: TStrings read GetPrinters; property Printing: Boolean read FPrinting; property Title: string read FTitle write FTitle; property OnNewPage: TNotifyEvent read FOnNewPage write FOnNewPage; property OnPrinterChange: TNotifyEvent read FOnPrinterChange write FOnPrinterChange; property OnRefresh: TNotifyEvent read FOnRefresh write FOnRefresh; end; TdxPaperInfo = class private FDMPaper: Integer; FName: string; FPrintDevice: TdxPrintDevice; FSize: TPoint; function GetSize(Index: Integer): Integer; procedure SetSize(Index: Integer; Value: Integer); public constructor Create(APrintDevice: TdxPrintDevice); procedure Assign(Source: TdxPaperInfo); function IsEqual(Source: TdxPaperInfo): Boolean; property DMPaper: Integer read FDMPaper; property Height: Integer index 1 read GetSize write SetSize; property Name: string read FName; property Size: TPoint read FSize; property Width: Integer index 0 read GetSize write SetSize; end; function dxConnectToNetPrinter(AParentWnd: HWND): Boolean; function dxDocumentProperties(AParentWnd: HWND): Boolean; function dxInitPrintDevice(ARaiseException: Boolean): Boolean; function dxIsAutoSelectBin(const AName: string): Boolean; function dxIsEnvelopePaper(const AName: string): Boolean; function dxIsPrintDeviceAllocated: Boolean; function dxIsPrintDeviceInitialized: Boolean; function dxPrintDevice: TdxPrintDevice; procedure dxReleasePrintDevice; function dxSetPrintDevice(APrintDevice: TdxPrintDevice): TdxPrintDevice; procedure dxDrawPrinter(ACanvas: TCanvas; ARect: TRect; const AText: string; AImageList: TCustomImageList; AImageIndex: Integer); procedure dxGetPrinterList(AStrings: TStrings); function dxGetDefaultDMPaper: Integer; function dxGetDefaultPrinter: string; function dxIsDefaultPrinter(ADevice: PChar ): Boolean; procedure dxSetDefaultPrinter(ADevice: PChar ); type TConnectToPrinterDlg = function(hWnd: HWND; Flags: DWORD): THandle; stdcall; TGetDefaultPrinter = function(pszBuffer: PChar; var cchBuffer: DWORD): BOOL; stdcall; TGetDefaultPrinterA = function(pszBuffer: PAnsiChar; var cchBuffer: DWORD): BOOL; stdcall; TGetDefaultPrinterW = function(pszBuffer: PWideChar; var cchBuffer: DWORD): BOOL; stdcall; TSetDefaultPrinter = function(pszPrinter: PChar): BOOL; stdcall; TSetDefaultPrinterA = function(pszPrinter: PAnsiChar): BOOL; stdcall; TSetDefaultPrinterW = function(pszPrinter: PWideChar): BOOL; stdcall; var dxPSUseCachedDMPaper: Boolean = True; const cMinPaperExtent = 254; cMaxPaperExtent = 30 * cMinPaperExtent; {$EXTERNALSYM LOCALE_IPAPERSIZE} LOCALE_IPAPERSIZE = $0000100A; implementation uses Forms, WinSpool, cxClasses, dxPSUtl, dxPSRes; const dxDefaultMaxPaperExtents = 5000; dxDefaultMinPaperExtents = 500; DuplexModeMap: array[DMDUP_SIMPLEX..DMDUP_HORIZONTAL] of TdxDuplexMode = (dmSimplex, dmHorizontal, dmVertical); OrientationMap: array[DMORIENT_PORTRAIT..DMORIENT_LANDSCAPE] of TdxPrinterOrientation = (poPortrait, poLandscape); PrinterOrientationMap: array[TdxPrinterOrientation] of Integer = (DMORIENT_PORTRAIT, DMORIENT_LANDSCAPE); var FPrintDevice: TdxPrintDevice = nil; FConnectToPrinterDlg: TConnectToPrinterDlg = nil; FGetDefaultPrinter: TGetDefaultPrinter = nil; FSetDefaultPrinter: TSetDefaultPrinter = nil; function DocumentProperties(hWnd: HWND; hPrinter: THandle; pDeviceName: PChar; pDevModeOutput, pDevModeInput: PDeviceMode; fMode: DWORD): Longint; stdcall; external winspl name {$IFNDEF DELPHI12} 'DocumentPropertiesA' {$ELSE} 'DocumentPropertiesW' {$ENDIF}; function IsConnectToPrinterDlgInitialized: Boolean; begin Result := @FConnectToPrinterDlg <> nil; end; function IsGetDefaultPrinterInitialized: Boolean; begin Result := @FGetDefaultPrinter <> nil; end; function IsSetDefaultPrinterInitialized: Boolean; begin Result := @FSetDefaultPrinter <> nil; end; { Memory Management Routines } function dxAllocMem(ASize: Integer): Pointer; begin Result := AllocMem(ASize); end; procedure dxFreeMem(var P: Pointer; ASize: Integer); begin FreeMem(P, ASize); P := nil; end; {.$DEFINE DEBUG_PRINTDEVICE} {$IFDEF DEBUG_PRINTDEVICE} var LogFile: TextFile; procedure RewriteLog; begin Rewrite(LogFile); end; procedure WriteLog(const S: string); begin WriteLn(LogFile, S); end; {$ENDIF} function FetchStr(var Str: PChar): PChar; var P: PChar; begin Result := Str; if Str = nil then Exit; P := Str; while P^ = ' ' do Inc(P); Result := P; while (P^ <> #0) and (P^ <> ',') do P := StrNextChar(P); if P^ = ',' then begin P^ := #0; Inc(P); end; Str := P; end; procedure RaiseError(const Msg: string); begin raise EdxPrintDevice.Create(Msg); end; function AbortProc(Prn: HDC; Error: Integer): Bool; stdcall; begin {$IFDEF DEBUG_PRINTDEVICE} WriteLog('AbortProc'); {$ENDIF} // Application.ProcessMessages; Result := not FPrintDevice.Aborted; end; function dxIsPrintDeviceAllocated: Boolean; begin Result := FPrintDevice <> nil; end; function dxIsPrintDeviceInitialized: Boolean; begin Result := dxIsPrintDeviceAllocated and dxPrintDevice.IsInitialized; end; function dxPrintDevice: TdxPrintDevice; begin if FPrintDevice = nil then FPrintDevice := TdxPrintDevice.Create; Result := FPrintDevice; end; function dxConnectToNetPrinter(AParentWnd: HWND): Boolean; var Handle: THandle; begin if IsConnectToPrinterDlgInitialized then begin Handle := FConnectToPrinterDlg(AParentWnd, 0); Result := Handle <> 0; if Result then begin ClosePrinter(Handle); dxPrintDevice.Refresh; end; end else Result := False; end; {.$WARN SYMBOL_DEPRECATED OFF} function dxDocumentProperties(AParentWnd: HWND): Boolean; var HNewDevMode: THandle; PNewDevMode: PDevMode ; begin Result := False; HNewDevMode := 0; try HNewDevMode := CopyDeviceMode(dxPrintDevice.HDeviceMode); if HNewDevMode <> 0 then try PNewDevMode := GlobalLock(HNewDevMode); try with dxPrintDevice do Result := IDOK = DocumentProperties(AParentWnd, FDeviceHandle, CurrentDevice, PNewDevMode, DeviceMode, DM_IN_PROMPT or DM_OUT_BUFFER or DM_IN_BUFFER); finally GlobalUnlock(HNewDevMode); end; finally if Result then with dxPrintDevice do begin while GlobalUnlock(FHDeviceMode) do; GlobalFree(FHDeviceMode); FHDeviceMode := HNewDevMode; FDeviceMode := GlobalLock(FHDeviceMode); end else if HNewDevMode <> 0 then GlobalFree(HNewDevMode); end; except if HNewDevMode <> 0 then GlobalFree(HNewDevMode); end; end; {.$WARN SYMBOL_DEPRECATED ON} {$HINTS OFF} function dxInitPrintDevice(ARaiseException: Boolean): Boolean; var Stub: HDC; begin Result := True; try Result := dxPrintDevice.Printers.Count > 0; if Result then Stub := dxPrintDevice.Handle; except Result := False; if ARaiseException then raise; end; end; {$HINTS ON} procedure dxReleasePrintDevice; begin dxSetPrintDevice(nil).Free; end; function dxSetPrintDevice(APrintDevice: TdxPrintDevice): TdxPrintDevice; begin Result := FPrintDevice; FPrintDevice := APrintDevice; end; function dxIsAutoSelectBin(const AName: string): Boolean; begin Result := Pos(UpperCase(cxGetResourceString(@sdxAuto)), UpperCase(AName)) > 0; end; function dxIsEnvelopePaper(const AName: string): Boolean; begin Result := Pos(UpperCase(cxGetResourceString(@sdxEnv)), UpperCase(AName)) > 0; end; function dxGetDefaultDMPaper: Integer; const CachedDMPaper: Integer = -1; var Device: string; HPrinter: THandle; dwNeeded: DWORD; PI9: PPrinterInfo9; begin if not dxPSUseCachedDMPaper or (CachedDMPaper = -1) then begin Result := Windows.DMPAPER_FIRST; if IsWin2KOrXP then begin Device := dxGetDefaultPrinter; if (Device <> '') and OpenPrinter(PChar(Device), HPrinter, nil) and (HPrinter <> 0) then try dwNeeded := 0; {3.03} if GetPrinter(HPrinter, 9, nil, 0, @dwNeeded) or (dwNeeded <> 0) then begin PI9 := GlobalAllocPtr(GPTR, dwNeeded); if PI9 <> nil then try if GetPrinter(HPrinter, 9, PI9, dwNeeded, @dwNeeded) then if PI9^.DevMode <> nil then try Result := PI9^.DevMode^.dmPaperSize; finally //GlobalFreePtr(PI9^.DevMode); //TODO: check necessity end; finally GlobalFreePtr(PI9); end; end; finally ClosePrinter(HPrinter); end else case GetLocaleChar(LOCALE_USER_DEFAULT, LOCALE_IPAPERSIZE, '0') of '1': Result := Windows.DMPAPER_LETTER; '5': Result := Windows.DMPAPER_LEGAL; '8': Result := Windows.DMPAPER_A3; '9': Result := Windows.DMPAPER_A4; end; end; CachedDMPaper := Result; end else Result := CachedDMPaper; end; function dxGetDefaultPrinter: string; const MAX_BUFFER_SIZE = 1024; var L: DWORD; Buffer: array[0..MAX_BUFFER_SIZE] of Char; C: PChar; begin Result := ''; L := MAX_BUFFER_SIZE; FillChar(Buffer, SizeOf(Buffer), 0); if false and IsGetDefaultPrinterInitialized and FGetDefaultPrinter(@Buffer, L) and (GetLastError = ERROR_SUCCESS) then Result := Buffer else if GetProfileString('WINDOWS', 'DEVICE', '', Buffer, L) > 0 then begin C := @Buffer[0]; Result := FetchStr(C); end; end; function dxIsDefaultPrinter(ADevice: PChar ): Boolean; begin Result := StrIComp(PChar(dxGetDefaultPrinter), ADevice) = 0; end; procedure dxSetDefaultPrinter(ADevice: PChar ); var dwNeeded: DWORD; HPrinter: THandle; PI2: PPrinterInfo2; S: string; begin if IsSetDefaultPrinterInitialized then // Win2KOrXP FSetDefaultPrinter(ADevice) else if OpenPrinter(ADevice, HPrinter, nil) and (HPrinter <> 0) then try dwNeeded := 0; {3.03} if GetPrinter(HPrinter, 2, nil, 0, @dwNeeded) or (dwNeeded <> 0) then begin PI2 := GlobalAllocPtr(GPTR, dwNeeded); if PI2 <> nil then try if GetPrinter(HPrinter, 2, PI2, dwNeeded, @dwNeeded) then if IsWin9X then begin PI2^.Attributes := PI2^.Attributes or PRINTER_ATTRIBUTE_DEFAULT; SetPrinter(HPrinter, 2, PI2, 0); SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, LPARAM(PChar('windows')), SMTO_NORMAL, 1000, dwNeeded); end else // WinNT or earlier if (PI2^.pDriverName <> nil) and (PI2^.pPortName <> nil) then begin S := dxConcatenateStrings([PI2^.pPrinterName, ',',PI2^.pDriverName, ',', PI2^.pDriverName]); if WriteProfileString('windows', 'device', PChar(S)) then begin dwNeeded := Length(S); SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0, SMTO_NORMAL, 1000, dwNeeded); end; end; finally GlobalFreePtr(PI2); end; end; finally ClosePrinter(hPrinter); end; end; procedure dxDrawPrinter(ACanvas: TCanvas; ARect: TRect; const AText: string; AImageList: TCustomImageList; AImageIndex: Integer); var Offset: Integer; begin with ACanvas, ARect do begin FillRect(ARect); // Image InflateRect(ARect, -1, -1); Offset := (Bottom - Top - AImageList.Height) div 2; Inc(Left, Offset); Inc(Top, Offset); AImageList.Draw(ACanvas, Left, Top, AImageIndex); // Text Inc(Offset); Inc(Left, Offset + AImageList.Width + Offset); Brush.Style := bsClear; TextRect(ARect, Left + Offset, Top + Offset, AText); Brush.Style := bsSolid; end; end; procedure dxGetPrinterList(AStrings: TStrings); begin with AStrings do begin BeginUpdate; try Clear; if dxPrintDevice.Printers <> nil then Assign(dxPrintDevice.Printers); finally EndUpdate; end; end; end; { TdxPrintDeviceInfo } constructor TdxPrintDeviceInfo.Create(ADriver, ADevice, APort: PChar ; AnIsDefault, AnIsNetwork: Boolean); begin inherited Create; FDriver := StrNew(ADriver); FDevice := StrNew(ADevice); FPort := StrNew(APort); FIsDefault := AnIsDefault; FIsNetwork := AnIsNetwork; end; destructor TdxPrintDeviceInfo.Destroy; begin StrDispose(FPort); StrDispose(FDevice); StrDispose(FDriver); inherited; end; function TdxPrintDeviceInfo.IsEqual(ADriver, ADevice, APort: PChar ): Boolean; begin Result := (StrIComp(Device, ADevice) = 0) and ((Port = nil) or (APort = nil) or (StrIComp(Port, APort) = 0)); end; { TdxPrintDeviceCanvas } type TdxPrintDeviceCanvas = class(TCanvas) private FPrintDevice: TdxPrintDevice; protected procedure CreateHandle; override; procedure Changing; override; procedure UpdateFont; public constructor Create(APrintDevice: TdxPrintDevice); end; constructor TdxPrintDeviceCanvas.Create(APrintDevice: TdxPrintDevice); begin inherited Create; FPrintDevice := APrintDevice; end; procedure TdxPrintDeviceCanvas.CreateHandle; begin FPrintDevice.SetState(psHandleIC); UpdateFont; Handle := FPrintDevice.FDC; end; procedure TdxPrintDeviceCanvas.Changing; begin FPrintDevice.CheckPrinting(True); inherited Changing; UpdateFont; end; procedure TdxPrintDeviceCanvas.UpdateFont; var FontSize: Integer; begin if GetDeviceCaps(FPrintDevice.FDC, LOGPIXELSY) <> Font.PixelsPerInch then begin FontSize := Font.Size; Font.PixelsPerInch := GetDeviceCaps(FPrintDevice.FDC, LOGPIXELSY); Font.Size := FontSize; end; end; { TdxPaperInfo } constructor TdxPaperInfo.Create(APrintDevice: TdxPrintDevice); begin inherited Create; FPrintDevice := APrintDevice; end; function TdxPaperInfo.IsEqual(Source: TdxPaperInfo): Boolean; begin Result := (DMPaper = Source.DMPaper) and (FName = Source.Name) and dxPSUtl.ArePointsEqual(Size, Source.Size); end; procedure TdxPaperInfo.Assign(Source: TdxPaperInfo); begin FDMPaper := Source.FDMPaper; FName := Source.FName; FSize := Source.FSize; end; function TdxPaperInfo.GetSize(Index: Integer): Integer; begin with Size do if Index = 0 then Result := X else Result := Y; end; procedure TdxPaperInfo.SetSize(Index: Integer; Value: Integer); begin if DMPaper < DMPAPER_USER then Exit; if Index = 0 then begin FSize.X := Value; if (FPrintDevice <> nil) and (FPrintDevice.FDeviceMode <> nil) then FPrintDevice.DeviceMode^.dmPaperWidth := FSize.X; end else begin FSize.Y := Value; if (FPrintDevice <> nil) and (FPrintDevice.FDeviceMode <> nil) then FPrintDevice.DeviceMode^.dmPaperLength := FSize.Y; end; end; { TdxPrintDevice } constructor TdxPrintDevice.Create; begin inherited Create; FAutoRefresh := {True; //}False; FIsDeviceModePersistent := True; FPrinterIndex := -MaxInt; FWindowHandle := dxPSUtl.dxAllocateHWnd(WndProc); end; destructor TdxPrintDevice.Destroy; begin if Printing then EndDoc; DeselectHandles; SetState(psNoHandle); dxPSUtl.dxDeallocatehWnd(FWindowHandle); FWindowHandle := 0; ClosePrintDevice; FreeAndNilPrinters; inherited; end; procedure TdxPrintDevice.Abort; begin CheckPrinting(True); AbortDoc(Canvas.Handle); FAborted := True; EndDoc; end; function TdxPrintDevice.BeginDoc: Integer; var DocInfo: TDocInfo; begin Application.ProcessMessages; {$IFDEF DEBUG_PRINTDEVICE} RewriteLog; WriteLog('BeginDoc'); {$ENDIF} CheckPrinting(False); SetState(psNoHandle); SetState(psHandleDC); Canvas.Refresh; TdxPrintDeviceCanvas(Canvas).UpdateFont; FPrinting := True; FAborted := False; FPageNumber := 1; FillChar(DocInfo, SizeOf(DocInfo), 0); with DocInfo do begin cbSize := SizeOf(DocInfo); if Title <> '' then lpszDocName := PChar(Title) else lpszDocName := PChar('Document'); if FileName <> '' then lpszOutput := PChar(FileName); end; SetAbortProc(FDC, AbortProc); Result := StartDoc(FDC, DocInfo); if Result > 0 then StartPage(FDC) else begin FPrinting := False; FAborted := False; SetState(psNoHandle); FPageNumber := 0; end; end; procedure TdxPrintDevice.EndDoc; begin {$IFDEF DEBUG_PRINTDEVICE} WriteLog('EndDoc'); {$ENDIF} CheckPrinting(True); // EndPage(FDC); if not Aborted then Windows.EndDoc(FDC); FPrinting := False; FAborted := False; SetState(psNoHandle); FPageNumber := 0; end; procedure TdxPrintDevice.NewPage; begin {$IFDEF DEBUG_PRINTDEVICE} WriteLog('NewPage'); {$ENDIF} CheckPrinting(True); EndPage(FDC); Inc(FPageNumber); Application.ProcessMessages; DoNewPage; ResetDC(False); StartPage(FDC); Canvas.Refresh; end; function TdxPrintDevice.FindPrintDevice(ADevice, APort: PChar ): Integer; begin for Result := 0 to Printers.Count - 1 do if PrinterInfos[Result].IsEqual(nil, ADevice, APort) then Exit; Result := -1; end; function TdxPrintDevice.IsDeviceModeChanged: Boolean; begin Result := FDeviceModeChanged; end; procedure TdxPrintDevice.Refresh; var ADevice, APort: PChar ; APrinterIndex: Integer; AHDeviceMode: THandle; begin ADevice := StrNew(FCurrentDevice); try APort := StrNew(FCurrentPort); try AHDeviceMode := 0; if FHDeviceMode <> 0 then AHDeviceMode := CopyDeviceMode(FHDeviceMode); try ClosePrintDevice; FreeAndNilPrinters; GetPrinters; if AHDeviceMode <> 0 then begin APrinterIndex := FindPrintDevice(ADevice, APort); if APrinterIndex <> -1 then begin OpenPrintDevice(APrinterIndex); if FHDeviceMode <> 0 then begin while GlobalUnLock(FHDeviceMode) do; GlobalFree(FHDeviceMode); FDeviceMode := nil; FHDeviceMode := CopyDeviceMode(AHDeviceMode); FDeviceMode := GlobalLock(FHDeviceMode); end; end else if Printers.Count > 0 then GetPrinterIndex; end; finally if AHDeviceMode <> 0 then GlobalFree(AHDeviceMode); end; finally StrDispose(APort); end; finally StrDispose(ADevice); end; DoRefresh; end; procedure TdxPrintDevice.ResetDC(IsForced: Boolean); var ACanvas: TCanvas; ABrushBitmap: TBitmap; procedure SaveCanvas; begin ACanvas := TdxPrintDeviceCanvas.Create(Self); with ACanvas do begin OnChanging := Canvas.OnChanging; OnChange := Canvas.OnChange; Canvas.OnChanging := nil; Canvas.OnChange := nil; Brush := Canvas.Brush; ABrushBitmap := nil; if Brush.Bitmap <> nil then ABrushBitmap := Brush.Bitmap; Font := Canvas.Font; Pen := Canvas.Pen; PenPos := Canvas.PenPos; CopyMode := Canvas.CopyMode; end; end; procedure RestoreCanvas; begin with Canvas do begin Brush := ACanvas.Brush; if ABrushBitmap <> nil then Brush.Bitmap := ABrushBitmap; Font := ACanvas.Font; Pen := ACanvas.Pen; PenPos := ACanvas.PenPos; CopyMode := ACanvas.CopyMode; OnChanging := ACanvas.OnChanging; OnChange := ACanvas.OnChange; end; ACanvas.Free; end; begin if IsDeviceModeChanged or IsForced then begin FDeviceModeChanged := False; SaveCanvas; try if FDeviceMode <> nil then Windows.ResetDC(FDC, FDeviceMode^); finally RestoreCanvas; end; end; end; procedure TdxPrintDevice.ResetPrintDevice; var Index: Integer; begin Index := PrinterIndex; ClosePrintDevice; OpenPrintDevice(Index); end; function TdxPrintDevice.FindBin(ABin: Integer): Integer; begin if Bins <> nil then Result := Bins.IndexOfObject(TObject(ABin)) else Result := -1; end; function TdxPrintDevice.FindBin(const AName: string): Integer; begin if Bins <> nil then Result := Bins.IndexOf(AName) else Result := -1; end; function TdxPrintDevice.IsAutoSelectBin(AIndex: Integer): Boolean; begin Result := (Bins <> nil) and (AIndex > -1) and (AIndex < Bins.Count) and dxIsAutoSelectBin(Bins[AIndex]); end; function TdxPrintDevice.IsUserPaperSource(AIndex: Integer): Boolean; begin Result := (Bins <> nil) and (AIndex > -1) and (AIndex < Bins.Count) and (Integer(Papers.Objects[AIndex]) >= DMBIN_USER); end; function TdxPrintDevice.SelectBin(Value: Integer): Boolean; var Index: Integer; begin Index := FindBin(Value); Result := Index > -1; if Result then BinIndex := Index; end; function TdxPrintDevice.SelectBin(const AName: string): Boolean; var Index: Integer; begin Index := FindBin(AName); Result := Index > -1; if Result then BinIndex := Index; end; function TdxPrintDevice.FindPaper(ADMPaper: Integer): Integer; begin if Papers <> nil then for Result := 0 to Papers.Count - 1 do if TdxPaperInfo(Papers.Objects[Result]).DMPaper = ADMPaper then Exit; Result := -1; end; function TdxPrintDevice.FindPaper(const AName: string): Integer; begin if Papers <> nil then Result := Papers.IndexOf(AName) else Result := -1; end; function TdxPrintDevice.FindPaper(const ASize: TPoint): Integer; begin if Papers <> nil then begin for Result := 0 to Papers.Count - 1 do if dxPSUtl.ArePointsEqual(TdxPaperInfo(Papers.Objects[Result]).Size, ASize) then Exit; Result := Papers.Count - 1; end else Result := -1; end; function TdxPrintDevice.FindPaper(AWidth, AHeight: Integer): Integer; begin Result := FindPaper(MakePoint(AWidth, AHeight)); end; function TdxPrintDevice.IsEnvelopePaper(AIndex: Integer): Boolean; begin Result := (Papers <> nil) and (AIndex > -1) and (AIndex < Papers.Count) and dxIsEnvelopePaper(Papers[AIndex]); end; function TdxPrintDevice.IsUserPaperSize(AIndex: Integer): Boolean; var PaperInfo: TdxPaperInfo; begin Result := (Papers <> nil) and (AIndex > -1) and (AIndex < Papers.Count); if Result then begin PaperInfo := TdxPaperInfo(Papers.Objects[AIndex]); Result := (PaperInfo.DMPaper >= DMPAPER_USER) or (Pos(cxGetResourceString(@sdxCustom), PaperInfo.Name) > 0); end; end; function TdxPrintDevice.SelectPaper(ADMPaper: Integer): Boolean; var Index: Integer; begin Index := FindPaper(ADMPaper); Result := Index > -1; if Result then PaperIndex := Index; end; function TdxPrintDevice.SelectPaper(const AName: string): Boolean; var Index: Integer; begin Index := FindPaper(AName); Result := Index > -1; if Result then PaperIndex := Index; end; function TdxPrintDevice.SelectPaper(var AWidth, AHeight: Integer): Boolean; var Index: Integer; begin Index := FindPaper(AWidth, AHeight); Result := Index > -1; if Result then begin PaperIndex := Index; // setting FDeviceModeChanged := True; if FDeviceMode <> nil then if PaperIndex <> Papers.Count - 1 then begin DeviceMode^.dmPaperWidth := 0; DeviceMode^.dmPaperLength := 0; end else InternalSelectPaperBySize(AWidth, AHeight) end; end; function TdxPrintDevice.IsSupportColoration: Boolean; begin Result := 1 = WinSpool.DeviceCapabilities(CurrentDevice, CurrentPort, DC_COLORDEVICE, nil, nil); end; function TdxPrintDevice.IsSupportDuplex: Boolean; begin Result := 1 = WinSpool.DeviceCapabilities(CurrentDevice, CurrentPort, DC_DUPLEX, nil, nil); end; procedure TdxPrintDevice.DoNewPage; begin if Assigned(FOnNewPage) then FOnNewPage(Self) end; procedure TdxPrintDevice.DoPrinterChanged; begin if Assigned(FOnPrinterChange) then FOnPrinterChange(Self); end; procedure TdxPrintDevice.DoRefresh; begin if Assigned(FOnRefresh) then FOnRefresh(Self); end; function TdxPrintDevice.GetBinIndex: Integer; begin if FDeviceMode <> nil then begin Result := FindBin(FDeviceMode^.dmDefaultSource); if (Result = -1) and (FBins <> nil) and (FBins.Count <> 0) then Result := 0; end else Result := 0; end; function TdxPrintDevice.GetBins: TStrings; const BinSize = SizeOf(Word); BinNameSize = 24; type TdxBin = Word; TdxBins = array[0..0] of TdxBin; PdxBins = ^TdxBins; TdxBinName = array[0..BinNameSize - 1] of Char; TdxBinNames = array[0..0] of TdxBinName; PdxBinNames = ^TdxBinNames; var BinCount: Integer; BinValues: PdxBins ; BinNames: PdxBinNames ; I: Integer; BinName: string; begin if FBins = nil then try if Printers.Count > 0 then begin GetPrinterIndex; if FDeviceMode = nil then begin Result := nil; Exit; end; BinCount := DeviceCapabilities(CurrentDevice, CurrentPort, DC_BINS, nil, nil); if BinCount > 0 then begin BinValues := AllocMem(BinSize * BinCount * SizeOf(Char)); try if DeviceCapabilities(CurrentDevice, CurrentPort, DC_BINS, PChar(BinValues), nil) <> -1 then begin BinNames := AllocMem(BinNameSize * BinCount * SizeOf(Char)); try if DeviceCapabilities(CurrentDevice, CurrentPort, DC_BINNAMES, PChar(BinNames), nil) <> -1 then begin FBins := TStringList.Create; for I := 0 to BinCount - 1 do begin BinName := BinNames^[I]; FBins.AddObject(BinName, TObject(BinValues^[I])); end; end; finally FreeMem(BinNames); end; end; finally FreeMem(BinValues); end; end; end else begin FBins := TStringList.Create; FBins.AddObject(cxGetResourceString(@sdxDefaultTray), TObject(DMBIN_USER)); end; except FreeAndNil(FBins); raise; end; Result := FBins; end; function TdxPrintDevice.GetCanvas: TCanvas; begin if FCanvas = nil then FCanvas := TdxPrintDeviceCanvas.Create(Self); Result := FCanvas; end; function TdxPrintDevice.GetCollate: Boolean; const CollationMap: array[DMCOLLATE_FALSE..DMCOLLATE_TRUE] of Boolean = (False, True); begin Result := False; if Printers.Count > 0 then begin GetPrinterIndex; if FDeviceMode <> nil then Result := CollationMap[FDeviceMode^.dmCollate]; end; end; function TdxPrintDevice.GetColorMode: Boolean; begin Result := False; if Printers.Count > 0 then begin GetPrinterIndex; if FDeviceMode <> nil then Result := FDeviceMode^.dmColor = DMCOLOR_COLOR; end; end; function TdxPrintDevice.GetCurrentDevice: PChar ; begin Result := nil; if Printers.Count > 0 then begin GetPrinterIndex; if FHDeviceMode <> 0 then Result := FCurrentDevice; end; end; function TdxPrintDevice.GetCurrentDriver: PChar ; begin Result := nil; if Printers.Count > 0 then begin GetPrinterIndex; if FHDeviceMode <> 0 then Result := FCurrentDriver; end; end; function TdxPrintDevice.GetCurrentPort: PChar ; begin Result := nil; if Printers.Count > 0 then begin GetPrinterIndex; if FHDeviceMode <> 0 then Result := FCurrentPort; end; end; function TdxPrintDevice.GetDefaultDMPaper: Integer; begin if Printers.Count > 0 then begin GetPrinterIndex; if FDeviceMode <> nil then Result := DeviceMode^.dmPaperSize else Result := Windows.DMPAPER_FIRST; end else Result := Windows.DMPAPER_FIRST; end; function TdxPrintDevice.GetDeviceMode: PDeviceMode; begin if Printers.Count > 0 then begin GetPrinterIndex; Result := FDeviceMode; end else Result := nil; end; function TdxPrintDevice.GetDuplex: TdxDuplexMode; begin Result := dmSimplex; if Printers.Count > 0 then begin GetPrinterIndex; if FDeviceMode <> nil then Result := DuplexModeMap[DeviceMode^.dmDuplex]; end; end; function EnumFontsProc(var LogFont: TLogFont; var TextMetric: TTextMetric; FontType: DWORD; Data: LPARAM): Integer; stdcall; begin TStrings(Data).Add(LogFont.lfFaceName); Result := 1; end; function TdxPrintDevice.GetFonts: TStrings; begin if FFonts = nil then try SetState(psHandleIC); FFonts := TStringList.Create; EnumFonts(FDC, nil, @EnumFontsProc, Pointer(FFonts)); except FreeAndNil(FFonts); raise; end; Result := FFonts; end; function TdxPrintDevice.GetHandle: HDC; begin SetState(psHandleIC); Result := FDC; end; function TdxPrintDevice.GetHDeviceMode: THandle; begin if Printers.Count > 0 then begin GetPrinterIndex; Result := FHDeviceMode; end else Result := 0; end; function TdxPrintDevice.GetIsDefault: Boolean; begin if PrinterIndex <> -1 then Result := PrinterInfos[PrinterIndex].IsDefault else Result := False; end; function TdxPrintDevice.GetIsInitialized: Boolean; begin Result := FPrinters <> nil; end; function TdxPrintDevice.GetIsNetwork: Boolean; begin if PrinterIndex <> -1 then Result := PrinterInfos[PrinterIndex].IsNetwork else Result := False; end; function TdxPrintDevice.GetMaxExtents(Index: Integer): Integer; begin if Printers.Count > 0 then begin GetPrinterIndex; if Index = 0 then Result := LOWORD(FMaxExtents) else Result := HIWORD(FMaxExtents); end else Result := dxDefaultMaxPaperExtents; end; function TdxPrintDevice.GetMinExtents(Index: Integer): Integer; begin if Printers.Count > 0 then begin GetPrinterIndex; if Index = 0 then Result := LOWORD(FMinExtents) else Result := HIWORD(FMinExtents); end else Result := dxDefaultMinPaperExtents; end; function TdxPrintDevice.GetNumCopies: Integer; begin Result := 0; if Printers.Count > 0 then begin GetPrinterIndex; if FDeviceMode <> nil then Result := FDeviceMode^.dmCopies; end; end; function TdxPrintDevice.GetOrientation: TdxPrinterOrientation; begin Result := poPortrait; if Printers.Count > 0 then begin GetPrinterIndex; if FDeviceMode <> nil then if FDeviceMode^.dmOrientation = DMORIENT_LANDSCAPE then Result := poLandscape; end; end; function TdxPrintDevice.GetPageHeight: Integer; begin SetState(psHandleIC); Result := GetDeviceCaps(FDC, VertRes); end; function TdxPrintDevice.GetPageHeightLoMetric: Integer; begin SetState(psHandleIC); Result := 10 * GetDeviceCaps(FDC, VertSize); end; function TdxPrintDevice.GetPageWidth: Integer; begin SetState(psHandleIC); Result := GetDeviceCaps(FDC, HorzRes); end; function TdxPrintDevice.GetPageWidthLoMetric: Integer; begin SetState(psHandleIC); Result := 10 * GetDeviceCaps(FDC, HorzSize); end; function TdxPrintDevice.GetPaperCount: Integer; begin if FPapers <> nil then Result := FPapers.Count else Result := 0; end; function TdxPrintDevice.GetPaperIndex: Integer; begin if FDeviceMode <> nil then begin Result := FindPaper(FDeviceMode^.dmPaperSize); if (Result = -1) and (FPapers <> nil) and (FPapers.Count > 0) then Result := 0; end else Result := 0; end; function TdxPrintDevice.GetPapers: TStrings; const PaperNameSize = 64; PaperValueSize = SizeOf(Word); PaperSizeSize = SizeOf(TPoint); type TdxPaperSize = TPoint; TdxPaperSizes = array[0..0] of TdxPaperSize; PdxPaperSizes = ^TdxPaperSizes; TdxPaperValue = Word; TdxPaperValues = array[0..0] of TdxPaperValue; PdxPaperValues = ^TdxPaperValues; TdxPaperName = array[0..PaperNameSize - 1] of Char; TdxPaperNames = array[0..0] of TdxPaperName; PdxPaperNames = ^TdxPaperNames; var HasStandardPapers: Boolean; PaperCount: Integer; PaperNames: PdxPaperNames ; PaperValues: PdxPaperValues ; PaperSizes: PdxPaperSizes ; I: Integer; Paper: TdxPaperInfo; begin if FPapers = nil then try HasStandardPapers := False; if Printers.Count > 0 then begin GetPrinterIndex; if DeviceMode = nil then begin Result := nil; Exit; end; if FPapers <> nil then begin Result := FPapers; Exit; end; PaperCount := DeviceCapabilities(CurrentDevice, CurrentPort, DC_PAPERNAMES, nil, nil); if PaperCount > 0 then begin PaperNames := AllocMem(PaperNameSize * PaperCount * SizeOf(Char)); try if DeviceCapabilities(CurrentDevice, CurrentPort, DC_PAPERNAMES, PChar(PaperNames), nil) <> -1 then begin PaperValues := AllocMem(PaperValueSize * PaperCount); try if DeviceCapabilities(CurrentDevice, CurrentPort, DC_PAPERS, PChar(PaperValues), nil) <> -1 then begin PaperSizes := AllocMem(PaperSizeSize * PaperCount); try if DeviceCapabilities(CurrentDevice, CurrentPort, DC_PAPERSIZE, PChar(PaperSizes), nil) <> -1 then begin HasStandardPapers := True; FPapers := TStringList.Create; for I := 0 to PaperCount - 1 do begin Paper := TdxPaperInfo.Create(Self); with Paper do begin FSize := PaperSizes^[I]; FDMPaper := PaperValues^[I]; FName := PaperNames^[I]; end; FPapers.AddObject(Paper.Name, Paper); end; if Pos(cxGetResourceString(@sdxCustom), FPapers[PaperCount - 1]) = 0 then begin Paper := TdxPaperInfo.Create(Self); with Paper do begin FSize := TdxPaperInfo(FPapers.Objects[0]).Size; FDMPaper := DMPAPER_USER; FName := cxGetResourceString(@sdxCustomSize); end; FPapers.AddObject(Paper.Name, Paper); end; end; finally FreeMem(PaperSizes); end; end; finally FreeMem(PaperValues); end; end; finally FreeMem(PaperNames); end; end; end; if not HasStandardPapers then begin FPapers := TStringList.Create; Paper := TdxPaperInfo.Create(Self); with Paper do begin FSize := Point(2100, 2970); {A4} // TODO: use default locale paper FDMPaper := DMPAPER_USER; FName := cxGetResourceString(@sdxCustomSize); end; FPapers.AddObject(Paper.Name, Paper); end; except FreeAndNil(FPapers); raise; end; Result := FPapers; end; function TdxPrintDevice.GetPhysOffset(Index: Integer): Integer; const PhysicalOffsets: array[0..1] of Integer = (PHYSICALOFFSETX, PHYSICALOFFSETY); begin if Printers.Count > 0 then begin GetPrinterIndex; try Result := GetDeviceCaps(Handle, PhysicalOffsets[Index]); except Result := 0; end; end else Result := 0; end; function TdxPrintDevice.GetPrinterCount: Integer; begin if FPrinters <> nil then Result := FPrinters.Count else Result := 0; end; function TdxPrintDevice.GetPrinterIndex: Integer; begin if FPrinterIndex = -MaxInt then SetToDefaultPrintDevice; Result := FPrinterIndex; end; function TdxPrintDevice.GetPrinterInfo(Index: Integer): TdxPrintDeviceInfo; begin if (FPrinters <> nil) and (Index >= 0) and (Index < FPrinters.Count) then Result := TdxPrintDeviceInfo(FPrinters.Objects[Index]) else Result := nil; end; function TdxPrintDevice.GetPrinters: TStrings; function IsDefaultDevice(AnAttributes: DWORD): Boolean; begin Result := AnAttributes and PRINTER_ATTRIBUTE_DEFAULT = PRINTER_ATTRIBUTE_DEFAULT; end; function IsNetworkDevice(AnAttributes: DWORD): Boolean; begin Result := AnAttributes and PRINTER_ATTRIBUTE_NETWORK = PRINTER_ATTRIBUTE_NETWORK; end; procedure GetAsPrinterInfo4(var AnOffset: Integer; ABuffer: PChar); var PrintDeviceInfo: TdxPrintDeviceInfo; begin Inc(PByte(ABuffer), AnOffset); with PPrinterInfo4(ABuffer)^ do begin PrintDeviceInfo := TdxPrintDeviceInfo.Create('', pPrinterName, '', dxIsDefaultPrinter(pPrinterName), IsNetworkDevice(Attributes)); FPrinters.AddObject(pPrinterName, PrintDeviceInfo); end; Inc(AnOffset, SizeOf(TPrinterInfo4)); end; procedure GetAsPrinterInfo5(var AnOffset: Integer; ABuffer: PChar); var LineCur, PortName: PChar; PrintDeviceTitle: string; PrintDeviceInfo: TdxPrintDeviceInfo; begin Inc(PByte(ABuffer), AnOffset); with PPrinterInfo5(ABuffer)^ do begin LineCur := pPortName; PortName := FetchStr(LineCur); while PortName^ <> #0 do begin PrintDeviceTitle := Format(cxGetResourceString(@sdxDeviceOnPort), [pPrinterName, PortName]); PrintDeviceInfo := TdxPrintDeviceInfo.Create(nil, pPrinterName, PortName, dxIsDefaultPrinter(pPrinterName), IsNetworkDevice(Attributes)); FPrinters.AddObject(PrintDeviceTitle, PrintDeviceInfo); PortName := FetchStr(LineCur); end; end; Inc(AnOffset, SizeOf(TPrinterInfo5)); end; const Levels: array[Boolean] of DWORD = (5, 4); Flags: array[Boolean] of DWORD = (PRINTER_ENUM_LOCAL, PRINTER_ENUM_CONNECTIONS or PRINTER_ENUM_LOCAL); var Level, Flag, Count, NumInfo: DWORD; Buffer: Pointer; Offset, I: Integer; begin if FPrinters = nil then begin FPrinters := TStringList.Create; try Level := Levels[IsWinNT]; Flag := Flags[IsWinNT]; Count := 0; EnumPrinters(Flag, nil, Level, nil, 0, Count, NumInfo); if Count <> 0 then begin Buffer := dxAllocMem(Count); try if EnumPrinters(Flag, nil, Level, Buffer, Count, Count, NumInfo) then begin Offset := 0; for I := 0 to NumInfo - 1 do if Level = 4 then GetAsPrinterInfo4(Offset, Buffer) else GetAsPrinterInfo5(Offset, Buffer); end; finally dxFreeMem(Buffer, Count); end; end; except FreeAndNil(FPrinters); raise; end; end; Result := FPrinters; end; procedure TdxPrintDevice.SetBinIndex(Value: Integer); begin if (FDeviceMode <> nil) and (Bins <> nil) and (Value > -1) and (Value < Bins.Count) then begin if FDeviceMode^.dmDefaultSource <> Value then begin FDeviceMode^.dmDefaultSource := Integer(Bins.Objects[Value]); FDeviceMode^.dmFields := FDeviceMode^.dmFields or DM_DEFAULTSOURCE; FDeviceModeChanged := True; end; end; end; procedure TdxPrintDevice.SetCollate(Value: Boolean); const Collations: array[Boolean] of ShortInt = (DMCOLLATE_FALSE, DMCOLLATE_TRUE); begin //CheckPrinting(False); if Printers.Count > 0 then begin GetPrinterIndex; if FDeviceMode <> nil then begin FDeviceMode^.dmCollate := Collations[Value]; FDeviceMode^.dmFields := FDeviceMode^.dmFields or DM_COLLATE; FDeviceModeChanged := True; end; end; end; procedure TdxPrintDevice.SetColorMode(Value: Boolean); const ColorModes: array[Boolean] of ShortInt = (DMCOLOR_MONOCHROME, DMCOLOR_COLOR); begin //CheckPrinting(False); if Printers.Count > 0 then begin GetPrinterIndex; if FDeviceMode <> nil then begin FDeviceMode^.dmColor := ColorModes[Value]; FDeviceMode^.dmFields := FDeviceMode^.dmFields or DM_COLOR; FDeviceModeChanged := True; end; end; end; procedure TdxPrintDevice.SetDuplex(Value: TdxDuplexMode); const DuplexModeMap: array[TdxDuplexMode] of UINT = (DMDUP_SIMPLEX, DMDUP_VERTICAL, DMDUP_HORIZONTAL); begin //CheckPrinting(False); if Printers.Count > 0 then begin GetPrinterIndex; if (FDeviceMode <> nil) and IsSupportDuplex then begin FDeviceMode^.dmDuplex := DuplexModeMap[Value]; FDeviceMode^.dmFields := FDeviceMode^.dmFields or DM_DUPLEX; FDeviceModeChanged := True; end; end; end; procedure TdxPrintDevice.SetIsDefault(Value: Boolean); var I: Integer; begin if (PrinterIndex <> -1) and (IsDefault <> Value) then begin for I := 0 to Printers.Count - 1 do PrinterInfos[I].IsDefault := False; dxSetDefaultPrinter(CurrentDevice); end end; procedure TdxPrintDevice.SetNumCopies(Value: Integer); begin CheckPrinting(False); if Printers.Count > 0 then begin GetPrinterIndex; if FDeviceMode <> nil then begin FDeviceMode^.dmCopies := Value; FDeviceMode^.dmFields := FDeviceMode^.dmFields or DM_COPIES; FDeviceModeChanged := True; end; end; end; procedure TdxPrintDevice.SetOrientation(Value: TdxPrinterOrientation); begin //CheckPrinting(False); if Printers.Count > 0 then begin GetPrinterIndex; if FDeviceMode <> nil then begin FDeviceMode^.dmOrientation := PrinterOrientationMap[Value]; FDeviceMode^.dmFields := FDeviceMode^.dmFields or DM_ORIENTATION; FDeviceModeChanged := True; end; end; end; procedure TdxPrintDevice.SetPaperIndex(Value: Integer); begin if (FDeviceMode <> nil) and (Papers <> nil) and (Value > -1) and (Value < Papers.Count) then begin FDeviceMode^.dmPaperSize := TdxPaperInfo(Papers.Objects[Value]).DMPaper; FDeviceMode^.dmFields := FDeviceMode^.dmFields or DM_PAPERSIZE; if Value = Papers.Count - 1 then FDeviceMode^.dmFields := FDeviceMode^.dmFields or DM_PAPERWIDTH or DM_PAPERLENGTH; FDeviceModeChanged := True; end; end; procedure TdxPrintDevice.SetPrinterCapabilities(Value: Integer); begin FCapabilities := []; if Value and DM_ORIENTATION <> 0 then Include(FCapabilities, pcOrientation); if Value and DM_COPIES <> 0 then Include(FCapabilities, pcCopies); if Value and DM_COLLATE <> 0 then Include(FCapabilities, pcCollation); // if Value and DM_DUPLEX <> 0 then Include(FCapabilities, pcDuplex); // if Value and DM_COLLATE <> 0 then Include(FCapabilities, pcColor); end; procedure TdxPrintDevice.SetPrinterIndex(Value: Integer); begin CheckPrinting(False); if (Value < -1) or (Value >= Printers.Count) then RaiseError(cxGetResourceString(@sdxPrinterIndexError)) else if Value = -1 then begin FPrinterIndex := -MaxInt; GetPrinterIndex; end else OpenPrintDevice(Value); SetState(psNoHandle); end; procedure TdxPrintDevice.CheckPrinting(Value: Boolean); begin if Printing <> Value then if Value then RaiseError(cxGetResourceString(@sdxNotPrinting)) else RaiseError(cxGetResourceString(@sdxPrinting)); end; procedure TdxPrintDevice.ClosePrintDevice; begin StrDispose(FCurrentDevice); FCurrentDevice := nil; StrDispose(FCurrentDriver); FCurrentDriver := nil; StrDispose(FCurrentPort); FCurrentPort := nil; FreeAndNilBins; FreeAndNilFonts; FreeAndNilPapers; if FHDeviceMode <> 0 then begin while GlobalUnlock(FHDeviceMode) do; GlobalFree(FHDeviceMode); //Marshal.DestroyStructure(DeviceMode, TypeOf(TDeviceMode)); FHDeviceMode := 0; FDeviceMode := nil; end; if FDeviceHandle <> 0 then begin ClosePrinter(FDeviceHandle); FDeviceHandle := 0; end; SetState(psNoHandle); FreeAndNilCanvas; FPrinterIndex := -MaxInt; end; procedure TdxPrintDevice.DeselectHandles; begin if FDC <> 0 then begin SelectObject(FDC, GetStockObject(BLACK_PEN)); SelectObject(FDC, GetStockObject(HOLLOW_BRUSH)); SelectObject(FDC, GetStockObject(SYSTEM_FONT)); end; end; procedure TdxPrintDevice.FixMinMaxExtents; var I: Integer; MaxSize, MinSize, PaperSize: TPoint; begin if (FMaxExtents = -1) or (FMaxExtents = 0) then FMaxExtents := SetLoHiWords(FMaxExtents, cMaxPaperExtent, cMaxPaperExtent); if (FMinExtents = -1) or (FMinExtents = 0) then FMinExtents := SetLoHiWords(FMinExtents, cMinPaperExtent, cMinPaperExtent); if Papers <> nil then begin MaxSize := Point(LoWord(FMaxExtents), HiWord(FMaxExtents)); MinSize := Point(LoWord(FMinExtents), HiWord(FMinExtents)); for I := 0 to Papers.Count - 1 do begin PaperSize := TdxPaperInfo(Papers.Objects[I]).Size; if PaperSize.X < MinSize.X then MinSize.X := PaperSize.X; if PaperSize.X > MaxSize.X then MaxSize.X := PaperSize.X; if PaperSize.Y < MinSize.Y then MinSize.Y := PaperSize.Y; if PaperSize.Y > MaxSize.Y then MaxSize.Y := PaperSize.Y; end; if MinSize.X <> LoWord(FMinExtents) then FMinExtents := SetLoWord(FMinExtents, MinSize.X); if MinSize.Y <> HiWord(FMinExtents) then FMinExtents := SetHiWord(FMinExtents, MinSize.Y); if MaxSize.X <> LoWord(FMaxExtents) then FMaxExtents := SetLoWord(FMaxExtents, MaxSize.X); if MaxSize.Y <> HiWord(FMaxExtents) then FMaxExtents := SetHiWord(FMaxExtents, MaxSize.Y); end; if LoWord(FMinExtents) < 500 then FMinExtents := SetLoWord(FMinExtents, 500); if HiWord(FMinExtents) < 500 then FMinExtents := SetHiWord(FMinExtents, 500); if LoWord(FMaxExtents) < LoWord(FMinExtents) then FMaxExtents := SetLoWord(FMaxExtents, LoWord(FMinExtents)); if HiWord(FMaxExtents) < HiWord(FMinExtents) then FMaxExtents := SetHiWord(FMaxExtents, HiWord(FMinExtents)); end; procedure TdxPrintDevice.FreeAndNilBins; begin FreeAndNil(FBins); end; procedure TdxPrintDevice.FreeAndNilFonts; begin FreeAndNil(FFonts); end; procedure TdxPrintDevice.FreeAndNilCanvas; begin FreeAndNil(FCanvas); end; procedure TdxPrintDevice.FreeAndNilPapers; var I: Integer; begin for I := 0 to PaperCount - 1 do FPapers.Objects[I].Free; FreeAndNil(FPapers); end; procedure TdxPrintDevice.FreeAndNilPrinters; var I: Integer; begin for I := 0 to PrinterCount - 1 do PrinterInfos[I].Free; FreeAndNil(FPrinters); end; procedure TdxPrintDevice.InternalSelectPaperBySize(var AWidth, AHeight: Integer); var DeviceMode: TDeviceMode; begin if AWidth > MaxExtentX then AWidth := MaxExtentX; if AWidth < MinExtentX then AWidth := MinExtentX; if AHeight > MaxExtentY then AHeight := MaxExtentY; if AHeight < MinExtentY then AHeight := MinExtentY; if FDeviceMode <> nil then begin DeviceMode := FDeviceMode^; if DeviceMode.dmPaperWidth <> AWidth then TdxPaperInfo(Papers.Objects[Papers.Count - 1]).Width := AWidth; if DeviceMode.dmPaperLength <> AHeight then TdxPaperInfo(Papers.Objects[Papers.Count - 1]).Height := AHeight; end; end; procedure TdxPrintDevice.OpenPrintDevice(AIndex: Integer); const dmFields: UINT = DM_ORIENTATION or DM_PAPERSIZE or DM_PAPERLENGTH or DM_PAPERWIDTH or DM_SCALE or DM_COPIES or DM_DEFAULTSOURCE or DM_PRINTQUALITY or DM_COLOR or DM_DUPLEX or DM_YRESOLUTION or DM_TTOPTION or DM_COLLATE or DM_FORMNAME or DM_LOGPIXELS or DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT; var DeviceMode: TDeviceMode; MemSize: Integer; SaveHDeviceMode: THandle; SavePaperWidth, SavePaperHeight: Integer; procedure RestoreDeviceMode(ASaveHDeviceMode: THandle); var PDevMode: Pointer ; DevMode: TDeviceMode; begin PDevMode := GlobalLock(ASaveHDeviceMode); try DevMode := PDeviceMode(PDevMode)^; Copies := DevMode.dmCopies; Duplex := DuplexModeMap[DevMode.dmDuplex]; Orientation := OrientationMap[DevMode.dmOrientation]; if not SelectPaper(DevMode.dmPaperSize) then SelectPaper(SavePaperWidth, SavePaperHeight); SelectBin(DevMode.dmDefaultSource); finally GlobalUnlock(ASaveHDeviceMode); end; end; begin if FPrinterIndex = AIndex then Exit; SaveHDeviceMode := 0; if IsDeviceModePersistent and (FHDeviceMode <> 0) then begin SaveHDeviceMode := CopyDeviceMode(FHDeviceMode); if (Papers <> nil) and (PaperIndex > -1) and (PaperIndex < Papers.Count) then with TdxPaperInfo(Papers.Objects[PaperIndex]) do begin SavePaperWidth := Width; SavePaperHeight := Height; end; end; try ClosePrintDevice; with PrinterInfos[AIndex] do begin FCurrentDevice := StrNew(FDevice); FCurrentDriver := StrNew(FDriver); FCurrentPort := StrNew(FPort); end; if OpenPrinter(FCurrentDevice, FDeviceHandle, nil) then begin MemSize := DocumentProperties(0, FDeviceHandle, FCurrentDevice, nil, nil, 0); if MemSize <= 0 then begin ClosePrintDevice; Exit; end; FHDeviceMode := GlobalAlloc(GHND, MemSize); if FHDeviceMode = 0 then begin ClosePrintDevice; Exit; end; FDeviceMode := GlobalLock(FHDeviceMode); FDeviceMode^.dmFields := dmFields; if IDOK <> DocumentProperties(0, FDeviceHandle, FCurrentDevice, FDeviceMode, nil, DM_OUT_BUFFER) then begin ClosePrintDevice; Exit; end; FPrinterIndex := AIndex; SetPrinterCapabilities(DeviceMode.dmFields); FMaxCopies := DeviceCapabilities(FCurrentDevice, FCurrentPort, DC_COPIES, nil, nil); FMaxExtents := DeviceCapabilities(FCurrentDevice, FCurrentPort, DC_MAXEXTENT, nil, nil); FMinExtents := DeviceCapabilities(FCurrentDevice, FCurrentPort, DC_MINEXTENT, nil, nil); FixMinMaxExtents; if IsDeviceModePersistent and (SaveHDeviceMode <> 0) then RestoreDeviceMode(SaveHDeviceMode); DoPrinterChanged; end; finally if SaveHDeviceMode <> 0 then GlobalFree(SaveHDeviceMode); end; end; procedure TdxPrintDevice.SetState(Value: TdxPrinterState); type TCreateHandleFunc = function(DriverName, DeviceName, Output: PChar; InitData: PDeviceMode): HDC stdcall; var CreateHandleFunc: TCreateHandleFunc; begin if Value <> FState then begin CreateHandleFunc := nil; case Value of psNoHandle: begin CheckPrinting(False); if FCanvas <> nil then FCanvas.Handle := 0; DeleteDC(FDC); FDC := 0; end; psHandleIC: if FState <> psHandleDC then CreateHandleFunc := CreateIC else Exit; psHandleDC: begin if FCanvas <> nil then FCanvas.Handle := 0; if FDC <> 0 then DeleteDC(FDC); CreateHandleFunc := CreateDC; end; end; if Assigned(CreateHandleFunc) and (PrinterIndex >= 0) and (PrinterIndex < PrinterCount) then with PrinterInfos[PrinterIndex] do begin FDC := CreateHandleFunc(Driver, Device, Port, FDeviceMode); if FDC = 0 then RaiseError(cxGetResourceString(@sdxInvalidPrintDevice)); if FCanvas <> nil then FCanvas.Handle := FDC; end; FState := Value; end; end; procedure TdxPrintDevice.SetToDefaultPrintDevice; var Device: string; Index: Integer; begin Device := dxGetDefaultPrinter; if Device <> '' then begin Index := FindPrintDevice(PChar(Device), nil); if Index <> -1 then begin OpenPrintDevice(Index); Exit; end; end; RaiseError(cxGetResourceString(@sdxNoDefaultPrintDevice)); end; procedure TdxPrintDevice.WndProc(var Message: TMessage); procedure PostPrnMessage; begin PostMessage(FWindowHandle, WMPS_PRINTERLISTCHANGED, 0, 0); end; function PeekPrnMessage(ARemove: Boolean): Boolean; const RemoveMap: array[Boolean] of UINT = (PM_NOREMOVE, PM_REMOVE); var Msg: TMsg; begin Result := PeekMessage(Msg, FWindowHandle, WMPS_PRINTERLISTCHANGED, WMPS_PRINTERLISTCHANGED, RemoveMap[ARemove]); end; begin with Message do begin case Msg of WM_SETTINGCHANGE: if AutoRefresh then PostPrnMessage; WMPS_PRINTERLISTCHANGED: if AutoRefresh then if not FPrinting then begin while PeekPrnMessage(True) do ; Refresh; end else if not PeekPrnMessage(False) then PostPrnMessage; end; Result := DefWindowProc(FWindowHandle, Msg, wParam, lParam); end; end; var LibHandle: THandle = 0; procedure InitializeProcs; begin LibHandle := LoadLibrary(winspl); if LibHandle > HINSTANCE_ERROR then begin @FConnectToPrinterDlg := GetProcAddress(LibHandle, 'ConnectToPrinterDlg'); @FGetDefaultPrinter := GetProcAddress(LibHandle, 'GetDefaultPrinterA'); @FSetDefaultPrinter := GetProcAddress(LibHandle, 'SetDefaultPrinterA'); end else LibHandle := 0; end; procedure DeinitializeProcs; begin if LibHandle <> 0 then begin @FConnectToPrinterDlg := nil; @FGetDefaultPrinter := nil; @FSetDefaultPrinter := nil; FreeLibrary(LibHandle); LibHandle := 0; end; end; initialization InitializeProcs; {$IFDEF DEBUG_PRINTDEVICE} AssignFile(LogFile, 'PrinterLog.txt'); Rewrite(LogFile); {$ENDIF} finalization {$IFDEF DEBUG_PRINTDEVICE} CloseFile(LogFile); {$ENDIF} FreeAndNil(FPrintDevice); DeinitializeProcs; end.