home *** CD-ROM | disk | FTP | other *** search
/ Chip 1997 April / Chip_1997-04_cd.bin / prezent / cb / data.z / PRINTERS.PAS < prev    next >
Pascal/Delphi Source File  |  1997-01-16  |  24KB  |  880 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Visual Component Library                 }
  5. {                                                       }
  6. {       Copyright (c) 1995,96 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;
  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(MsgID: Integer);
  165. begin
  166.   raise EPrinter.CreateRes(MsgID);
  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.   ADeviceMode := DeviceMode;
  577.   with TPrinterDevice(Printers.Objects[PrinterIndex]) do
  578.   begin
  579.     StrCopy(ADevice, PChar(Device));
  580.     StrCopy(ADriver, PChar(Driver));
  581.     StrCopy(APort, PChar(Port));
  582.   end;
  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
  604.     if DeviceMode <> 0 then
  605.     begin
  606.       GlobalUnlock(DeviceMode);
  607.       GlobalFree(DeviceMode);
  608.     end;
  609.     DeviceMode := ADeviceMode;
  610.     if DeviceMode <> 0 then
  611.     begin
  612.       DevMode := GlobalLock(DeviceMode);
  613.       SetPrinterCapabilities(DevMode.dmFields);
  614.     end;
  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.   for I := 0 to Printers.Count - 1 do
  625.   begin
  626.     if TPrinterDevice(Printers.Objects[I]).IsEqual(ADriver, ADevice, APort) then
  627.     begin
  628.       J := I;
  629.       Break;
  630.     end;
  631.   end;
  632.   if J = -1 then
  633.   begin
  634.     J := FPrinters.Count;
  635.     FPrinters.AddObject(FmtLoadStr(SDeviceOnPort, [ADevice, APort]),
  636.       TPrinterDevice.Create(ADriver, ADevice, APort));
  637.   end;
  638.   FPrinterIndex := J;
  639.   if OpenPrinter(ADevice, FPrinterHandle, nil) and (DeviceMode = 0) then
  640.   begin
  641.     DeviceMode := GlobalAlloc(GHND,
  642.       DocumentProperties(0, FPrinterHandle, ADevice, StubDevMode,
  643.       StubDevMode, 0));
  644.     if DeviceMode <> 0 then
  645.     begin
  646.       DevMode := GlobalLock(DeviceMode);
  647.       if DocumentProperties(0, FPrinterHandle, ADevice, DevMode^,
  648.         DevMode^, DM_OUT_BUFFER) < 0 then
  649.       begin
  650.         GlobalUnlock(DeviceMode);
  651.         GlobalFree(DeviceMode);
  652.         DeviceMode := 0;
  653.       end
  654.       else SetPrinterCapabilities(DevMode^.dmFields);
  655.     end;
  656.   end;
  657. end;
  658.  
  659. function TPrinter.GetCanvas: TCanvas;
  660. begin
  661.   if FCanvas = nil then FCanvas := TPrinterCanvas.Create(Self);
  662.   Result := FCanvas;
  663. end;
  664.  
  665. function EnumFontsProc(var LogFont: TLogFont; var TextMetric: TTextMetric;
  666.   FontType: Integer; Data: Pointer): Integer; stdcall;
  667. begin
  668.   TStrings(Data).Add(LogFont.lfFaceName);
  669.   Result := 1;
  670. end;
  671.  
  672. function TPrinter.GetFonts: TStrings;
  673. begin
  674.   if FFonts = nil then
  675.   try
  676.     SetState(psHandleIC);
  677.     FFonts := TStringList.Create;
  678.     EnumFonts(DC, nil, @EnumFontsProc, Pointer(FFonts));
  679.   except
  680.     FFonts.Free;
  681.     FFonts := nil;
  682.     raise;
  683.   end;
  684.   Result := FFonts;
  685. end;
  686.  
  687. function TPrinter.GetHandle: HDC;
  688. begin
  689.   SetState(psHandleIC);
  690.   Result := DC;
  691. end;
  692.  
  693. function TPrinter.GetNumCopies: Integer;
  694. begin
  695.   GetPrinterIndex;
  696.   if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
  697.   Result := DevMode^.dmCopies;
  698. end;
  699.  
  700. procedure TPrinter.SetNumCopies(Value: Integer);
  701. begin
  702.   CheckPrinting(False);
  703.   GetPrinterIndex;
  704.   if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
  705.   SetState(psNoHandle);
  706.   DevMode^.dmCopies := Value;
  707. end;
  708.  
  709. function TPrinter.GetOrientation: TPrinterOrientation;
  710. begin
  711.   GetPrinterIndex;
  712.   if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
  713.   if DevMode^.dmOrientation = DMORIENT_PORTRAIT then Result := poPortrait
  714.   else Result := poLandscape;
  715. end;
  716.  
  717. procedure TPrinter.SetOrientation(Value: TPrinterOrientation);
  718. const
  719.   Orientations: array [TPrinterOrientation] of Integer = (
  720.     DMORIENT_PORTRAIT, DMORIENT_LANDSCAPE);
  721. begin
  722.   CheckPrinting(False);
  723.   GetPrinterIndex;
  724.   if DeviceMode = 0 then RaiseError(SInvalidPrinterOp);
  725.   SetState(psNoHandle);
  726.   DevMode^.dmOrientation := Orientations[Value];
  727. end;
  728.  
  729. function TPrinter.GetPageHeight: Integer;
  730. begin
  731.   SetState(psHandleIC);
  732.   Result := GetDeviceCaps(DC, VertRes);
  733. end;
  734.  
  735. function TPrinter.GetPageWidth: Integer;
  736. begin
  737.   SetState(psHandleIC);
  738.   Result := GetDeviceCaps(DC, HorzRes);
  739. end;
  740.  
  741. function TPrinter.GetPrinterIndex: Integer;
  742. begin
  743.   if FPrinterIndex = -1 then SetToDefaultPrinter;
  744.   Result := FPrinterIndex;
  745. end;
  746.  
  747. procedure TPrinter.SetPrinterIndex(Value: Integer);
  748. begin
  749.   CheckPrinting(False);
  750.   if (Value = -1) or (PrinterIndex = -1) then SetToDefaultPrinter
  751.   else if (Value < 0) or (Value >= Printers.Count) then RaiseError(SPrinterIndexError);
  752.   FPrinterIndex := Value;
  753.   FreeFonts;
  754.   SetState(psNoHandle);
  755. end;
  756.  
  757. function TPrinter.GetPrinters: TStrings;
  758. var
  759.   LineCur, Port: PChar;
  760.   Buffer, PrinterInfo: PChar;
  761.   I, Count, NumInfo: Integer;
  762.   Flags: Integer;
  763.   Level: Byte;
  764. begin
  765.   if FPrinters = nil then
  766.   begin
  767.     FPrinters := TStringList.Create;
  768.     Result := FPrinters;
  769.     try
  770.       if Win32Platform = VER_PLATFORM_WIN32_NT then
  771.       begin
  772.         Flags := PRINTER_ENUM_CONNECTIONS or PRINTER_ENUM_LOCAL;
  773.         Level := 4;
  774.       end
  775.       else
  776.       begin
  777.         Flags := PRINTER_ENUM_LOCAL;
  778.         Level := 5;
  779.       end;
  780.       EnumPrinters(Flags, nil, Level, nil, 0, Count, NumInfo);
  781.       GetMem(Buffer, Count);
  782.       try
  783.         if not EnumPrinters(Flags, nil, Level, PByte(Buffer), Count, Count, NumInfo) then
  784.           Exit;
  785.         PrinterInfo := Buffer;
  786.         for I := 0 to NumInfo - 1 do
  787.         begin
  788.           if Level = 4 then
  789.             with PPrinterInfo4(PrinterInfo)^ do
  790.             begin
  791.               FPrinters.AddObject(pPrinterName,
  792.                 TPrinterDevice.Create(nil, pPrinterName, nil));
  793.               Inc(PrinterInfo, sizeof(TPrinterInfo4));
  794.             end
  795.           else
  796.             with PPrinterInfo5(PrinterInfo)^ do
  797.             begin
  798.               LineCur := pPortName;
  799.               Port := FetchStr(LineCur);
  800.               while Port^ <> #0 do
  801.               begin
  802.                 FPrinters.AddObject(FmtLoadStr(SDeviceOnPort, [pPrinterName, Port]),
  803.                   TPrinterDevice.Create(nil, pPrinterName, Port));
  804.                 Port := FetchStr(LineCur);
  805.               end;
  806.               Inc(PrinterInfo, sizeof(TPrinterInfo5));
  807.             end;
  808.         end;
  809.       finally
  810.         FreeMem(Buffer, Count);
  811.       end;
  812.     except
  813.       FPrinters.Free;
  814.       FPrinters := nil;
  815.       raise;
  816.     end;
  817.   end;
  818.   Result := FPrinters;
  819. end;
  820.  
  821. procedure TPrinter.SetToDefaultPrinter;
  822. var
  823.   I: Integer;
  824.   DefaultPrinter: array[0..79] of Char;
  825.   Cur, Device: PChar;
  826. begin
  827.   GetProfileString('windows', 'device', '', DefaultPrinter,
  828.     SizeOf(DefaultPrinter) - 1);
  829.   Cur := DefaultPrinter;
  830.   Device := FetchStr(Cur);
  831.   with Printers do
  832.     for I := 0 to Count-1 do
  833.     begin
  834.       if TPrinterDevice(Objects[I]).Device = Device then
  835.       begin
  836.         with TPrinterDevice(Objects[I]) do
  837.           SetPrinter(PChar(Device), PChar(Driver), PChar(Port), 0);
  838.         Exit;
  839.       end;
  840.     end;
  841.   RaiseError(SNoDefaultPrinter);
  842. end;
  843.  
  844. procedure TPrinter.FreePrinters;
  845. var
  846.   I: Integer;
  847. begin
  848.   if FPrinters <> nil then
  849.   begin
  850.     for I := 0 to FPrinters.Count - 1 do
  851.       FPrinters.Objects[I].Free;
  852.     FPrinters.Free;
  853.     FPrinters := nil;
  854.   end;
  855. end;
  856.  
  857. procedure TPrinter.FreeFonts;
  858. begin
  859.   FFonts.Free;
  860.   FFonts := nil;
  861. end;
  862.  
  863. function Printer: TPrinter;
  864. begin
  865.   if FPrinter = nil then FPrinter := TPrinter.Create;
  866.   Result := FPrinter;
  867. end;
  868.  
  869. function SetPrinter(NewPrinter: TPrinter): TPrinter;
  870. begin
  871.   Result := FPrinter;
  872.   FPrinter := NewPrinter;
  873. end;
  874.  
  875. initialization
  876.   FPrinter := nil;
  877. finalization
  878.   FPrinter.Free;
  879. end.
  880.