📄 printers.pas
字号:
begin
inherited Create;
FPrinterIndex := -1;
end;
destructor TPrinter.Destroy;
begin
if Printing then EndDoc;
SetState(psNoHandle);
FreePrinters;
FreeFonts;
FCanvas.Free;
if FPrinterHandle <> 0 then ClosePrinter(FPrinterHandle);
if DeviceMode <> 0 then
begin
GlobalUnlock(DeviceMode);
GlobalFree(DeviceMode);
DeviceMode := 0;
end;
inherited Destroy;
end;
procedure TPrinter.SetState(Value: TPrinterState);
type
TCreateHandleFunc = function (DriverName, DeviceName, Output: PChar;
InitData: PDeviceMode): HDC stdcall;
var
CreateHandleFunc: TCreateHandleFunc;
begin
if Value <> State then
begin
CreateHandleFunc := nil;
case Value of
psNoHandle:
begin
CheckPrinting(False);
if Assigned(FCanvas) then FCanvas.Handle := 0;
DeleteDC(DC);
DC := 0;
end;
psHandleIC:
if State <> psHandleDC then CreateHandleFunc := CreateIC
else Exit;
psHandleDC:
begin
if FCanvas <> nil then FCanvas.Handle := 0;
if DC <> 0 then DeleteDC(DC);
CreateHandleFunc := CreateDC;
end;
end;
if Assigned(CreateHandleFunc) then
with TPrinterDevice(Printers.Objects[PrinterIndex]) do
begin
DC := CreateHandleFunc(PChar(Driver), PChar(Device), PChar(Port), DevMode);
if DC = 0 then RaiseError(SInvalidPrinter);
if FCanvas <> nil then FCanvas.Handle := DC;
end;
State := Value;
end;
end;
procedure TPrinter.CheckPrinting(Value: Boolean);
begin
if Printing <> Value then
if Value then RaiseError(SNotPrinting)
else RaiseError(SPrinting);
end;
procedure TPrinter.Abort;
begin
CheckPrinting(True);
AbortDoc(Canvas.Handle);
FAborted := True;
EndDoc;
end;
procedure TPrinter.BeginDoc;
var
DocInfo: TDocInfo;
begin
CheckPrinting(False);
SetState(psHandleDC);
Canvas.Refresh;
TPrinterCanvas(Canvas).UpdateFont;
FPrinting := True;
FAborted := False;
FPageNumber := 1;
FillChar(DocInfo, SizeOf(DocInfo), 0);
with DocInfo do
begin
cbSize := SizeOf(DocInfo);
lpszDocName := PChar(Title);
end;
SetAbortProc(DC, AbortProc);
StartDoc(DC, DocInfo);
StartPage(DC);
end;
procedure TPrinter.EndDoc;
begin
CheckPrinting(True);
EndPage(DC);
if not Aborted then Windows.EndDoc(DC);
FPrinting := False;
FAborted := False;
FPageNumber := 0;
end;
procedure TPrinter.NewPage;
begin
CheckPrinting(True);
EndPage(DC);
StartPage(DC);
Inc(FPageNumber);
Canvas.Refresh;
end;
procedure TPrinter.GetPrinter(ADevice, ADriver, APort: PChar; var ADeviceMode: THandle);
begin
with TPrinterDevice(Printers.Objects[PrinterIndex]) do
begin
StrCopy(ADevice, PChar(Device));
StrCopy(ADriver, PChar(Driver));
StrCopy(APort, PChar(Port));
end;
ADeviceMode := DeviceMode;
end;
procedure TPrinter.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);
end;
procedure TPrinter.SetPrinter(ADevice, ADriver, APort: PChar; ADeviceMode: THandle);
var
I, J: Integer;
StubDevMode: TDeviceMode;
begin
CheckPrinting(False);
if ADeviceMode <> DeviceMode then
begin // free the devmode block we have, and take the one we're given
if DeviceMode <> 0 then
begin
GlobalUnlock(DeviceMode);
GlobalFree(DeviceMode);
end;
DeviceMode := ADeviceMode;
end;
if DeviceMode <> 0 then
begin
DevMode := GlobalLock(DeviceMode);
SetPrinterCapabilities(DevMode.dmFields);
end;
FreeFonts;
if FPrinterHandle <> 0 then
begin
ClosePrinter(FPrinterHandle);
FPrinterHandle := 0;
end;
SetState(psNoHandle);
J := -1;
with Printers do // <- this rebuilds the FPrinters list
for I := 0 to Count - 1 do
begin
if TPrinterDevice(Objects[I]).IsEqual(ADriver, ADevice, APort) then
begin
TPrinterDevice(Objects[I]).Port := APort;
J := I;
Break;
end;
end;
if J = -1 then
begin
J := FPrinters.Count;
FPrinters.AddObject(Format(SDeviceOnPort, [ADevice, APort]),
TPrinterDevice.Create(ADriver, ADevice, APort));
end;
FPrinterIndex := J;
if OpenPrinter(ADevice, FPrinterHandle, nil) then
begin
if DeviceMode = 0 then // alloc new device mode block if one was not passed in
begin
DeviceMode := GlobalAlloc(GHND,
DocumentProperties(0, FPrinterHandle, ADevice, StubDevMode,
StubDevMode, 0));
if DeviceMode <> 0 then
begin
DevMode := GlobalLock(DeviceMode);
if DocumentProperties(0, FPrinterHandle, ADevice, DevMode^,
DevMode^, DM_OUT_BUFFER) < 0 then
begin
GlobalUnlock(DeviceMode);
GlobalFree(DeviceMode);
DeviceMode := 0;
end
end;
end;
if DeviceMode <> 0 then
SetPrinterCapabilities(DevMode^.dmFields);
end;
end;
function TPrinter.GetCanvas: TCanvas;
begin
if FCanvas = nil then FCanvas := TPrinterCanvas.Create(Self);
Result := FCanvas;
end;
function EnumFontsProc(var LogFont: TLogFont; var TextMetric: TTextMetric;
FontType: Integer; Data: Pointer): Integer; stdcall;
begin
TStrings(Data).Add(LogFont.lfFaceName);
Result := 1;
end;
function TPrinter.GetFonts: TStrings;
begin
if FFonts = nil then
try
SetState(psHandleIC);
FFonts := TStringList.Create;
EnumFonts(DC, nil, @EnumFontsProc, Pointer(FFonts));
except
FreeAndNil(FFonts);
raise;
end;
Result := FFonts;
end;
function TPrinter.GetHandle: HDC;
begin
SetState(psHandleIC);
Result := DC;
end;
function TPrinter.GetNumCopies: Integer;
begin
GetPrinterIndex;
if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
Result := DevMode^.dmCopies;
end;
procedure TPrinter.SetNumCopies(Value: Integer);
begin
CheckPrinting(False);
GetPrinterIndex;
if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
SetState(psNoHandle);
DevMode^.dmCopies := Value;
end;
function TPrinter.GetOrientation: TPrinterOrientation;
begin
GetPrinterIndex;
if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
if DevMode^.dmOrientation = DMORIENT_PORTRAIT then Result := poPortrait
else Result := poLandscape;
end;
procedure TPrinter.SetOrientation(Value: TPrinterOrientation);
const
Orientations: array [TPrinterOrientation] of Integer = (
DMORIENT_PORTRAIT, DMORIENT_LANDSCAPE);
begin
CheckPrinting(False);
GetPrinterIndex;
if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
SetState(psNoHandle);
DevMode^.dmOrientation := Orientations[Value];
end;
function TPrinter.GetPageHeight: Integer;
begin
SetState(psHandleIC);
Result := GetDeviceCaps(DC, VertRes);
end;
function TPrinter.GetPageWidth: Integer;
begin
SetState(psHandleIC);
Result := GetDeviceCaps(DC, HorzRes);
end;
function TPrinter.GetPrinterIndex: Integer;
begin
if FPrinterIndex = -1 then SetToDefaultPrinter;
Result := FPrinterIndex;
end;
procedure TPrinter.SetPrinterIndex(Value: Integer);
begin
CheckPrinting(False);
if (Value = -1) or (PrinterIndex = -1) then SetToDefaultPrinter
else if (Value < 0) or (Value >= Printers.Count) then RaiseError(SPrinterIndexError);
FPrinterIndex := Value;
FreeFonts;
SetState(psNoHandle);
end;
function TPrinter.GetPrinters: TStrings;
var
LineCur, Port: PChar;
Buffer, PrinterInfo: PChar;
Flags, Count, NumInfo: DWORD;
I: Integer;
Level: Byte;
begin
if FPrinters = nil then
begin
FPrinters := TStringList.Create;
Result := FPrinters;
try
if Win32Platform = VER_PLATFORM_WIN32_NT then
begin
Flags := PRINTER_ENUM_CONNECTIONS or PRINTER_ENUM_LOCAL;
Level := 4;
end
else
begin
Flags := PRINTER_ENUM_LOCAL;
Level := 5;
end;
Count := 0;
EnumPrinters(Flags, nil, Level, nil, 0, Count, NumInfo);
if Count = 0 then Exit;
GetMem(Buffer, Count);
try
if not EnumPrinters(Flags, nil, Level, PByte(Buffer), Count, Count, NumInfo) then
Exit;
PrinterInfo := Buffer;
for I := 0 to NumInfo - 1 do
begin
if Level = 4 then
with PPrinterInfo4(PrinterInfo)^ do
begin
FPrinters.AddObject(pPrinterName,
TPrinterDevice.Create(nil, pPrinterName, nil));
Inc(PrinterInfo, sizeof(TPrinterInfo4));
end
else
with PPrinterInfo5(PrinterInfo)^ do
begin
LineCur := pPortName;
Port := FetchStr(LineCur);
while Port^ <> #0 do
begin
FPrinters.AddObject(Format(SDeviceOnPort, [pPrinterName, Port]),
TPrinterDevice.Create(nil, pPrinterName, Port));
Port := FetchStr(LineCur);
end;
Inc(PrinterInfo, sizeof(TPrinterInfo5));
end;
end;
finally
FreeMem(Buffer, Count);
end;
except
FPrinters.Free;
FPrinters := nil;
raise;
end;
end;
Result := FPrinters;
end;
procedure TPrinter.SetToDefaultPrinter;
var
I: Integer;
ByteCnt, StructCnt: DWORD;
DefaultPrinter: array[0..1023] of Char;
Cur, Device: PChar;
PrinterInfo: PPrinterInfo5;
begin
ByteCnt := 0;
StructCnt := 0;
if not EnumPrinters(PRINTER_ENUM_DEFAULT, nil, 5, nil, 0, ByteCnt,
StructCnt) and (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
begin
// With no printers installed, Win95/98 fails above with "Invalid filename".
// NT succeeds and returns a StructCnt of zero.
if GetLastError = ERROR_INVALID_NAME then
RaiseError(SNoDefaultPrinter)
else
RaiseLastOSError;
end;
PrinterInfo := AllocMem(ByteCnt);
try
EnumPrinters(PRINTER_ENUM_DEFAULT, nil, 5, PrinterInfo, ByteCnt, ByteCnt,
StructCnt);
if StructCnt > 0 then
Device := PrinterInfo.pPrinterName
else begin
GetProfileString('windows', 'device', '', DefaultPrinter,
SizeOf(DefaultPrinter) - 1);
Cur := DefaultPrinter;
Device := FetchStr(Cur);
end;
with Printers do
for I := 0 to Count-1 do
begin
if AnsiSameText(TPrinterDevice(Objects[I]).Device, Device) then
begin
with TPrinterDevice(Objects[I]) do
SetPrinter(PChar(Device), PChar(Driver), PChar(Port), 0);
Exit;
end;
end;
finally
FreeMem(PrinterInfo);
end;
RaiseError(SNoDefaultPrinter);
end;
procedure TPrinter.FreePrinters;
var
I: Integer;
begin
if FPrinters <> nil then
begin
for I := 0 to FPrinters.Count - 1 do
FPrinters.Objects[I].Free;
FreeAndNil(FPrinters);
end;
end;
procedure TPrinter.FreeFonts;
begin
FreeAndNil(FFonts);
end;
function Printer: TPrinter;
begin
if FPrinter = nil then FPrinter := TPrinter.Create;
Result := FPrinter;
end;
function SetPrinter(NewPrinter: TPrinter): TPrinter;
begin
Result := FPrinter;
FPrinter := NewPrinter;
end;
procedure TPrinter.Refresh;
begin
FreeFonts;
FreePrinters;
end;
initialization
finalization
FPrinter.Free;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -