mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-11 12:28:48 +02:00
1042 lines
26 KiB
PHP
1042 lines
26 KiB
PHP
{%MainUnit ../osprinters.pas}
|
|
{**************************************************************
|
|
Implementation for winprinter
|
|
***************************************************************}
|
|
uses
|
|
InterfaceBase, LCLIntf, WinVer, WinUtilPrn
|
|
{todo: use WinSpool when it will be released with fpc, WinSpool};
|
|
|
|
// todo: this ^ is a mess: mixed WinUtilPrn/Windows units clean...
|
|
|
|
// todo: this should be a method, can not be atm because mixed units ^
|
|
|
|
|
|
function GetCurrentDevModeW(out DM:PDeviceModeW): Boolean;
|
|
var
|
|
PDev: TPrinterDevice;
|
|
begin
|
|
Result := false;
|
|
if (Printer.Printers.Count > 0) then
|
|
begin
|
|
PDev := TPrinterDevice(Printer.Printers.Objects[Printer.PrinterIndex]);
|
|
DM := PDev.DevModeW;
|
|
Result := DM <> nil;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TWinPrinter }
|
|
|
|
constructor TWinPrinter.Create;
|
|
begin
|
|
inherited Create;
|
|
|
|
fLastHandleType := htNone;
|
|
fPrinterHandle := 0; //None
|
|
end;
|
|
|
|
procedure TWinPrinter.DoDestroy;
|
|
begin
|
|
ClearDC;
|
|
|
|
DoResetPrintersList;
|
|
|
|
if fPrinterHandle <> 0 then
|
|
ClosePrinter(fPrinterHandle);
|
|
|
|
inherited DoDestroy;
|
|
end;
|
|
|
|
function TWinPrinter.Write(const Buffer; Count: Integer; out Written: Integer): Boolean;
|
|
begin
|
|
CheckRawMode(True);
|
|
Result := WritePrinter(FPrinterHandle, @Buffer, Count, pdword(@Written));
|
|
end;
|
|
|
|
function TWinPrinter.GetHandlePrinter : HDC;
|
|
begin
|
|
SetIC;
|
|
Result := fDC;
|
|
end;
|
|
|
|
|
|
procedure TWinPrinter.SetHandlePrinter(aValue : HDC);
|
|
begin
|
|
CheckRawMode(False);
|
|
if aValue <> fDC then
|
|
begin
|
|
ClearDC;
|
|
fDC := aValue;
|
|
if Assigned(Canvas) then
|
|
Canvas.Handle := fDC;
|
|
fLastHandleType := htDC;
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.RawModeChanging;
|
|
begin
|
|
// if old mode was standard free DC if it was created
|
|
if not RawMode and (fDC <> 0) then
|
|
FreeDC;
|
|
end;
|
|
|
|
procedure TWinPrinter.PrinterSelected;
|
|
begin
|
|
if ([pfDestroying, pfRawMode]*PrinterFlags=[]) and (PrinterIndex>=0) then
|
|
SetDC;
|
|
end;
|
|
|
|
function TWinPrinter.GetXDPI: Integer;
|
|
begin
|
|
Result:=72;
|
|
if (Printers.Count > 0) and not RawMode then
|
|
begin
|
|
SetDC;
|
|
Result:=windows.GetDeviceCaps(fDC, LOGPIXELSX);
|
|
end;
|
|
end;
|
|
|
|
function TWinPrinter.GetYDPI: Integer;
|
|
begin
|
|
Result:=72;
|
|
if (Printers.Count>0) and not RawMode then
|
|
begin
|
|
SetDC;
|
|
Result:=windows.GetDeviceCaps(fDC,LOGPIXELSY);
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.SetIC;
|
|
var PDev : TPrinterDevice;
|
|
begin
|
|
if (fLastHandleType=htNone) and (Printers.Count>0) then
|
|
begin
|
|
PDev:=TPrinterDevice(Printers.Objects[PrinterIndex]);
|
|
fDC:=CreateICW(
|
|
PWidechar(UTF8Decode(PDev.Driver)),
|
|
PWidechar(UTF8Decode(PDev.Device)),
|
|
PWidechar(UTF8Decode(PDev.Port)),
|
|
PDev.DevModeW);
|
|
|
|
if fDC=0 then
|
|
begin
|
|
fDC:=CreateICW(
|
|
PWidechar('WINSPOOL'),
|
|
PWidechar(UTF8Decode(PDev.Device)),
|
|
PWidechar(UTF8Decode(PDev.Port)),
|
|
PDev.DevModeW);
|
|
end;
|
|
if fDC=0 then
|
|
raise EPrinter.Create(
|
|
Format('Invalid printer (DC=%d Driver=%s Device=%s Port=%s)',
|
|
[fDC,Pdev.Driver,PDev.Device,PDev.Port]));
|
|
|
|
if Assigned(Canvas) then
|
|
Canvas.Handle:=fDC;
|
|
|
|
fLastHandleType:=htIC;
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.SetDC;
|
|
var PDev : TPrinterDevice;
|
|
begin
|
|
|
|
if (fLastHandleType<>htDC) and (Printers.Count>0) then
|
|
begin
|
|
ClearDC;
|
|
PDev:=TPrinterDevice(Printers.Objects[PrinterIndex]);
|
|
try
|
|
|
|
//Device is only 32 chars long,
|
|
//if the Printername or share is longer than 32 chars, this will return 0
|
|
fDC := CreateDCW(nil, PWidechar(UTF8Decode(PDev.Name)), nil, PDev.DevModeW);
|
|
if fDC=0 then
|
|
begin
|
|
fDC := CreateDCW(PWidechar('WINSPOOL'),PWidechar(UTF8Decode(PDev.Name)), nil, PDev.DevModeW);
|
|
end;
|
|
{Workaround (hack) for Lexmark 1020 JetPrinter (Mono)}
|
|
if fDC=0 then
|
|
begin
|
|
fDC:=CreateDCW(nil,PWidechar(UTF8Decode(PDev.Driver)),nil, PDev.DevModeW);
|
|
end;
|
|
if fDC=0 then
|
|
begin
|
|
fDC:=CreateDCW(PWideChar('WINSPOOL'),PWideChar(UTF8Decode(PDev.Driver)),nil,PDev.DevModeW);
|
|
end;
|
|
except on E:Exception do
|
|
raise EPrinter.Create(Format('CreateDC Exception:"%s" (Error:"%s", '+
|
|
'DC=%d Driver="%s" Device="%s" Port="%s")', [E.Message,
|
|
SysErrorMessage(GetLastError),fDC, Pdev.Driver,
|
|
Printers[PrinterIndex],PDev.Port]));
|
|
end;
|
|
|
|
if fDC=0 then
|
|
raise EPrinter.Create(Format('Invalid printer (Error:%s, '+
|
|
'DC=%d Driver="%s" Device="%s" Port="%s")',
|
|
[SysErrorMessage(GetLastError),fDC,Pdev.Driver,Printers[PrinterIndex],
|
|
PDev.Port]));
|
|
|
|
if Assigned(Canvas) then
|
|
Canvas.Handle:=fDC;
|
|
|
|
fLastHandleType:=htDC;
|
|
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.ClearDC;
|
|
begin
|
|
if not RawMode then
|
|
FreeDC
|
|
end;
|
|
|
|
procedure TWinPrinter.FreeDC;
|
|
begin
|
|
if Assigned(Canvas) then
|
|
Canvas.Handle:=0;
|
|
|
|
if fDC<>0 then
|
|
begin
|
|
DeleteDC(fDC);
|
|
fDc := 0;
|
|
end;
|
|
|
|
fLastHandleType:=htNone;
|
|
end;
|
|
|
|
// Based on MS Article Q167345
|
|
function TWinPrinter.UpdateDevMode(APrinterIndex:Integer): boolean;
|
|
var
|
|
PDev: TPrinterDevice;
|
|
dwRet: Integer;
|
|
begin
|
|
if FPrinterHandle=0 then begin
|
|
result := false;
|
|
exit;
|
|
end;
|
|
|
|
// now we have a right FPrinterHandle, get current printer settings
|
|
PDev := TPrinterDevice(Printers.Objects[APrinterIndex]);
|
|
|
|
// 1. Determine the required size of the buffer from the device,
|
|
// and then allocate enough memory for it.
|
|
PDev.DevModeSize := DocumentPropertiesW(0, FPrinterHandle, Pwidechar(UTF8Decode(PDev.Name)),
|
|
nil, nil, 0);
|
|
if PDev.DevModeSize>0 then
|
|
ReallocMem(Pdev.DevModeW, PDev.DevModeSize);
|
|
|
|
if PDev.DevModeSize<=0 then begin
|
|
result := false;
|
|
exit;
|
|
end;
|
|
|
|
// 2. Ask the device driver to initialize the DEVMODE buffer with
|
|
// the default settings.
|
|
dwRet := DocumentPropertiesW(0, FPrinterHandle, PWideChar(UTF8Decode(Pdev.Name)),
|
|
PDev.DevModeW, nil, DM_OUT_BUFFER);
|
|
result := (dwRet=IDOK);
|
|
if not result then begin
|
|
ReallocMem(PDev.DevmodeW, 0);
|
|
exit;
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TWinPrinter.DoBeginDoc;
|
|
var
|
|
Inf: TDocInfo;
|
|
Doc1: DOC_INFO_1;
|
|
begin
|
|
inherited DoBeginDoc;
|
|
|
|
if fPrinterHandle = 0 then
|
|
raise EPrinter.Create('Printer handle not defined');
|
|
|
|
if RawMode then
|
|
begin
|
|
Doc1.pDocName := PChar(Title);
|
|
if Filename <> '' then
|
|
Doc1.pOutputFile := PChar(Filename)
|
|
else
|
|
Doc1.pOutputFile := nil;
|
|
Doc1.pDataType := 'RAW';
|
|
|
|
if StartDocPrinter(FPrinterHandle, 1, PByte(@Doc1)) = 0 then
|
|
begin
|
|
ClosePrinter(FPrinterHandle);
|
|
FPrinterHandle := 0;
|
|
end
|
|
else
|
|
if not StartPagePrinter(FPrinterHandle) then
|
|
begin
|
|
EndDocPrinter(FPrinterHandle);
|
|
ClosePrinter(FPrinterHandle);
|
|
FPrinterHandle := 0;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
SetDC;
|
|
Canvas.Handle := fDC;
|
|
Canvas.Refresh;
|
|
|
|
FillChar(Inf, SizeOf(Inf), 0);
|
|
Inf.cbSize := SizeOf(Inf);
|
|
Inf.lpszDocName := PChar(Title);
|
|
if FileName <> '' then
|
|
Inf.lpszOutput := PChar(Filename);
|
|
|
|
StartDoc(fDC,@Inf);
|
|
StartPage(fDC);
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.DoNewPage;
|
|
begin
|
|
inherited DoNewPage;
|
|
|
|
if RawMode then begin
|
|
|
|
EndPagePrinter(FPrinterHandle);
|
|
StartPagePrinter(FPrinterHandle);
|
|
|
|
end else begin
|
|
EndPage(fDC);
|
|
StartPage(fDC);
|
|
Canvas.Refresh;
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.DoEndDoc(aAborded: Boolean);
|
|
begin
|
|
inherited DoEndDoc(aAborded);
|
|
|
|
if RawMode then begin
|
|
|
|
EndPagePrinter(FPrinterHandle);
|
|
EndDocPrinter(FPrinterHandle);
|
|
{
|
|
ClosePrinter(FPrinterHandle);
|
|
FPrinterHandle:=0;
|
|
}
|
|
end else begin
|
|
EndPage(fDC);
|
|
if not aAborded then
|
|
WinUtilPrn.EndDoc(fDC);
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.DoAbort;
|
|
begin
|
|
inherited DoAbort;
|
|
if RawMode then
|
|
AbortPrinter(FPrinterHandle)
|
|
else
|
|
AbortDoc(fDC);
|
|
end;
|
|
|
|
function TWinPrinter.GetDefaultPrinter: string;
|
|
|
|
const
|
|
MAXBUFSIZE = 512;
|
|
|
|
var
|
|
PrtCount: DWORD;
|
|
IntRes: Integer;
|
|
GetDefPrnFunc: function(buffer: LPTSTR; var bufSize: DWORD): BOOL; stdcall;
|
|
SpoolerHandle: HINST;
|
|
AName: widestring;
|
|
begin
|
|
// retrieve default printer using ms blessed method, see
|
|
// see: http://support.microsoft.com/default.aspx?scid=kb;en-us;246772
|
|
Result := '';
|
|
if Win32MajorVersion >=5 then
|
|
begin
|
|
// for Windows 2000 or later, use api GetDefaultPrinter
|
|
SpoolerHandle := LoadLibrary(LibWinSpool);
|
|
if SpoolerHandle = 0 then
|
|
Exit;
|
|
Pointer(GetDefPrnFunc) := GetProcAddress(SpoolerHandle, 'GetDefaultPrinterW');
|
|
if GetDefPrnFunc = nil then
|
|
begin
|
|
FreeLibrary(SpoolerHandle);
|
|
Exit;
|
|
end;
|
|
GetDefPrnFunc(nil, PrtCount);
|
|
result := '';
|
|
if (prtcount>0) then begin
|
|
SetLength(AName, PrtCount-1); // this includes the #0 terminator
|
|
GetDefPrnFunc(@AName[1], prtCount);
|
|
result := UTF8Encode(AName);
|
|
end;
|
|
FreeLibrary(SpoolerHandle);
|
|
end else
|
|
begin
|
|
// for NT, use GetProfileString
|
|
SetLength(result, MAXBUFSIZE);
|
|
IntRes := GetProfileString('windows', 'device', ',,,', PChar(result),
|
|
MAXBUFSIZE);
|
|
if (IntRes>0) and (pos(',',Result)<>0) then
|
|
Result := AnsiToUTF8(copy(Result, 1, pos(',', Result)-1))
|
|
else
|
|
Result := ''
|
|
end;
|
|
end;
|
|
|
|
|
|
//Enum all defined printers. First printer it's default
|
|
procedure TWinPrinter.DoEnumPrinters(Lst: TStrings);
|
|
var
|
|
Flags : DWORD;
|
|
Level : DWORD;
|
|
PrtCount : DWORD;
|
|
Needed : DWORD;
|
|
Buffer : PByte;
|
|
InfoPrt : PByte;
|
|
i : Integer;
|
|
DefaultPrinter : string;
|
|
PDev : TPrinterDevice;
|
|
TmpDevModeW : PDeviceModeW;
|
|
PrtStr : string;
|
|
BoolRes: LCLType.BOOL;
|
|
B: Boolean;
|
|
begin
|
|
{$IFDEF NOPRINTERS}
|
|
Lst.Clear;
|
|
exit;
|
|
{$ENDIF}
|
|
DefaultPrinter := GetDefaultPrinter;
|
|
|
|
Flags := PRINTER_ENUM_CONNECTIONS or PRINTER_ENUM_LOCAL;
|
|
Level := 2;
|
|
|
|
//Evaluate buffer size
|
|
Needed := 0;
|
|
EnumPrintersW(Flags, nil, Level, nil, 0, @Needed, @PrtCount);
|
|
if Needed <> 0 then
|
|
begin
|
|
GetMem(Buffer, Needed);
|
|
Fillchar(Buffer^, Needed, 0);
|
|
try
|
|
//Enumerate Printers
|
|
BoolRes := EnumPrintersW(Flags, nil, Level, Buffer, Needed, @Needed, @PrtCount);
|
|
if BoolRes then
|
|
begin
|
|
InfoPrt := Buffer;
|
|
for i := 0 to PrtCount - 1 do
|
|
begin
|
|
if Level = 2 then
|
|
begin
|
|
PDev := TPrinterDevice.Create;
|
|
PDev.Name := UTF8Encode(widestring(PPRINTER_INFO_2W(InfoPrt)^.pPrinterName));
|
|
PDev.Driver := UTF8Encode(widestring(PPRINTER_INFO_2W(InfoPrt)^.pDriverName));
|
|
PDev.Port := UTF8Encode(widestring(PPRINTER_INFO_2W(InfoPrt)^.pPortName));
|
|
TmpDevModeW := PPRINTER_INFO_2W(InfoPrt)^.pDevMode;
|
|
|
|
|
|
if (TmpDevModeW <> nil) then
|
|
begin
|
|
// the devmode structure obtained this way have two problems
|
|
// 1. It's not the full devmode, because it doesn't have
|
|
// the private info
|
|
// 2. It's not initialized with the current settings and
|
|
// have not extra settings at all.
|
|
//
|
|
// PDev.DevMode:=PPRINTER_INFO_2(InfoPrt)^.PDevMode^;
|
|
PDev.Device := UTF8Encode(widestring(TmpDevModeW^.dmDeviceName));
|
|
PDev.DefaultPaperName := UTF8Encode(widestring(TmpDevModeW^.dmFormName));
|
|
PDev.DefaultPaper := TmpDevModeW^.dmPaperSize;
|
|
PDev.DefaultBin := TmpDevModeW^.dmDefaultSource;
|
|
|
|
end
|
|
else begin
|
|
PDev.Device:='';
|
|
PDev.DefaultPaper:=0;
|
|
PDev.DefaultBin := 0
|
|
end;
|
|
PrtStr := PDev.Name;
|
|
B := CompareText(PrtStr, DefaultPrinter)<>0;
|
|
|
|
if B then
|
|
Lst.AddObject(PrtStr,PDev)
|
|
else
|
|
begin
|
|
Lst.Insert(0,PrtStr);
|
|
Lst.Objects[0]:=PDev;
|
|
end;
|
|
Inc(InfoPrt,SizeOf(_PRINTER_INFO_2W));
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
FreeMem(Buffer);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.DoResetPrintersList;
|
|
var i : Integer;
|
|
Obj : TObject;
|
|
begin
|
|
for i:=0 to Printers.Count-1 do
|
|
begin
|
|
Obj:=Printers.Objects[i];
|
|
Printers.Objects[i]:=nil;
|
|
Obj.Free;
|
|
end;
|
|
inherited DoResetPrintersList;
|
|
end;
|
|
|
|
procedure TWinPrinter.DoEnumPapers(Lst: TStrings);
|
|
var
|
|
BufferW : PWideChar;
|
|
PaperN : String;
|
|
PaperC,i : Integer;
|
|
Count : Integer;
|
|
PDev : TPrinterDevice;
|
|
ArPapers : Array[0..255] of Word;
|
|
begin
|
|
inherited DoEnumPapers(Lst);
|
|
|
|
if (Printers.Count>0) then
|
|
begin
|
|
PDev:=TPrinterDevice(Printers.Objects[PrinterIndex]);
|
|
|
|
if fPrinterHandle=0 then
|
|
SetPrinter(Printers.Strings[PrinterIndex]);
|
|
|
|
if fPrinterHandle=0 then
|
|
raise EPrinter.Create('Printer handle not defined');
|
|
|
|
//Retreive the supported papers
|
|
PaperC:=0;
|
|
Count := DeviceCapabilitiesW(
|
|
PWidechar(UTF8Decode(Pdev.Name)),
|
|
PWidechar(UTF8Decode(PDev.Port)), DC_PAPERNAMES, nil, nil);
|
|
if Count<=0 then
|
|
raise EPrinter.CreateFmt('DoEnumPapers<DC_PAPERNAMES> error : %d, (%s)',
|
|
[GetLastError,SysErrorMessage(GetLastError)]);
|
|
try
|
|
GetMem(BufferW,64*SizeOf(Widechar)*Count);
|
|
PaperC := DeviceCapabilitiesW(
|
|
PWidechar(UTF8Decode(Pdev.Name)),
|
|
PWidechar(UTF8Decode(PDev.Port)),
|
|
DC_PAPERNAMES,
|
|
BufferW,
|
|
nil);
|
|
for i:=0 to PaperC-1 do
|
|
begin
|
|
PaperN:=UTF8Encode(Widestring(BufferW+i*64));
|
|
Lst.Add(PaperN);
|
|
end;
|
|
finally
|
|
FreeMem(BufferW);
|
|
end;
|
|
|
|
//Retreive the code of papers
|
|
FillChar(ArPapers,SizeOf(ArPapers),0);
|
|
PaperC:=DeviceCapabilitiesW(
|
|
PWidechar(UTF8Decode(Pdev.Name)),
|
|
PWidechar(UTF8Decode(PDev.Port)),
|
|
DC_PAPERS,
|
|
PWidechar(@ArPapers[0]),
|
|
nil);
|
|
if PaperC<=0 then
|
|
raise EPrinter.CreateFmt('DoEnumPapers<DC_PAPERS> error : %d, (%s)',
|
|
[GetLastError,SysErrorMessage(GetLastError)])
|
|
else if PaperC>Lst.Count then
|
|
PaperC := Lst.Count;
|
|
|
|
for i:=0 to PaperC-1 do
|
|
Lst.Objects[i]:=TObject(ptrint(ArPapers[i]));
|
|
end;
|
|
end;
|
|
|
|
function TWinPrinter.DoGetPaperName: string;
|
|
var
|
|
i : Integer;
|
|
dmW : PDeviceModeW;
|
|
Paper: PtrInt;
|
|
Lst : TStrings;
|
|
begin
|
|
Paper :=-1;
|
|
Result:=inherited DoGetPaperName;
|
|
Lst := PaperSize.SupportedPapers;
|
|
|
|
if GetCurrentDevModeW(dmW) then
|
|
Paper := dmW^.dmPaperSize;
|
|
|
|
if Paper<>-1 then
|
|
begin
|
|
i := Lst.IndexOfObject(TObject(Paper));
|
|
if i>=0 then
|
|
result := lst[i]
|
|
else
|
|
begin
|
|
// Weird, selected paper code (size) do not agree with previously
|
|
// retrieved paper sizes.
|
|
//
|
|
// NOTE.
|
|
// This problem was observed while trying to print on a just installed CutePDF
|
|
// printer in Win 7. Once Printer properties dialog were 'navigated' (no
|
|
// changes were needed) in ctrl panel/devices and printers/CutePDF printer
|
|
// it started to work normally.
|
|
result := UTF8Encode(Widestring(dmW^.dmFormName));
|
|
i := Lst.IndexOf(result);
|
|
if i<0 then
|
|
result := lst[0];
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TWinPrinter.DoGetDefaultPaperName: string;
|
|
var i : Integer;
|
|
PDev : TPrinterDevice;
|
|
begin
|
|
Result:=inherited DoGetDefaultPaperName;
|
|
|
|
if (Printers.Count>0) then
|
|
begin
|
|
PDev:=TPrinterDevice(Printers.Objects[PrinterIndex]);
|
|
with PaperSize.SupportedPapers do begin
|
|
i:=IndexOfObject(TObject(ptrint(PDev.DefaultPaper)));
|
|
if i<>-1 then
|
|
Result:= Strings[i]
|
|
else
|
|
begin
|
|
// See note on doGetPaperName
|
|
i := IndexOf(PDev.DefaultPaperName);
|
|
if i<0 then
|
|
Result := Strings[0];
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.DoSetPaperName(aName: string);
|
|
var i : Integer;
|
|
dmW : PDeviceModeW;
|
|
begin
|
|
inherited DoSetPaperName(aName);
|
|
if GetCurrentDevModeW(dmW) then begin
|
|
i:=PaperSize.SupportedPapers.IndexOf(aName);
|
|
if i<>-1 then begin
|
|
ClearDC;
|
|
dmW^.dmPaperSize := SHORT(ptrint(PaperSize.SupportedPapers.Objects[i]));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TWinPrinter.DoGetPaperRect(aName: string;
|
|
var aPaperRc: TPaperRect): Integer;
|
|
var NSize, i : Integer;
|
|
PDev : TPrinterDevice;
|
|
ArSizes : Array[0..255] of TPoint;
|
|
begin
|
|
Result:=Inherited DoGetPaperRect(aName,aPaperRc);
|
|
|
|
if (Printers.Count>0) and not RawMode then
|
|
begin
|
|
// Information for physical sizes can be obtained for any paper supported
|
|
// by the printer, the same is not true for printable paper size, this can
|
|
// be obtained only(?) for currently selected paper.
|
|
//
|
|
if DoGetPaperName=AName then begin
|
|
SetDC;
|
|
with aPaperRC.PhysicalRect do begin
|
|
Left :=0;
|
|
Top :=0;
|
|
Right :=Windows.GetDeviceCaps(fDC, PHYSICALWIDTH);
|
|
Bottom:=Windows.GetDeviceCaps(fDC, PHYSICALHEIGHT);
|
|
end;
|
|
with aPaperRC.WorkRect do begin
|
|
Left :=Windows.GetDeviceCaps(fDC, PHYSICALOFFSETX);
|
|
Top :=Windows.GetDeviceCaps(fDC, PHYSICALOFFSETY);
|
|
Right :=Left + Windows.GetDeviceCaps(fDC, HORZRES);
|
|
Bottom:=Top + Windows.GetDeviceCaps(fDC, VERTRES);
|
|
end;
|
|
end else begin
|
|
// for other papers return at least the physical size
|
|
// note: old implementation was using DeviceCapabilities function with
|
|
// index DC_PAPERSIZE, unfortunately this returns dimensions in
|
|
// tenths of millimeter which is wrong, we need points (not font
|
|
// points, but printer "pixels" at current resolution).
|
|
//
|
|
PDev:=TPrinterDevice(Printers.Objects[PrinterIndex]);
|
|
|
|
//Retreive the Width and Height of aName paper
|
|
FillChar(ArSizes,SizeOf(ArSizes),0);
|
|
|
|
//ToDo: use DeviceCapabilitiesW with appropriate W-variant datastructures
|
|
// In particular, I don't know if using PWideChar(@ArSizes[0]) is correct in that variant,
|
|
// so for now leave it as is
|
|
NSize:=DeviceCapabilities(PChar(Pdev.Name),PChar(PDev.Port),
|
|
DC_PAPERSIZE,PChar(@ArSizes[0]),nil);
|
|
i:=PaperSize.SupportedPapers.IndexOf(aName);
|
|
if (i>=0) and (i<NSize) and (NSize<>0) then
|
|
begin
|
|
aPaperRc.PhysicalRect:=Classes.Rect(0,0,ArSizes[i].X,ArSizes[i].Y);
|
|
with aPaperRC.PhysicalRect do begin
|
|
// convert from tenths of millimeter to points
|
|
Right := round(Right * XDPI / 254);
|
|
Bottom := round(Bottom* YDPI / 254);
|
|
end;
|
|
aPaperRc.WorkRect := aPaperRC.PhysicalRect;
|
|
end;
|
|
end;
|
|
Result:=1;
|
|
end;
|
|
end;
|
|
|
|
function TWinPrinter.DoSetPrinter(aName: string): Integer;
|
|
var
|
|
i: Integer;
|
|
PDev: TPrinterDevice;
|
|
BoolRes: LCLType.BOOL;
|
|
begin
|
|
Result := inherited DoSetPrinter(aName);
|
|
|
|
i := Printers.IndexOf(aName);
|
|
if i <> -1 then
|
|
begin
|
|
ClearDC;
|
|
|
|
if FPrinterHandle <> 0 then
|
|
ClosePrinter(FPrinterHandle);
|
|
|
|
if pfDestroying in PrinterFlags then
|
|
result := i
|
|
else begin
|
|
PDev := TPrinterDevice(Printers.Objects[i]);
|
|
BoolRes := OpenPrinterW(PWideChar(UTF8Decode(PDev.Name)), @fPrinterHandle, nil);
|
|
if not BoolRes then
|
|
begin
|
|
FprinterHandle := 0;
|
|
raise EPrinter.CreateFmt('OpenPrinter exception : %s',
|
|
[SysErrorMessage(GetlastError)]);
|
|
end;
|
|
|
|
if UpdateDevMode(i) then
|
|
Result := i
|
|
else
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TWinPrinter.DoGetCopies: Integer;
|
|
var
|
|
dmW: PDeviceModeW;
|
|
Boolres: Boolean;
|
|
begin
|
|
Boolres := GetCurrentDevModeW(dmW);
|
|
if BoolRes then begin
|
|
if dmW^.dmCopies<>0 then
|
|
result := dmW^.dmCopies;
|
|
end;
|
|
if Not BoolRes then
|
|
Result:=inherited DoGetCopies;
|
|
end;
|
|
|
|
procedure TWinPrinter.DoSetCopies(aValue: Integer);
|
|
var
|
|
dmW: PDeviceModeW;
|
|
begin
|
|
inherited DoSetCopies(aValue);
|
|
if (AValue>0) and GetCurrentDevModeW(dmW) then begin
|
|
ClearDC;
|
|
dmW^.dmCopies := SHORT(aValue)
|
|
end;
|
|
end;
|
|
|
|
function TWinPrinter.DoGetOrientation: TPrinterOrientation;
|
|
var
|
|
dmW: PDeviceModeW;
|
|
begin
|
|
Result:=inherited DoGetOrientation;
|
|
if GetCurrentDevModeW(dmW) then begin
|
|
case dmW^.dmOrientation of
|
|
DMORIENT_PORTRAIT : result:=poPortrait;
|
|
DMORIENT_LANDSCAPE: result:=poLandscape;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.DoSetOrientation(aValue: TPrinterOrientation);
|
|
var
|
|
dmW: PDeviceModeW;
|
|
begin
|
|
inherited DoSetOrientation(aValue);
|
|
if GetCurrentDevModeW(dmW) then begin
|
|
ClearDC;
|
|
dmW^.dmOrientation := Win32Orientations[aValue];
|
|
end;
|
|
end;
|
|
|
|
function TWinPrinter.GetPrinterType: TPrinterType;
|
|
var
|
|
Size: Dword;
|
|
InfoPrt: Pointer;
|
|
begin
|
|
Result := ptLocal;
|
|
GetPrinter(fPrinterHandle, 4, nil, 0, @Size);
|
|
GetMem(InfoPrt, Size);
|
|
try
|
|
if not GetPrinter(fPRinterHandle, 4, InfoPrt, Size, @Size)
|
|
then
|
|
raise EPrinter.CreateFmt('GetPrinterType failed : %s',
|
|
[SysErrorMessage(GetLastError)]);
|
|
if (PPRINTER_INFO_4(InfoPrt)^.Attributes and PRINTER_ATTRIBUTE_NETWORK)<>0 then
|
|
Result := ptNetwork;
|
|
finally
|
|
FreeMem(InfoPrt);
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
function TWinPrinter.DoGetPrinterState: TPrinterState;
|
|
var
|
|
Size, Status, Jobs : DWord;
|
|
InfoPrt: Pointer;
|
|
begin
|
|
Result := psNoDefine;
|
|
GetPrinter(fPrinterHandle, 2, nil, 0, @Size);
|
|
GetMem(InfoPrt,Size);
|
|
try
|
|
//ToDo: use the Wide variant of GetPrinter and with the appropraite W-variant datastructures
|
|
|
|
if not GetPrinter(fPrinterHandle, 2, InfoPrt, Size, @Size)
|
|
then
|
|
raise EPrinter.CreateFmt('GetPrinterState failed : %s',
|
|
[SysErrorMessage(GetLastError)]);
|
|
|
|
Jobs := PPRINTER_INFO_2A(InfoPrt)^.cJobs;
|
|
Status := PPRINTER_INFO_2A(InfoPrt)^.Status;
|
|
case Status of
|
|
0: Result := psReady;
|
|
PRINTER_STATUS_PRINTING,
|
|
PRINTER_STATUS_PROCESSING,
|
|
PRINTER_STATUS_WARMING_UP,
|
|
PRINTER_STATUS_WAITING,
|
|
PRINTER_STATUS_IO_ACTIVE,
|
|
PRINTER_STATUS_PENDING_DELETION,
|
|
PRINTER_STATUS_INITIALIZING: Result := psPrinting;
|
|
PRINTER_STATUS_PAPER_JAM,
|
|
PRINTER_STATUS_PAPER_OUT,
|
|
PRINTER_STATUS_PAPER_PROBLEM,
|
|
PRINTER_STATUS_USER_INTERVENTION,
|
|
PRINTER_STATUS_NO_TONER,
|
|
PRINTER_STATUS_ERROR,
|
|
PRINTER_STATUS_DOOR_OPEN,
|
|
PRINTER_STATUS_PAGE_PUNT,
|
|
PRINTER_STATUS_OUT_OF_MEMORY,
|
|
PRINTER_STATUS_PAUSED: Result := psStopped;
|
|
end;
|
|
|
|
if (Result = psReady) and (Jobs > 0) then
|
|
Result := psPrinting;
|
|
finally
|
|
FreeMem(InfoPrt);
|
|
end;
|
|
end;
|
|
|
|
function TWinPrinter.GetCanPrint: Boolean;
|
|
begin
|
|
Result := (DoGetPrinterState <> psStopped);
|
|
end;
|
|
|
|
function TWinPrinter.GetCanRenderCopies: Boolean;
|
|
var
|
|
pDev : TPrinterDevice;
|
|
Count : Integer;
|
|
begin
|
|
if (Printers.Count>0) then
|
|
begin
|
|
PDev:=TPrinterDevice(Printers.Objects[PrinterIndex]);
|
|
Count := DeviceCapabilitiesW(
|
|
PWidechar(UTF8Decode(Pdev.Name)),
|
|
PWidechar(UTF8Decode(PDev.Port)),
|
|
DC_COPIES,
|
|
nil,PDev.DevModeW);
|
|
Result := (Count>1);
|
|
end
|
|
else
|
|
Result := inherited GetCanRenderCopies;
|
|
end;
|
|
|
|
procedure TWinPrinter.AdvancedProperties;
|
|
var
|
|
PDev: TPrinterDevice;
|
|
begin
|
|
if Printers.Count>0 then
|
|
begin
|
|
PDev:=TPrinterDevice(Printers.Objects[PrinterIndex]);
|
|
DocumentPropertiesW(
|
|
Widgetset.AppHandle,
|
|
FPrinterHandle,
|
|
PWidechar(UTF8Decode(PDev.Name)),
|
|
Pdev.DevModeW, Pdev.DevModeW,
|
|
DM_OUT_BUFFER or DM_IN_BUFFER or DM_IN_PROMPT);
|
|
//PrinterProperties(Widgetset.AppHandle,fPrinterHandle)
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.DoEnumBins(Lst : TStrings);
|
|
var
|
|
BufferW: PWideChar;
|
|
BinN : String;
|
|
BinC,i : Integer;
|
|
Count : Integer;
|
|
PDev : TPrinterDevice;
|
|
arBins : Array[0..255] of Word;
|
|
begin
|
|
|
|
if Lst=nil then
|
|
exit;
|
|
|
|
Lst.Clear;
|
|
|
|
if (Printers.Count>0) then
|
|
begin
|
|
PDev:=TPrinterDevice(Printers.Objects[PrinterIndex]);
|
|
|
|
if fPrinterHandle=0 then
|
|
SetPrinter(Printers.Strings[PrinterIndex]);
|
|
|
|
if fPrinterHandle=0 then
|
|
raise EPrinter.Create('Printer handle not defined');
|
|
|
|
//Retreive the supported bins
|
|
BinC:=0;
|
|
Count := DeviceCapabilitiesW(
|
|
PWidechar(UTF8Decode(Pdev.Name)),
|
|
PWidechar(UTF8Decode(PDev.Port)), DC_BINNAMES, nil, nil);
|
|
if Count<=0 then
|
|
raise EPrinter.CreateFmt('DoEnumBins<DC_BINNAMES> error : %d, (%s)',
|
|
[GetLastError,SysErrorMessage(GetLastError)]);
|
|
|
|
try
|
|
GetMem(BufferW,24*SizeOf(Widechar)*Count);
|
|
BinC := DeviceCapabilitiesW(
|
|
PWidechar(UTF8Decode(Pdev.Name)),
|
|
PWidechar(UTF8Decode(PDev.Port)),
|
|
DC_BINNAMES,
|
|
BufferW,
|
|
nil);
|
|
for i:=0 to BinC-1 do
|
|
begin
|
|
BinN:=UTF8Encode(Widestring(BufferW+i*24));
|
|
Lst.Add(BinN);
|
|
end;
|
|
finally
|
|
Freemem(BufferW);
|
|
end;
|
|
|
|
//Retreive the code of bins
|
|
FillChar(arBins,SizeOf(arBins),0);
|
|
BinC:=DeviceCapabilitiesW(
|
|
PWidechar(UTF8Decode(Pdev.Name)),
|
|
PWidechar(UTF8Decode(PDev.Port)),
|
|
DC_BINS,
|
|
PWidechar(@ArBins[0]),
|
|
nil);
|
|
if BinC<=0 then
|
|
raise EPrinter.CreateFmt('DoEnumBinss<DC_BINS> error : %d, (%s)',
|
|
[GetLastError,SysErrorMessage(GetLastError)])
|
|
else if BinC>Lst.Count then
|
|
BinC := Lst.Count;
|
|
|
|
for i:=0 to BinC-1 do
|
|
Lst.Objects[i]:=TObject(ptrint(arBins[i]));
|
|
end;
|
|
end;
|
|
|
|
function TWinPrinter.DoGetDefaultBinName: string;
|
|
var i : Integer;
|
|
PDev : TPrinterDevice;
|
|
begin
|
|
Result:=inherited DoGetDefaultBinName;
|
|
|
|
with SupportedBins do
|
|
if (Printers.Count>0) then
|
|
begin
|
|
PDev:=TPrinterDevice(Printers.Objects[PrinterIndex]);
|
|
i:=IndexOfObject(TObject(ptrint(PDev.DefaultBin)));
|
|
if i<>-1 then
|
|
Result:= Strings[i];
|
|
end;
|
|
end;
|
|
|
|
function TWinPrinter.DoGetBinName: string;
|
|
var
|
|
i : Integer;
|
|
dmW: PDeviceModeW;
|
|
begin
|
|
Result:=inherited DoGetBinName;
|
|
if GetCurrentDevModeW(dmW) then
|
|
with SupportedBins do begin
|
|
i := IndexOfObject(TObject(ptrInt(dmW^.dmDefaultSource)));
|
|
if i>=0 then
|
|
result := Strings[i];
|
|
end;
|
|
end;
|
|
|
|
procedure TWinPrinter.DoSetBinName(aName: string);
|
|
var
|
|
i : Integer;
|
|
dmW: PDeviceModeW;
|
|
begin
|
|
with SupportedBins do begin
|
|
|
|
if not GetCurrentDevModeW(dmW) then
|
|
raise EPrinter.Create('DoSetBinName error : unable to get current DevMode');
|
|
|
|
i := IndexOf(aName);
|
|
if (i>=0) then begin
|
|
ClearDC;
|
|
dmW^.dmDefaultSource := SHORT(ptrint(Objects[i]));
|
|
end else
|
|
inherited DoSetBinName(aName); // handle uknown bin name
|
|
|
|
end;
|
|
end;
|
|
|
|
function PrinterEnumFontsProc(
|
|
var ELogFont: LCLType.TEnumLogFontEx;
|
|
var {%H-}Metric: LCLType.TNewTextMetricEx;
|
|
FontType: Longint;
|
|
Data:LParam):Longint; stdcall;
|
|
var
|
|
S: string;
|
|
Lst: TStrings;
|
|
begin
|
|
s := StrPas(ELogFont.elfLogFont.lfFaceName);
|
|
Lst := TStrings(PtrInt(Data));
|
|
if Lst.IndexOf(S)<0 then
|
|
Lst.AddObject(S, TObject(PtrInt(FontType)));
|
|
result := 1;
|
|
end;
|
|
|
|
procedure TWinPrinter.DoEnumFonts(Lst: TStrings);
|
|
var
|
|
Lf: TLogFont;
|
|
begin
|
|
if (Lst=nil) then
|
|
exit;
|
|
Lst.Clear;
|
|
if Printers.Count>0 then begin
|
|
Lf.lfFaceName := '';
|
|
Lf.lfCharSet := DEFAULT_CHARSET;
|
|
Lf.lfPitchAndFamily := 0;
|
|
LCLIntf.EnumFontFamiliesEx(Canvas.Handle, @Lf, @PrinterEnumFontsProc, PtrInt(Lst), 0);
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
Printer:=TWinPrinter.Create;
|
|
|
|
|
|
{end.}
|