home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 2000 March / pcp161b.iso / full / delphi / RUNIMAGE / DELPHI30 / SOURCE / VCL / PRINTERS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-08-03  |  23.3 KB  |  887 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Visual Component Library                 }
  5. {                                                       }
  6. {       Copyright (c) 1995,97 Borland International     }
  7. {                                                       }
  8. {*******************************************************}
  9.  
  10. unit Printers;
  11.  
  12. {$R-}
  13.  
  14. interface
  15.  
  16. uses Windows, WinSpool, SysUtils, Classes, Graphics, Forms;
  17.  
  18. type
  19.   EPrinter = class(Exception);
  20.  
  21.   { TPrinter }
  22.  
  23.   { The printer object encapsulates the printer interface of Windows.  A print
  24.     job is started whenever any redering is done either through a Text variable
  25.     or the printers canvas.  This job will stay open until EndDoc is called or
  26.     the Text variable is closed.  The title displayed in the Print Manager (and
  27.     on network header pages) is determined by the Title property.
  28.  
  29.     EndDoc - Terminates the print job (and closes the currently open Text).
  30.       The print job will being printing on the printer after a call to EndDoc.
  31.     NewPage - Starts a new page and increments the PageNumber property.  The
  32.       pen position of the Canvas is put back at (0, 0).
  33.     Canvas - Represents the surface of the currently printing page.  Note that
  34.       some printer do not support drawing pictures and the Draw, StretchDraw,
  35.       and CopyRect methods might fail.
  36.     Fonts - The list of fonts supported by the printer.  Note that TrueType
  37.       fonts appear in this list even if the font is not supported natively on
  38.       the printer since GDI can render them accurately for the printer.
  39.     PageHeight - The height, in pixels, of the page.
  40.     PageWidth - The width, in pixels, of the page.
  41.     PageNumber - The current page number being printed.  This is incremented
  42.       when ever the NewPage method is called.  (Note: This property can also be
  43.       incremented when a Text variable is written, a CR is encounted on the
  44.       last line of the page).
  45.     PrinterIndex - Specifies which printer in the TPrinters list that is
  46.       currently selected for printing.  Setting this property to -1 will cause
  47.       the default printer to be selected.  If this value is changed EndDoc is
  48.       called automatically.
  49.     Printers - A list of the printers installed in Windows.
  50.     Title - The title used by Windows in the Print Manager and for network
  51.       title pages. }
  52.  
  53.   TPrinterState = (psNoHandle, psHandleIC, psHandleDC);
  54.   TPrinterOrientation = (poPortrait, poLandscape);
  55.   TPrinterCapability = (pcCopies, pcOrientation, pcCollation);
  56.   TPrinterCapabilities = set of TPrinterCapability;
  57.  
  58.   TPrinter = class(TObject)
  59.   private
  60.     FCanvas: TCanvas;
  61.     FFonts: TStrings;
  62.     FPageNumber: Integer;
  63.     FPrinters: TStrings;
  64.     FPrinterIndex: Integer;
  65.     FTitle: string;
  66.     FPrinting: Boolean;
  67.     FAborted: Boolean;
  68.     FCapabilities: TPrinterCapabilities;
  69.     State: TPrinterState;
  70.     DC: HDC;
  71.     DevMode: PDeviceMode;
  72.     DeviceMode: THandle;
  73.     FPrinterHandle: THandle;
  74.     procedure SetState(Value: TPrinterState);
  75.     function GetCanvas: TCanvas;
  76.     function GetNumCopies: Integer;
  77.     function GetFonts: TStrings;
  78.     function GetHandle: HDC;
  79.     function GetOrientation: TPrinterOrientation;
  80.     function GetPageHeight: Integer;
  81.     function GetPageWidth: Integer;
  82.     function GetPrinterIndex: Integer;
  83.     procedure SetPrinterCapabilities(Value: Integer);
  84.     procedure SetPrinterIndex(Value: Integer);
  85.     function GetPrinters: TStrings;
  86.     procedure SetNumCopies(Value: Integer);
  87.     procedure SetOrientation(Value: TPrinterOrientation);
  88.     procedure SetToDefaultPrinter;
  89.     procedure CheckPrinting(Value: Boolean);
  90.     procedure FreePrinters;
  91.     procedure FreeFonts;
  92.   public
  93.     constructor Create;
  94.     destructor Destroy; override;
  95.     procedure Abort;
  96.     procedure BeginDoc;
  97.     procedure EndDoc;
  98.     procedure NewPage;
  99.     procedure GetPrinter(ADevice, ADriver, APort: PChar; var ADeviceMode: THandle);
  100.     procedure SetPrinter(ADevice, ADriver, APort: PChar; ADeviceMode: THandle);
  101.     property Aborted: Boolean read FAborted;
  102.     property Canvas: TCanvas read GetCanvas;
  103.     property Capabilities: TPrinterCapabilities read FCapabilities;
  104.     property Copies: Integer read GetNumCopies write SetNumCopies;
  105.     property Fonts: TStrings read GetFonts;
  106.     property Handle: HDC read GetHandle;
  107.     property Orientation: TPrinterOrientation read GetOrientation write SetOrientation;
  108.     property PageHeight: Integer read GetPageHeight;
  109.     property PageWidth: Integer read GetPageWidth;
  110.     property PageNumber: Integer read FPageNumber;
  111.     property PrinterIndex: Integer read GetPrinterIndex write SetPrinterIndex;
  112.     property Printing: Boolean read FPrinting;
  113.     property Printers: TStrings read GetPrinters;
  114.     property Title: string read FTitle write FTitle;
  115.   end;
  116.  
  117. { Printer function - Replaces the Printer global variable of previous versions,
  118.   to improve smart linking (reduce exe size by 2.5k in projects that don't use
  119.   the printer).  Code which assigned to the Printer global variable
  120.   must call SetPrinter instead.  SetPrinter returns current printer object
  121.   and makes the new printer object the current printer.  It is the caller's
  122.   responsibility to free the old printer, if appropriate.  (This allows
  123.   toggling between different printer objects without destroying configuration
  124.   settings.) }
  125.  
  126. function Printer: TPrinter;
  127. function SetPrinter(NewPrinter: TPrinter): TPrinter;
  128.  
  129. { AssignPrn - Assigns a Text variable to the currently selected printer.  Any
  130.   Write or Writeln's going to that file variable will be written on the
  131.   printer using the Canvas property's font.  A new page is automatically
  132.   started if a CR is encountered on (or a Writeln is written to) the last
  133.   line on the page.  Closing the text file will imply a call to the
  134.   Printer.EndDoc method. Note: only one Text variable can be open on the
  135.   printer at a time.  Opening a second will cause an exception.}
  136.  
  137. procedure AssignPrn(var F: Text);
  138.  
  139. implementation
  140.  
  141. uses Consts;
  142.  
  143. var
  144.   FPrinter: TPrinter = nil;
  145.  
  146. function FetchStr(var Str: PChar): PChar;
  147. var
  148.   P: PChar;
  149. begin
  150.   Result := Str;
  151.   if Str = nil then Exit;
  152.   P := Str;
  153.   while P^ = ' ' do Inc(P);
  154.   Result := P;
  155.   while (P^ <> #0) and (P^ <> ',') do Inc(P);
  156.   if P^ = ',' then
  157.   begin
  158.     P^ := #0;
  159.     Inc(P);
  160.   end;
  161.   Str := P;
  162. end;
  163.  
  164. procedure RaiseError(const Msg: string);
  165. begin
  166.   raise EPrinter.Create(Msg);
  167. end;
  168.  
  169. function AbortProc(Prn: HDC; Error: Integer): Bool; stdcall;
  170. begin
  171.   Application.ProcessMessages;
  172.   Result := not FPrinter.Aborted;
  173. end;
  174.  
  175. type
  176.   PrnRec = record
  177.     case Integer of
  178.       1: (
  179.         Cur: TPoint;
  180.         Finish: TPoint;         { End of the printable area }
  181.         Height: Integer);       { Height of the current line }
  182.       2: (
  183.         Tmp: array[1..32] of Char);
  184.   end;
  185.  
  186. procedure NewPage(var Prn: PrnRec);
  187. begin
  188.   with Prn do
  189.   begin
  190.     Cur.X := 0;
  191.     Cur.Y := 0;
  192.     FPrinter.NewPage;
  193.   end;
  194. end;
  195.  
  196. { Start a new line on the current page, if no more lines left start a new
  197.   page. }
  198. procedure NewLine(var Prn: PrnRec);
  199.  
  200.   function CharHeight: Word;
  201.   var
  202.     Metrics: TTextMetric;
  203.   begin
  204.     GetTextMetrics(FPrinter.Canvas.Handle, Metrics);
  205.     Result := Metrics.tmHeight;
  206.   end;
  207.  
  208. begin
  209.   with Prn do
  210.   begin
  211.     Cur.X := 0;
  212.     if Height = 0 then
  213.       Inc(Cur.Y, CharHeight) else
  214.       Inc(Cur.Y, Height);
  215.     if Cur.Y > (Finish.Y - (Height * 2)) then NewPage(Prn);
  216.     Height := 0;
  217.   end;
  218. end;
  219.  
  220. { Print a string to the printer without regard to special characters.  These
  221.   should handled by the caller. }
  222. procedure PrnOutStr(var Prn: PrnRec; Text: PChar; Len: Integer);
  223. var
  224.   Extent: TSize;
  225.   L: Integer;
  226. begin
  227.   with Prn, FPrinter.Canvas do
  228.   begin
  229.     while Len > 0 do
  230.     begin
  231.       L := Len;
  232.       GetTextExtentPoint(Handle, Text, L, Extent);
  233.  
  234.       while (L > 0) and (Extent.cX + Cur.X > Finish.X) do
  235.       begin
  236.         L := CharPrev(Text, Text+L) - Text;
  237.         GetTextExtentPoint(Handle, Text, L, Extent);
  238.       end;
  239.  
  240.       if Extent.cY > Height then Height := Extent.cY + 2;
  241.       Windows.TextOut(Handle, Cur.X, Cur.Y, Text, L);
  242.       Dec(Len, L);
  243.       Inc(Text, L);
  244.       if Len > 0 then NewLine(Prn)
  245.       else Inc(Cur.X, Extent.cX);
  246.     end;
  247.   end;
  248. end;
  249.  
  250. { Print a string to the printer handling special characters. }
  251. procedure PrnString(var Prn: PrnRec; Text: PChar; Len: Integer);
  252. var
  253.   L: Integer;
  254.   TabWidth: Word;
  255.  
  256.   procedure Flush;
  257.   begin
  258.     if L <> 0 then PrnOutStr(Prn, Text, L);
  259.     Inc(Text, L + 1);
  260.     Dec(Len, L + 1);
  261.     L := 0;
  262.   end;
  263.  
  264.   function AvgCharWidth: Word;
  265.   var
  266.     Metrics: TTextMetric;
  267.   begin
  268.     GetTextMetrics(FPrinter.Canvas.Handle, Metrics);
  269.     Result := Metrics.tmAveCharWidth;
  270.   end;
  271.  
  272. begin
  273.   L := 0;
  274.   with Prn do
  275.   begin
  276.     while L < Len do
  277.     begin
  278.       case Text[L] of
  279.         #9:
  280.           begin
  281.             Flush;
  282.             TabWidth := AvgCharWidth * 8;
  283.             Inc(Cur.X, TabWidth - ((Cur.X + TabWidth + 1)
  284.               mod TabWidth) + 1);
  285.             if Cur.X > Finish.X then NewLine(Prn);
  286.           end;
  287.         #13: Flush;
  288.         #10:
  289.           begin
  290.             Flush;
  291.             NewLine(Prn);
  292.           end;
  293.         ^L:
  294.           begin
  295.             Flush;
  296.             NewPage(Prn);
  297.           end;
  298.       else
  299.         Inc(L);
  300.       end;
  301.     end;
  302.   end;
  303.   Flush;
  304. end;
  305.  
  306. { Called when a Read or Readln is applied to a printer file. Since reading is
  307.   illegal this routine tells the I/O system that no characters where read, which
  308.   generates a runtime error. }
  309. function PrnInput(var F: TTextRec): Integer;
  310. begin
  311.   with F do
  312.   begin
  313.     BufPos := 0;
  314.     BufEnd := 0;
  315.   end;
  316.   Result := 0;
  317. end;
  318.  
  319. { Called when a Write or Writeln is applied to a printer file. The calls
  320.   PrnString to write the text in the buffer to the printer. }
  321. function PrnOutput(var F: TTextRec): Integer;
  322. begin
  323.   with F do
  324.   begin
  325.     PrnString(PrnRec(UserData), PChar(BufPtr), BufPos);
  326.     BufPos := 0;
  327.     Result := 0;
  328.   end;
  329. end;
  330.  
  331. { Will ignore certain requests by the I/O system such as flush while doing an
  332.   input. }
  333. function PrnIgnore(var F: TTextRec): Integer;
  334. begin
  335.   Result := 0;
  336. end;
  337.  
  338. { Deallocates the resources allocated to the printer file. }
  339. function PrnClose(var F: TTextRec): Integer;
  340. begin
  341.   with PrnRec(F.UserData) do
  342.   begin
  343.     FPrinter.EndDoc;
  344.     Result := 0;
  345.   end;
  346. end;
  347.  
  348. { Called to open I/O on a printer file.  Sets up the TTextFile to point to
  349.   printer I/O functions. }
  350. function PrnOpen(var F: TTextRec): Integer;
  351. const
  352.   Blank: array[0..0] of Char = '';
  353. begin
  354.   with F, PrnRec(UserData) do
  355.   begin
  356.     if Mode = fmInput then
  357.     begin
  358.       InOutFunc := @PrnInput;
  359.       FlushFunc := @PrnIgnore;
  360.       CloseFunc := @PrnIgnore;
  361.     end else
  362.     begin
  363.       Mode := fmOutput;
  364.       InOutFunc := @PrnOutput;
  365.       FlushFunc := @PrnOutput;
  366.       CloseFunc := @PrnClose;
  367.       FPrinter.BeginDoc;
  368.  
  369.       Cur.X := 0;
  370.       Cur.Y := 0;
  371.       Finish.X := FPrinter.PageWidth;
  372.       Finish.Y := FPrinter.PageHeight;
  373.       Height := 0;
  374.     end;
  375.     Result := 0;
  376.   end;
  377. end;
  378.  
  379. procedure AssignPrn(var F: Text);
  380. begin
  381.   with TTextRec(F), PrnRec(UserData) do
  382.   begin
  383.     Printer;
  384.     FillChar(F, SizeOf(F), 0);
  385.     Mode := fmClosed;
  386.     BufSize := SizeOf(Buffer);
  387.     BufPtr := @Buffer;
  388.     OpenFunc := @PrnOpen;
  389.   end;
  390. end;
  391.  
  392. { TPrinterDevice }
  393.  
  394. type
  395.   TPrinterDevice = class
  396.     Driver, Device, Port: String;
  397.     constructor Create(ADriver, ADevice, APort: PChar);
  398.     function IsEqual(ADriver, ADevice, APort: PChar): Boolean;
  399.   end;
  400.  
  401. constructor TPrinterDevice.Create(ADriver, ADevice, APort: PChar);
  402. begin
  403.   inherited Create;
  404.   Driver := ADriver;
  405.   Device := ADevice;
  406.   Port := APort;
  407. end;
  408.  
  409. function TPrinterDevice.IsEqual(ADriver, ADevice, APort: PChar): Boolean;
  410. begin
  411.   Result := (Device = ADevice) and (Port = APort);
  412. end;
  413.  
  414. { TPrinterCanvas }
  415.  
  416. type
  417.   TPrinterCanvas = class(TCanvas)
  418.     Printer: TPrinter;
  419.     constructor Create(APrinter: TPrinter);
  420.     procedure CreateHandle; override;
  421.     procedure Changing; override;
  422.     procedure UpdateFont;
  423.   end;
  424.  
  425. constructor TPrinterCanvas.Create(APrinter: TPrinter);
  426. begin
  427.   inherited Create;
  428.   Printer := APrinter;
  429. end;
  430.  
  431. procedure TPrinterCanvas.CreateHandle;
  432. begin
  433.   Printer.SetState(psHandleIC);
  434.   UpdateFont;
  435.   Handle:= Printer.DC;
  436. end;
  437.  
  438. procedure TPrinterCanvas.Changing;
  439. begin
  440.   Printer.CheckPrinting(True);
  441.   inherited Changing;
  442.   UpdateFont;
  443. end;
  444.  
  445. procedure TPrinterCanvas.UpdateFont;
  446. var
  447.   FontSize: Integer;
  448. begin
  449.   if GetDeviceCaps(Printer.DC, LOGPIXELSY) <> Font.PixelsPerInch then
  450.   begin
  451.     FontSize := Font.Size;
  452.     Font.PixelsPerInch := GetDeviceCaps(Printer.DC, LOGPIXELSY);
  453.     Font.Size := FontSize;
  454.   end;
  455. end;
  456.  
  457. { TPrinter }
  458.  
  459. constructor TPrinter.Create;
  460. begin
  461.   inherited Create;
  462.   FPrinterIndex := -1;
  463. end;
  464.  
  465. destructor TPrinter.Destroy;
  466. begin
  467.   if Printing then EndDoc;
  468.   SetState(psNoHandle);
  469.   FreePrinters;
  470.   FreeFonts;
  471.   FCanvas.Free;
  472.   if FPrinterHandle <> 0 then ClosePrinter(FPrinterHandle);
  473.   inherited Destroy;
  474. end;
  475.  
  476. procedure TPrinter.SetState(Value: TPrinterState);
  477. type
  478.   TCreateHandleFunc = function (DriverName, DeviceName, Output: PChar;
  479.     InitData: PDeviceMode): HDC stdcall;
  480. var
  481.   CreateHandleFunc: TCreateHandleFunc;
  482. begin
  483.   if Value <> State then
  484.   begin
  485.     CreateHandleFunc := nil;
  486.     case Value of
  487.       psNoHandle:
  488.         begin
  489.           CheckPrinting(False);
  490.           if Assigned(FCanvas) then FCanvas.Handle := 0;
  491.           DeleteDC(DC);
  492.           DC := 0;
  493.         end;
  494.       psHandleIC:
  495.         if State <> psHandleDC then CreateHandleFunc := CreateIC
  496.         else Exit;
  497.       psHandleDC:
  498.         begin
  499.           if FCanvas <> nil then FCanvas.Handle := 0;
  500.           if DC <> 0 then DeleteDC(DC);
  501.           CreateHandleFunc := CreateDC;
  502.         end;
  503.     end;
  504.     if Assigned(CreateHandleFunc) then
  505.       with TPrinterDevice(Printers.Objects[PrinterIndex]) do
  506.       begin
  507.         DC := CreateHandleFunc(PChar(Driver), PChar(Device), PChar(Port), DevMode);
  508.         if DC = 0 then RaiseError(SInvalidPrinter);
  509.         if FCanvas <> nil then FCanvas.Handle := DC;
  510.       end;
  511.     State := Value;
  512.   end;
  513. end;
  514.  
  515. procedure TPrinter.CheckPrinting(Value: Boolean);
  516. begin
  517.   if Printing <> Value then
  518.     if Value then RaiseError(SNotPrinting)
  519.     else RaiseError(SPrinting);
  520. end;
  521.  
  522. procedure TPrinter.Abort;
  523. begin
  524.   CheckPrinting(True);
  525.   AbortDoc(Canvas.Handle);
  526.   FAborted := True;
  527.   EndDoc;
  528. end;
  529.  
  530. procedure TPrinter.BeginDoc;
  531. var
  532.   CTitle: array[0..31] of Char;
  533.   DocInfo: TDocInfo;
  534. begin
  535.   CheckPrinting(False);
  536.   SetState(psHandleDC);
  537.   Canvas.Refresh;
  538.   TPrinterCanvas(Canvas).UpdateFont;
  539.   FPrinting := True;
  540.   FAborted := False;
  541.   FPageNumber := 1;
  542.   StrPLCopy(CTitle, Title, SizeOf(CTitle) - 1);
  543.   FillChar(DocInfo, SizeOf(DocInfo), 0);
  544.   with DocInfo do
  545.   begin
  546.     cbSize := SizeOf(DocInfo);
  547.     lpszDocName := CTitle;
  548.     lpszOutput := nil;
  549.   end;
  550.   SetAbortProc(DC, AbortProc);
  551.   StartDoc(DC, DocInfo);
  552.   StartPage(DC);
  553. end;
  554.  
  555. procedure TPrinter.EndDoc;
  556. begin
  557.   CheckPrinting(True);
  558.   EndPage(DC);
  559.   if not Aborted then Windows.EndDoc(DC);
  560.   FPrinting := False;
  561.   FAborted := False;
  562.   FPageNumber := 0;
  563. end;
  564.  
  565. procedure TPrinter.NewPage;
  566. begin
  567.   CheckPrinting(True);
  568.   EndPage(DC);
  569.   StartPage(DC);
  570.   Inc(FPageNumber);
  571.   Canvas.Refresh;
  572. end;
  573.  
  574. procedure TPrinter.GetPrinter(ADevice, ADriver, APort: PChar; var ADeviceMode: THandle);
  575. begin
  576.   with TPrinterDevice(Printers.Objects[PrinterIndex]) do
  577.   begin
  578.     StrCopy(ADevice, PChar(Device));
  579.     StrCopy(ADriver, PChar(Driver));
  580.     StrCopy(APort, PChar(Port));
  581.   end;
  582.   ADeviceMode := DeviceMode;
  583. end;
  584.  
  585. procedure TPrinter.SetPrinterCapabilities(Value: Integer);
  586. begin
  587.   FCapabilities := [];
  588.   if (Value and DM_ORIENTATION) <> 0 then
  589.     Include(FCapabilities, pcOrientation);
  590.   if (Value and DM_COPIES) <> 0 then
  591.     Include(FCapabilities, pcCopies);
  592.   if (Value and DM_COLLATE) <> 0 then
  593.     Include(FCapabilities, pcCollation);
  594. end;
  595.  
  596. procedure TPrinter.SetPrinter(ADevice, ADriver, APort: PChar; ADeviceMode: THandle);
  597. var
  598.   I, J: Integer;
  599.   StubDevMode: TDeviceMode;
  600. begin
  601.   CheckPrinting(False);
  602.   if ADeviceMode <> DeviceMode then
  603.   begin  // free the devmode block we have, and take the one we're given
  604.     if DeviceMode <> 0 then
  605.     begin
  606.       GlobalUnlock(DeviceMode);
  607.       GlobalFree(DeviceMode);
  608.     end;
  609.     DeviceMode := ADeviceMode;
  610.   end;
  611.   if DeviceMode <> 0 then
  612.   begin
  613.     DevMode := GlobalLock(DeviceMode);
  614.     SetPrinterCapabilities(DevMode.dmFields);
  615.   end;
  616.   FreeFonts;
  617.   if FPrinterHandle <> 0 then
  618.   begin
  619.     ClosePrinter(FPrinterHandle);
  620.     FPrinterHandle := 0;
  621.   end;
  622.   SetState(psNoHandle);
  623.   J := -1;
  624.   with Printers do   // <- this rebuilds the FPrinters list
  625.     for I := 0 to Count - 1 do
  626.     begin
  627.       if TPrinterDevice(Objects[I]).IsEqual(ADriver, ADevice, APort) then
  628.       begin
  629.         J := I;
  630.         Break;
  631.       end;
  632.     end;
  633.   if J = -1 then
  634.   begin
  635.     J := FPrinters.Count;
  636.     FPrinters.AddObject(Format(SDeviceOnPort, [ADevice, APort]),
  637.       TPrinterDevice.Create(ADriver, ADevice, APort));
  638.   end;
  639.   FPrinterIndex := J;
  640.   if OpenPrinter(ADevice, FPrinterHandle, nil) then
  641.   begin
  642.     if DeviceMode = 0 then  // alloc new device mode block if one was not passed in
  643.     begin
  644.       DeviceMode := GlobalAlloc(GHND,
  645.         DocumentProperties(0, FPrinterHandle, ADevice, StubDevMode,
  646.         StubDevMode, 0));
  647.       if DeviceMode <> 0 then
  648.       begin
  649.         DevMode := GlobalLock(DeviceMode);
  650.         if DocumentProperties(0, FPrinterHandle, ADevice, DevMode^,
  651.           DevMode^, DM_OUT_BUFFER) < 0 then
  652.         begin
  653.           GlobalUnlock(DeviceMode);
  654.           GlobalFree(DeviceMode);
  655.           DeviceMode := 0;
  656.         end
  657.       end;
  658.     end;
  659.     if DeviceMode <> 0 then
  660.       SetPrinterCapabilities(DevMode^.dmFields);
  661.   end;
  662. end;
  663.  
  664. function TPrinter.GetCanvas: TCanvas;
  665. begin
  666.   if FCanvas = nil then FCanvas := TPrinterCanvas.Create(Self);
  667.   Result := FCanvas;
  668. end;
  669.  
  670. function EnumFontsProc(var LogFont: TLogFont; var TextMetric: TTextMetric;
  671.   FontType: Integer; Data: Pointer): Integer; stdcall;
  672. begin
  673.   TStrings(Data).Add(LogFont.lfFaceName);
  674.   Result := 1;
  675. end;
  676.  
  677. function TPrinter.GetFonts: TStrings;
  678. begin
  679.   if FFonts = nil then
  680.   try
  681.     SetState(psHandleIC);
  682.     FFonts := TStringList.Create;
  683.     EnumFonts(DC, nil, @EnumFontsProc, Pointer(FFonts));
  684.   except
  685.     FFonts.Free;
  686.     FFonts := nil;
  687.     raise;
  688.   end;
  689.   Result := FFonts;
  690. end;
  691.  
  692. function TPrinter.GetHandle: HDC;
  693. begin
  694.   SetState(psHandleIC);
  695.   Result := DC;
  696. end;
  697.  
  698. function TPrinter.GetNumCopies: Integer;
  699. begin
  700.   GetPrinterIndex;
  701.   if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
  702.   Result := DevMode^.dmCopies;
  703. end;
  704.  
  705. procedure TPrinter.SetNumCopies(Value: Integer);
  706. begin
  707.   CheckPrinting(False);
  708.   GetPrinterIndex;
  709.   if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
  710.   SetState(psNoHandle);
  711.   DevMode^.dmCopies := Value;
  712. end;
  713.  
  714. function TPrinter.GetOrientation: TPrinterOrientation;
  715. begin
  716.   GetPrinterIndex;
  717.   if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
  718.   if DevMode^.dmOrientation = DMORIENT_PORTRAIT then Result := poPortrait
  719.   else Result := poLandscape;
  720. end;
  721.  
  722. procedure TPrinter.SetOrientation(Value: TPrinterOrientation);
  723. const
  724.   Orientations: array [TPrinterOrientation] of Integer = (
  725.     DMORIENT_PORTRAIT, DMORIENT_LANDSCAPE);
  726. begin
  727.   CheckPrinting(False);
  728.   GetPrinterIndex;
  729.   if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
  730.   SetState(psNoHandle);
  731.   DevMode^.dmOrientation := Orientations[Value];
  732. end;
  733.  
  734. function TPrinter.GetPageHeight: Integer;
  735. begin
  736.   SetState(psHandleIC);
  737.   Result := GetDeviceCaps(DC, VertRes);
  738. end;
  739.  
  740. function TPrinter.GetPageWidth: Integer;
  741. begin
  742.   SetState(psHandleIC);
  743.   Result := GetDeviceCaps(DC, HorzRes);
  744. end;
  745.  
  746. function TPrinter.GetPrinterIndex: Integer;
  747. begin
  748.   if FPrinterIndex = -1 then SetToDefaultPrinter;
  749.   Result := FPrinterIndex;
  750. end;
  751.  
  752. procedure TPrinter.SetPrinterIndex(Value: Integer);
  753. begin
  754.   CheckPrinting(False);
  755.   if (Value = -1) or (PrinterIndex = -1) then SetToDefaultPrinter
  756.   else if (Value < 0) or (Value >= Printers.Count) then RaiseError(SPrinterIndexError);
  757.   FPrinterIndex := Value;
  758.   FreeFonts;
  759.   SetState(psNoHandle);
  760. end;
  761.  
  762. function TPrinter.GetPrinters: TStrings;
  763. var
  764.   LineCur, Port: PChar;
  765.   Buffer, PrinterInfo: PChar;
  766.   I, Count, NumInfo: Integer;
  767.   Flags: Integer;
  768.   Level: Byte;
  769. begin
  770.   if FPrinters = nil then
  771.   begin
  772.     FPrinters := TStringList.Create;
  773.     Result := FPrinters;
  774.     try
  775.       if Win32Platform = VER_PLATFORM_WIN32_NT then
  776.       begin
  777.         Flags := PRINTER_ENUM_CONNECTIONS or PRINTER_ENUM_LOCAL;
  778.         Level := 4;
  779.       end
  780.       else
  781.       begin
  782.         Flags := PRINTER_ENUM_LOCAL;
  783.         Level := 5;
  784.       end;
  785.       Count := 0;
  786.       EnumPrinters(Flags, nil, Level, nil, 0, Count, NumInfo);
  787.       if Count = 0 then Exit;
  788.       GetMem(Buffer, Count);
  789.       try
  790.         if not EnumPrinters(Flags, nil, Level, PByte(Buffer), Count, Count, NumInfo) then
  791.           Exit;
  792.         PrinterInfo := Buffer;
  793.         for I := 0 to NumInfo - 1 do
  794.         begin
  795.           if Level = 4 then
  796.             with PPrinterInfo4(PrinterInfo)^ do
  797.             begin
  798.               FPrinters.AddObject(pPrinterName,
  799.                 TPrinterDevice.Create(nil, pPrinterName, nil));
  800.               Inc(PrinterInfo, sizeof(TPrinterInfo4));
  801.             end
  802.           else
  803.             with PPrinterInfo5(PrinterInfo)^ do
  804.             begin
  805.               LineCur := pPortName;
  806.               Port := FetchStr(LineCur);
  807.               while Port^ <> #0 do
  808.               begin
  809.                 FPrinters.AddObject(Format(SDeviceOnPort, [pPrinterName, Port]),
  810.                   TPrinterDevice.Create(nil, pPrinterName, Port));
  811.                 Port := FetchStr(LineCur);
  812.               end;
  813.               Inc(PrinterInfo, sizeof(TPrinterInfo5));
  814.             end;
  815.         end;
  816.       finally
  817.         FreeMem(Buffer, Count);
  818.       end;
  819.     except
  820.       FPrinters.Free;
  821.       FPrinters := nil;
  822.       raise;
  823.     end;
  824.   end;
  825.   Result := FPrinters;
  826. end;
  827.  
  828. procedure TPrinter.SetToDefaultPrinter;
  829. var
  830.   I: Integer;
  831.   DefaultPrinter: array[0..79] of Char;
  832.   Cur, Device: PChar;
  833. begin
  834.   GetProfileString('windows', 'device', '', DefaultPrinter,
  835.     SizeOf(DefaultPrinter) - 1);
  836.   Cur := DefaultPrinter;
  837.   Device := FetchStr(Cur);
  838.   with Printers do
  839.     for I := 0 to Count-1 do
  840.     begin
  841.       if TPrinterDevice(Objects[I]).Device = Device then
  842.       begin
  843.         with TPrinterDevice(Objects[I]) do
  844.           SetPrinter(PChar(Device), PChar(Driver), PChar(Port), 0);
  845.         Exit;
  846.       end;
  847.     end;
  848.   RaiseError(SNoDefaultPrinter);
  849. end;
  850.  
  851. procedure TPrinter.FreePrinters;
  852. var
  853.   I: Integer;
  854. begin
  855.   if FPrinters <> nil then
  856.   begin
  857.     for I := 0 to FPrinters.Count - 1 do
  858.       FPrinters.Objects[I].Free;
  859.     FPrinters.Free;
  860.     FPrinters := nil;
  861.   end;
  862. end;
  863.  
  864. procedure TPrinter.FreeFonts;
  865. begin
  866.   FFonts.Free;
  867.   FFonts := nil;
  868. end;
  869.  
  870. function Printer: TPrinter;
  871. begin
  872.   if FPrinter = nil then FPrinter := TPrinter.Create;
  873.   Result := FPrinter;
  874. end;
  875.  
  876. function SetPrinter(NewPrinter: TPrinter): TPrinter;
  877. begin
  878.   Result := FPrinter;
  879.   FPrinter := NewPrinter;
  880. end;
  881.  
  882. initialization
  883.  
  884. finalization
  885.   FPrinter.Free;
  886. end.
  887.