lazarus/components/printers/win32/winprinters.inc

1330 lines
35 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;
function GetCurrentDevModeA(out DM:PDeviceModeA): Boolean;
var
PDev: TPrinterDevice;
begin
Result := false;
if (Printer.Printers.Count > 0) then
begin
PDev := TPrinterDevice(Printer.Printers.Objects[Printer.PrinterIndex]);
DM := PDev.DevModeA;
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;
var 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]);
if UseUnicode then
fDC:=CreateICW(
PWidechar(UTF8Decode(PDev.Driver)),
PWidechar(UTF8Decode(PDev.Device)),
PWidechar(UTF8Decode(PDev.Port)),
PDev.DevModeW)
else
fDC:=CreateIC(PChar(PDev.Driver),PChar(PDev.Device),
PChar(PDev.Port),PDev.DevModeA);
if fDC=0 then
begin
if UseUnicode then
fDC:=CreateICW(
PWidechar('WINSPOOL'),
PWidechar(UTF8Decode(PDev.Device)),
PWidechar(UTF8Decode(PDev.Port)),
PDev.DevModeW)
else
fDC:=CreateIC(PChar('WINSPOOL'),PChar(PDev.Device),
PChar(PDev.Port),PDev.DevModeA);
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
if UseUnicode then
fDC := CreateDCW(nil, PWidechar(UTF8Decode(PDev.Name)), nil, PDev.DevModeW)
else
fDC := CreateDC(nil, PChar(PDev.Name), nil, PDev.DevModeA);
if fDC=0 then
begin
if UseUnicode then
fDC := CreateDCW(PWidechar('WINSPOOL'),PWidechar(UTF8Decode(PDev.Name)), nil, PDev.DevModeW)
else
fDC := CreateDC(PChar('WINSPOOL'),PChar(PDev.Name), nil, PDev.DevModeA);
end;
{Workaround (hack) for Lexmark 1020 JetPrinter (Mono)}
if fDC=0 then
begin
if UseUnicode then
fDC:=CreateDCW(nil,PWidechar(UTF8Decode(PDev.Driver)),nil, PDev.DevModeW)
else
fDC:=CreateDC(nil,PChar(PDev.Driver),nil, PDev.DevModeA);
end;
if fDC=0 then
begin
if UseUnicode then
fDC:=CreateDCW(PWideChar('WINSPOOL'),PWideChar(UTF8Decode(PDev.Driver)),nil,PDev.DevModeW)
else
fDC:=CreateDC(pChar('WINSPOOL'),PChar(PDev.Driver),nil,PDev.DevModeA);
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.
if UseUnicode then
begin
PDev.DevModeSize := DocumentPropertiesW(0, FPrinterHandle, Pwidechar(UTF8Decode(PDev.Name)),
nil, nil, 0);
ReallocMem(Pdev.DevModeW, PDev.DevModeSize);
end
else
begin
PDev.DevModeSize := DocumentProperties(0, FPrinterHandle, pchar(PDev.Name),
nil, nil, 0);
ReallocMem(Pdev.DevModeA, PDev.DevModeSize);
end;
if PDev.DevModeSize=0 then begin
result := false;
exit;
end;
// 2. Ask the device driver to initialize the DEVMODE buffer with
// the default settings.
if UseUnicode then
dwRet := DocumentPropertiesW(0, FPrinterHandle, PWideChar(UTF8Decode(Pdev.Name)),
PDev.DevModeW, nil, DM_OUT_BUFFER)
else
dwRet := DocumentProperties(0, FPrinterHandle, pchar(Pdev.Name),
PDev.DevModeA, nil, DM_OUT_BUFFER);
result := (dwRet=IDOK);
if not result then begin
if UseUnicode then
ReallocMem(PDev.DevmodeW, 0)
else
ReallocMem(PDev.DevmodeA, 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
Needed, PrtCount: DWORD;
BoolRes: BOOL;
IntRes: Integer;
PrintInfo2Buf: PByte;
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 Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
begin //No unicode printer function on Win9x platform
// Get PRINT_INFO_2 record size
SetLastError(0);
//if UseUnicode then
// BoolRes := EnumPrintersW(PRINTER_ENUM_DEFAULT, nil, 2, nil, 0, @Needed, @PrtCount);
//else
BoolRes := EnumPrinters(PRINTER_ENUM_DEFAULT, nil, 2, nil, 0, @Needed, @PrtCount);
if not BoolRes and
((GetLastError <> ERROR_INSUFFICIENT_BUFFER) or (Needed = 0)) then
Exit;
// Get PRINT_INFO_2 record
GetMem(PrintInfo2Buf, Needed);
//if UseUnicode then
// BoolRes := EnumPrintersW(PRINTER_ENUM_DEFAULT, nil, 2, PrintInfo2Buf,
// Needed, @Needed, @PrtCount);
//else
BoolRes := EnumPrinters(PRINTER_ENUM_DEFAULT, nil, 2, PrintInfo2Buf,
Needed, @Needed, @PrtCount);
if not BoolRes then
begin
FreeMem(PrintInfo2Buf);
Exit;
end;
//if UseUnicode then
// Result := UTF8Encode(widestring(PPRINTER_INFO_2W(PrintInfo2Buf)^.pPrinterName));
//else
begin
Result := PPRINTER_INFO_2A(PrintInfo2Buf)^.pPrinterName;
Result := AnsiToUTF8(Result);
end;
FreeMem(PrintInfo2Buf);
end
else
if Win32Platform=VER_PLATFORM_WIN32_NT then
begin
if Win32MajorVersion >=5 then
begin
// for Windows 2000 or later, use api GetDefaultPrinter
// TODO: needs to check WindowsUnicodeSupport
SpoolerHandle := LoadLibrary(LibWinSpool);
if SpoolerHandle = 0 then
Exit;
if UseUnicode then
Pointer(GetDefPrnFunc) := GetProcAddress(SpoolerHandle, 'GetDefaultPrinterW')
else
Pointer(GetDefPrnFunc) := GetProcAddress(SpoolerHandle, 'GetDefaultPrinterA');
if GetDefPrnFunc = nil then
begin
FreeLibrary(SpoolerHandle);
Exit;
end;
Boolres := GetDefPrnFunc(nil, PrtCount);
result := '';
if (prtcount>0) then begin
if UseUnicode then
begin
SetLength(AName, PrtCount-1); // this includes the #0 terminator
BoolRes := GetDefPrnFunc(@AName[1], prtCount);
result := UTF8Encode(AName);
end
else
begin
SetLength(Result, PrtCount); // make room for printer name
BoolRes := GetDefPrnFunc(pchar(Result), prtCount);
Result := AnsiToUTF8(Result);
end;
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;
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;
TmpDevModeA : PDeviceMode;
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;
if UseUnicode then
EnumPrintersW(Flags, nil, Level, nil, 0, @Needed, @PrtCount)
else
EnumPrinters(Flags, nil, Level, nil, 0, @Needed, @PrtCount);
if Needed <> 0 then
begin
GetMem(Buffer, Needed);
Fillchar(Buffer^, Needed, 0);
try
//Enumerate Printers
if UseUnicode then
BoolRes := EnumPrintersW(Flags, nil, Level, Buffer, Needed, @Needed, @PrtCount)
else
BoolRes := EnumPrinters(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;
if UseUnicode then
begin
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;
end
else
begin
PDev.Name := PPRINTER_INFO_2A(InfoPrt)^.pPrinterName;
PDev.Driver := PPRINTER_INFO_2A(InfoPrt)^.pDriverName;
PDev.Port := PPRINTER_INFO_2A(InfoPrt)^.pPortName;
TmpDevModeA := PPRINTER_INFO_2(InfoPrt)^.pDevMode;
end;
if (UseUnicode and (TmpDevModeW <> nil)) or (not UseUnicode and (TmpDevModeA <> 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^;
if UseUnicode then
begin
PDev.Device := UTF8Encode(widestring(TmpDevModeW^.dmDeviceName));
PDev.DefaultPaperName := UTF8Encode(widestring(TmpDevModeW^.dmFormName));
PDev.DefaultPaper := TmpDevModeW^.dmPaperSize;
PDev.DefaultBin := TmpDevModeW^.dmDefaultSource;
end
else
begin
{$IF FPC_FULLVERSION>20602}
PDev.Device := PChar(TmpDevModeA^.dmDeviceName);
{$ELSE}
PDev.Device := PChar(PByte(TmpDevModeA^.dmDeviceName));
{$ENDIF}
PDev.DefaultPaperName := StrPas(TmpDevModeA^.dmFormName);
PDev.DefaultPaper := TmpDevModeA^.dmPaperSize;
PDev.DefaultBin := TmpDevModeA^.dmDefaultSource;
end;
end
else begin
PDev.Device:='';
PDev.DefaultPaper:=0;
PDev.DefaultBin := 0
end;
if UseUnicode then
begin
PrtStr := PDev.Name;
B := CompareText(PrtStr, DefaultPrinter)<>0
end
else
begin
PrtStr := AnsiToUTF8(PDev.Name);
B := AnsiCompareText(PrtStr,DefaultPrinter)<>0
end;
if B then
Lst.AddObject(PrtStr,PDev)
else
begin
Lst.Insert(0,PrtStr);
Lst.Objects[0]:=PDev;
end;
if UseUnicode then
Inc(InfoPrt,SizeOf(_PRINTER_INFO_2W))
else
Inc(InfoPrt,SizeOf(_PRINTER_INFO_2A));
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;
BufferA : PChar;
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;
if UseUnicode then
Count := DeviceCapabilitiesW(
PWidechar(UTF8Decode(Pdev.Name)),
PWidechar(UTF8Decode(PDev.Port)), DC_PAPERNAMES, nil, nil)
else
Count := DeviceCapabilities(PChar(Pdev.Name), PCHar(PDev.Port), DC_PAPERNAMES, nil, nil);
if Count<=0 then
raise EPrinter.CreateFmt('DoEnumPapers<DC_PAPERNAMES> error : %d, (%s)',
[GetLastError,SysErrorMessage(GetLastError)]);
try
if UseUnicode then
begin
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;
end
else
begin
GetMem(BufferA,64*Count);
PaperC:=DeviceCapabilities(PChar(Pdev.Name),PCHar(PDev.Port),
DC_PAPERNAMES,BufferA,nil);
for i:=0 to PaperC-1 do
begin
PaperN:=StrPas(BufferA+i*64);
Lst.Add(PaperN);
end;
end;
finally
if UseUnicode then
FreeMem(BufferW)
else
FreeMem(BufferA);
end;
//Retreive the code of papers
FillChar(ArPapers,SizeOf(ArPapers),0);
if UseUnicode then
PaperC:=DeviceCapabilitiesW(
PWidechar(UTF8Decode(Pdev.Name)),
PWidechar(UTF8Decode(PDev.Port)),
DC_PAPERS,
PWidechar(@ArPapers[0]),
nil)
else
PaperC:=DeviceCapabilities(PChar(Pdev.Name),PChar(PDev.Port),
DC_PAPERS,PChar(@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;
dmA : PDeviceModeA;
Paper: PtrInt;
Lst : TStrings;
begin
Paper :=-1;
Result:=inherited DoGetPaperName;
Lst := PaperSize.SupportedPapers;
if UseUnicode and GetCurrentDevModeW(dmW) then
Paper := dmW^.dmPaperSize
else
if not UseUnicode and GetCurrentDevModeA(dmA) then
Paper := dmA^.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.
if UseUnicode then
result := UTF8Encode(Widestring(dmW^.dmFormName))
else
result := StrPas(dmA^.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;
dmA : PDeviceModeA;
begin
inherited DoSetPaperName(aName);
if UseUnicode then
begin
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
else
begin
if GetCurrentDevModeA(dmA) then begin
i:=PaperSize.SupportedPapers.IndexOf(aName);
if i<>-1 then begin
ClearDC;
dmA^.dmPaperSize := SHORT(ptrint(PaperSize.SupportedPapers.Objects[i]));
end;
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);
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]);
if UseUnicode then
BoolRes := OpenPrinterW(PWideChar(UTF8Decode(PDev.Name)), @fPrinterHandle, nil)
else
BoolRes := OpenPrinter(PChar(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;
dmA: PDeviceMode;
Boolres: Boolean;
begin
if UseUnicode then
begin
Boolres := GetCurrentDevModeW(dmW);
if BoolRes then begin
if dmW^.dmCopies<>0 then
result := dmW^.dmCopies;
end;
end
else
begin
BoolRes := GetCurrentDevModeA(dmA);
if BoolRes then begin
if dmA^.dmCopies<>0 then
result := dmA^.dmCopies;
end;
end;
if Not BoolRes then
Result:=inherited DoGetCopies;
end;
procedure TWinPrinter.DoSetCopies(aValue: Integer);
var
dmW: PDeviceModeW;
dmA: PDeviceModeA;
begin
inherited DoSetCopies(aValue);
if UseUnicode then
begin
if (AValue>0) and GetCurrentDevModeW(dmW) then begin
ClearDC;
dmW^.dmCopies := SHORT(aValue)
end;
end
else
begin
if (AValue>0) and GetCurrentDevModeA(dmA) then begin
ClearDC;
dmA^.dmCopies := SHORT(aValue)
end;
end;
end;
function TWinPrinter.DoGetOrientation: TPrinterOrientation;
var
dmW: PDeviceModeW;
dmA: PDeviceModeA;
begin
Result:=inherited DoGetOrientation;
if UseUnicode then
begin
if GetCurrentDevModeW(dmW) then begin
case dmW^.dmOrientation of
DMORIENT_PORTRAIT : result:=poPortrait;
DMORIENT_LANDSCAPE: result:=poLandscape;
end;
end;
end
else
begin
if GetCurrentDevModeA(dmA) then begin
case dmA^.dmOrientation of
DMORIENT_PORTRAIT : result:=poPortrait;
DMORIENT_LANDSCAPE: result:=poLandscape;
end;
end;
end;
end;
procedure TWinPrinter.DoSetOrientation(aValue: TPrinterOrientation);
var
dmW: PDeviceModeW;
dmA: PDeviceModeA;
begin
inherited DoSetOrientation(aValue);
if UseUnicode then
begin
if GetCurrentDevModeW(dmW) then begin
ClearDC;
dmW^.dmOrientation := Win32Orientations[aValue];
end;
end
else
begin
if GetCurrentDevModeA(dmA) then begin
ClearDC;
dmA^.dmOrientation := Win32Orientations[aValue];
end;
end;
end;
function TWinPrinter.GetPrinterType: TPrinterType;
var
Size: Dword;
InfoPrt: Pointer;
begin
Result := ptLocal;
//On Win9X all printers are local
if Win32Platform <> VER_PLATFORM_WIN32_NT then Exit;
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
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]);
if UseUnicode then
Count := DeviceCapabilitiesW(
PWidechar(UTF8Decode(Pdev.Name)),
PWidechar(UTF8Decode(PDev.Port)),
DC_COPIES,
nil,PDev.DevModeW)
else
Count := DeviceCapabilities(PChar(Pdev.Name),PCHar(PDev.Port),
DC_COPIES,nil,PDev.DevModeA);
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]);
if UseUnicode then
DocumentPropertiesW(
Widgetset.AppHandle,
FPrinterHandle,
PWidechar(UTF8Decode(PDev.Name)),
Pdev.DevModeW, Pdev.DevModeW,
DM_OUT_BUFFER or DM_IN_BUFFER or DM_IN_PROMPT)
else
DocumentProperties(
Widgetset.AppHandle,
FPrinterHandle,
pchar(PDev.Name),
Pdev.DevModeA, Pdev.DevModeA,
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;
BufferA: PChar;
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;
if UseUnicode then
Count := DeviceCapabilitiesW(
PWidechar(UTF8Decode(Pdev.Name)),
PWidechar(UTF8Decode(PDev.Port)), DC_BINNAMES, nil, nil)
else
Count := DeviceCapabilities(PChar(Pdev.Name), PCHar(PDev.Port), DC_BINNAMES, nil, nil);
if Count<=0 then
raise EPrinter.CreateFmt('DoEnumBins<DC_BINNAMES> error : %d, (%s)',
[GetLastError,SysErrorMessage(GetLastError)]);
try
if UseUnicode then
begin
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;
end
else
begin
GetMem(BufferA,24*Count);
BinC:=DeviceCapabilities(PChar(Pdev.Name),PCHar(PDev.Port),
DC_BINNAMES,BufferA,nil);
for i:=0 to BinC-1 do
begin
BinN:=StrPas(BufferA+i*24);
Lst.Add(BinN);
end;
end;
finally
if UseUnicode then
Freemem(BufferW)
else
FreeMem(BufferA);
end;
//Retreive the code of bins
FillChar(arBins,SizeOf(arBins),0);
if UseUnicode then
BinC:=DeviceCapabilitiesW(
PWidechar(UTF8Decode(Pdev.Name)),
PWidechar(UTF8Decode(PDev.Port)),
DC_BINS,
PWidechar(@ArBins[0]),
nil)
else
BinC:=DeviceCapabilities(PChar(Pdev.Name),PChar(PDev.Port),
DC_BINS,PChar(@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;
dmA: PDeviceModeA;
begin
Result:=inherited DoGetBinName;
if UseUnicode then
begin
if GetCurrentDevModeW(dmW) then
with SupportedBins do begin
i := IndexOfObject(TObject(ptrInt(dmW^.dmDefaultSource)));
if i>=0 then
result := Strings[i];
end;
end
else
begin
if GetCurrentDevModeA(dmA) then
with SupportedBins do begin
i := IndexOfObject(TObject(ptrInt(dmA^.dmDefaultSource)));
if i>=0 then
result := Strings[i];
end;
end;
end;
procedure TWinPrinter.DoSetBinName(aName: string);
var
i : Integer;
dmW: PDeviceModeW;
dmA: PDeviceModeA;
begin
with SupportedBins do begin
if (UseUnicode and (not GetCurrentDevModeW(dmW))) or ((not UseUnicode) and (not GetCurrentDevModeA(dmA))) then
raise EPrinter.Create('DoSetBinName error : unable to get current DevMode');
i := IndexOf(aName);
if (i>=0) then begin
ClearDC;
if UseUnicode then
dmW^.dmDefaultSource := SHORT(ptrint(Objects[i]))
else
dmA^.dmDefaultSource := SHORT(ptrint(Objects[i]));
end else
inherited DoSetBinName(aName); // handle uknown bin name
end;
end;
function PrinterEnumFontsProc(
var ELogFont: LCLType.TEnumLogFontEx;
var 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;
{$IFnDef WinCE}
if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then UseUnicode := False;
{$ENDIF}
{end.}