home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 December / Chip_2001-12_cd1.bin / zkuste / delphi / kompon / d345 / PPREV.ZIP / BenPreview / PrevPrinter.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2001-09-29  |  34.3 KB  |  1,111 lines

  1. Unit PrevPrinter;
  2.  
  3. Interface
  4.  
  5. {
  6.  
  7. Component Title:           Ben Zeigler's Print Preview Component
  8. Program FileName:        PrevPrinter.pas
  9. Creation Date:           You'll have to ask Ben
  10. Notes:                   Created by Ben Ziegler (bziegler@Radix.Net)
  11. Required Files:          Requires PrevForm & FormSettings & Res files Components
  12. Programming Language:    Delphi 5 now...
  13. Targeted OS:             32-bit calls everywhere -- Win32
  14.  
  15. Version      Date     Programmer        Notes
  16.   2.00     ??????       BZ         Initial release by Ben.
  17.   3.00     ??????       JGW        Initial release by JGW for Delhpi 5.  I have attempted
  18.                               to support several things.  Notably the ability to have
  19.                               landscape and portrait plots at the same time as well as
  20.                               a bounding box-zoom control but didn't succeed.  Mostly, I
  21.                               changed things to be compatable with all of the other code I had,
  22.                               and altered a few things based upon the kind of users I had--they
  23.                               got confused a lot.
  24.   3.01     09Feb01      JGW       Problem:  The original method of drawing was to simply
  25.                               take the metafile created and draw it to the canvas as is.  What
  26.                               happened, however, was that whenever you changed printers, the
  27.                               metafile still had the attributes of the old printer, and even
  28.                               with minor differences, the printouts became inconsistant.  And
  29.                               attempting to simply alter the metafiles was even more inconsistent.
  30.                                   Solution (one of many I'm sure):  Since metafiles are vectored,
  31.                               I will simply use StretchDraw to print the metafile and to view
  32.                               it on the screen.  And since the bounds of the metafile seem to be
  33.                               variable, I picked a happy median that worked.
  34.                                   Fun stuff, windows.
  35.  
  36. }
  37.  
  38. Uses SysUtils, Classes, Windows, Graphics, Forms, Printers, StdCtrls, ComCtrls;
  39.  
  40. Const
  41.   INCH_TO_CM = 2.54;
  42.  
  43. Type
  44.   TDrawStyle = ( dsStandard, dsOwnerDrawFixed, dsOwnerDrawVariable );
  45.   TPrintPageNumber = ( pnBottom, pnTop, pnNone );
  46.   TUnits = ( unInches, unCentimeters );
  47.   TZoomOption = ( zoFitToPage, zoFitToWidth, zoTwoPages, zoCustom );
  48.   TStatusType = ( stPaginating, stPrinting, stPaginationFinished, stPrintFinished );
  49.  
  50.   TOwnerHeightProc = Procedure( Sender: TObject; Line: integer; Var Height: integer; Var ForceNewPage: boolean ) Of Object;
  51.   TOwnerDrawProc = Procedure( Sender: TObject; Page, Line: integer; R: TRect; Canvas: TCanvas ) Of Object;
  52.   TNewPageProc = Procedure( Sender: TObject; Page: integer ) Of Object;
  53.   TStatusProc = Procedure( Sender: TObject; Const StatMsg: String; PageNum: integer; StatusType: TStatusType ) Of Object;
  54.   PPrinterOrientation = ^TPrinterOrientation;
  55.  
  56.   TTextOptions = Class( TPersistent )
  57.   Protected
  58.     FDrawStyle: TDrawStyle;
  59.     FLeft: double;
  60.     FTop: double;
  61.     FRight: double;
  62.     FBot: double;
  63.     FBodyFont: TFont;
  64.     FHdrFont: TFont;
  65.     FFtrFont: TFont;
  66.     FPageFont: TFont;
  67.     FHeader: String;
  68.     FFooter: String;
  69.     FHdrMarg: double;
  70.     FFtrMarg: double;
  71.     FHdrAlign: TAlignment;
  72.     FFtrAlign: TAlignment;
  73.     FPrtPage: TPrintPageNumber;
  74.     FPageAlign: TAlignment;
  75.     FPageText: String;
  76.     Procedure SetBodyFont( Val: TFont );
  77.     Procedure SetHdrFont( Val: TFont );
  78.     Procedure SetFtrFont( Val: TFont );
  79.     Procedure SetPageFont( Val: TFont );
  80.   Public
  81.     Constructor Create;
  82.     Destructor Destroy; Override;
  83.     Procedure Assign( Source: TPersistent ); Override;
  84.   Published
  85.     Property DrawStyle: TDrawStyle Read FDrawStyle Write FDrawStyle;
  86.     Property MarginLeft: double Read FLeft Write FLeft;
  87.     Property MarginTop: double Read FTop Write FTop;
  88.     Property MarginRight: double Read FRight Write FRight;
  89.     Property MarginBottom: double Read FBot Write FBot;
  90.     Property BodyFont: TFont Read FBodyFont Write SetBodyFont;
  91.     Property HeaderFont: TFont Read FHdrFont Write SetHdrFont;
  92.     Property FooterFont: TFont Read FFtrFont Write SetFtrFont;
  93.     Property PageNumFont: TFont Read FPageFont Write SetPageFont;
  94.     Property Header: String Read FHeader Write FHeader;
  95.     Property Footer: String Read FFooter Write FFooter;
  96.     Property HeaderMargin: double Read FHdrMarg Write FHdrMarg;
  97.     Property FooterMargin: double Read FFtrMarg Write FFtrMarg;
  98.     Property HeaderAlign: TAlignment Read FHdrAlign Write FHdrAlign;
  99.     Property FooterAlign: TAlignment Read FFtrAlign Write FFtrAlign;
  100.     Property PrintPageNumber: TPrintPageNumber Read FPrtPage Write FPrtPage;
  101.     Property PageNumAlign: TAlignment Read FPageAlign Write FPageAlign;
  102.     Property PageNumText: String Read FPageText Write FPageText;
  103.   End;
  104.  
  105.   TPreviewPrinter = Class( TComponent )
  106.   Protected
  107.     FOrient: TPrinterOrientation;
  108.     FPrinting: boolean;
  109.     FTitle: String;
  110.     MFList: TList;
  111.     CurCanvas: TCanvas;
  112.     ppix, ppiy: integer;
  113.     sizex, sizey: integer;
  114.     offx, offy: integer;
  115.     UsedPage: boolean;
  116.     FDrawOpt: TTextOptions;
  117.     FUnits: TUnits;
  118.     ConvFac: double;
  119.     FShowGrid: boolean;
  120.     FZoomOpt: TZoomOption;
  121.     FZoomVal: integer;
  122.     FOwnHgt: TOwnerHeightProc;
  123.     FOwnDraw: TOwnerDrawProc;
  124.     FNewPage: TNewPageProc;
  125.     FOnStatus: TStatusProc;
  126.     FCurPageIndex: LongInt;
  127.     FOrientations: TList;
  128.     FPageTitles: TStringList;
  129.     FBundlePrint: Boolean;
  130.     //FWorkingDC: HDC;
  131.   Protected
  132.     Procedure SetOrientation( Index: LongInt; Value: TPrinterOrientation );
  133.     Function GetOrientation( Index: LongInt ): TPrinterOrientation;
  134.     Function GetCanvas: TCanvas;
  135.     Function GetPageNum: integer;
  136.     Procedure FreeMetaFiles;
  137.     Function GetMetaFile( i: integer ): TMetaFile;
  138.     Function GetLastAvailPage: integer;
  139.     Procedure SetDrawOptions( NewOptions: TTextOptions );
  140.     Procedure SetUnits( Val: TUnits );
  141.     Procedure InitPrinterVars( hdc: THandle );
  142.     Procedure Loaded; Override;
  143.     Function GetMetaFileCount: LongInt;
  144.     Function GetPageOrientCount: LongInt;
  145.     Function GetPageTitleCount: LongInt;
  146.   Public
  147.     Property CurPageIndex: LongInt Write FCurPageIndex;
  148.     Constructor Create( AOwner: TComponent ); Override;
  149.     Destructor Destroy; Override;
  150.    // Helper Methods (Canvas)
  151.     Function UnitToX( x: double ): integer;
  152.     Function UnitToY( y: double ): integer;
  153.     Function XToUnit( x: integer ): double;
  154.     Function YToUnit( y: integer ): double;
  155.     Procedure DrawAlignText( y: integer; Align: TAlignment; Const Text: String; Font: TFont );
  156.     Procedure FixFont( Font: TFont );
  157.     Procedure RestoreFont( Font: TFont; PPI: integer );
  158.     Function PageSetupDlg: integer;
  159.    // Printer Methods
  160.     Procedure BeginDoc;
  161.     Procedure BeginDocEx( Title: String; Orientation: TPrinterOrientation ); Overload;
  162.     Procedure BeginDocEx( Title: String; Orientation: TPrinterOrientation; pDC: HDC ); Overload;
  163.     Procedure NewPage;
  164.     Procedure NewPageEx( Title: String; Orientation: TPrinterOrientation );
  165.     Procedure EndDoc;
  166.     Procedure Preview;
  167.     Procedure Print;
  168.     Function GetPreviewForm: TForm;
  169.     Function PrintDialog: boolean;
  170.     Function PrintRange( StartPage, StopPage: integer ): boolean;
  171.     Procedure DrawHdrFtrPage( PageNum: integer );
  172.     Procedure DrawStringList( Strings: TStrings );
  173.     Procedure DrawRichText( RE: TCustomRichEdit );
  174.     Property PageTitles: TStringList Read FPageTitles Write FPageTitles;
  175.     Property MultiPageOrientations[ Index: LongInt ]: TPrinterOrientation Read GetOrientation
  176.     Write SetOrientation;
  177.     Property PageOrientCount: LongInt Read GetPageOrientCount;
  178.     Property PageTitleCount: LongInt Read GetPageTitleCount;
  179.     Property MetaFiles[ i: integer ]: TMetaFile Read GetMetaFile;
  180.     Property PixelsPerInchX: integer Read ppix;
  181.     Property PixelsPerInchY: integer Read ppiy;
  182.     Property PageWidth: integer Read sizex;
  183.     Property PageHeight: integer Read sizey;
  184.     Property OffsetX: integer Read offx;
  185.     Property OffsetY: integer Read offy;
  186.     Property LastAvailPage: integer Read GetLastAvailPage;
  187.     Property Canvas: TCanvas Read GetCanvas;
  188.     Property PageNumber: integer Read GetPageNum;
  189.     Property Printing: boolean Read FPrinting;
  190.     Property PageCount: LongInt Read GetMetaFileCount;
  191.   Published
  192.     Property BundlePrint: Boolean Read FBundlePrint Write FBundlePrint Default true;
  193.     Property Orientation: TPrinterOrientation Read FOrient Write FOrient;
  194.     Property Title: String Read FTitle Write FTitle;
  195.     Property TextOptions: TTextOptions Read FDrawOpt Write SetDrawOptions;
  196.     Property Units: TUnits Read FUnits Write SetUnits;
  197.     Property ShowGrid: boolean Read FShowGrid Write FShowGrid;
  198.     Property ZoomOption: TZoomOption Read FZoomOpt Write FZoomOpt;
  199.     Property ZoomVal: integer Read FZoomVal Write FZoomVal;
  200.     Property OnOwnerHeight: TOwnerHeightProc Read FOwnHgt Write FOwnHgt;
  201.     Property OnOwnerDraw: TOwnerDrawProc Read FOwnDraw Write FOwnDraw;
  202.     Property OnNewPage: TNewPageProc Read FNewPage Write FNewPage;
  203.     Property OnStatus: TStatusProc Read FOnStatus Write FOnStatus;
  204.   End;
  205.  
  206. Procedure Register;
  207.  
  208. Implementation
  209.  
  210. Uses PrevForm, Controls, Dialogs, RichEdit, PageSetupDlg;
  211.  
  212. Type
  213.   TBenMetaFileCanvas = Class( TMetaFileCanvas )
  214.   Protected
  215.     OldFontChanged: TNotifyEvent;
  216.     Procedure NewFontChanged( Sender: TObject );
  217.   Public
  218.     PPI: integer;
  219.     Constructor Create( AMetafile: TMetafile; ReferenceDevice: HDC );
  220.   End;
  221.  
  222. // ************************************************************************
  223. // TBenMetaFileCanvas
  224.  
  225. Constructor TBenMetaFileCanvas.Create( AMetafile: TMetafile; ReferenceDevice: HDC );
  226. Begin
  227.   Inherited;
  228.   OldFontChanged := Font.OnChange;
  229.   Font.OnChange := NewFontChanged;
  230. End;
  231.  
  232. Procedure TBenMetaFileCanvas.NewFontChanged( Sender: TObject );
  233. Begin
  234.   If Assigned( OldFontChanged ) Then OldFontChanged( Sender );
  235. End;
  236.  
  237. // ************************************************************************
  238. // TTextOptions
  239.  
  240. Constructor TTextOptions.Create;
  241. Begin
  242.   Inherited;
  243.   DrawStyle := dsStandard;
  244.   MarginLeft := 0;
  245.   MarginTop := 0;
  246.   MarginRight := 0;
  247.   MarginBottom := 0;
  248.   FHdrMarg := 0.5;
  249.   FFtrMarg := 0.75;
  250.   FHdrAlign := taCenter;
  251.   FFtrAlign := taCenter;
  252.  
  253.   PrintPageNumber := pnBottom;
  254.   FPageAlign := taRightJustify;
  255.  
  256.   FBodyFont := TFont.Create;
  257.   FHdrFont := TFont.Create;
  258.   FFtrFont := TFont.Create;
  259.   FPageFont := TFont.Create;
  260.  
  261.   FBodyFont.Name := 'Arial';
  262.   FBodyFont.Size := 10;
  263.   FHdrFont.Name := 'Times New Roman';
  264.   FHdrFont.Size := 18;
  265.   FHdrFont.Style := [ fsBold ];
  266.   FFtrFont.Name := 'Times New Roman';
  267.   FFtrFont.Size := 10;
  268.   FFtrFont.Style := [ fsItalic ];
  269.   FPageFont.Assign( FFtrFont );
  270.  
  271.   FPageText := 'Page %d';
  272. End;
  273.  
  274. Destructor TTextOptions.Destroy;
  275. Begin
  276.   FBodyFont.Free;
  277.   FHdrFont.Free;
  278.   FFtrFont.Free;
  279.   FPageFont.Free;
  280.   Inherited;
  281. End;
  282.  
  283. Procedure TTextOptions.Assign( Source: TPersistent );
  284. Begin
  285.   If Self = Source Then exit;
  286.   MessageBeep( 0 );
  287. End;
  288.  
  289. Procedure TTextOptions.SetBodyFont( Val: TFont );
  290. Begin
  291.   FBodyFont.Assign( Val );
  292. End;
  293.  
  294. Procedure TTextOptions.SetHdrFont( Val: TFont );
  295. Begin
  296.   FHdrFont.Assign( Val );
  297. End;
  298.  
  299. Procedure TTextOptions.SetFtrFont( Val: TFont );
  300. Begin
  301.   FFtrFont.Assign( Val );
  302. End;
  303.  
  304. Procedure TTextOptions.SetPageFont( Val: TFont );
  305. Begin
  306.   FPageFont.Assign( Val );
  307. End;
  308.  
  309. // ************************************************************************
  310. // TPreviewPrinter
  311.  
  312. Constructor TPreviewPrinter.Create( AOwner: TComponent );
  313. Begin
  314.   Inherited;
  315.   FDrawOpt := TTextOptions.Create;
  316.   FPrinting := False;
  317.   FOrient := poPortrait;
  318.   CurCanvas := Nil;
  319.   MFList := TList.Create;
  320.   FUnits := unInches;
  321.   FShowGrid := False;
  322.   FZoomOpt := zoFitToPage;
  323.   FZoomVal := 100;
  324.   FCurPageIndex := -1;
  325.   FOrientations := TList.Create;
  326.   FPageTitles := TStringList.Create;
  327.   FBundlePrint := True;
  328. End;
  329.  
  330. Destructor TPreviewPrinter.Destroy;
  331. Begin
  332.   FreeMetaFiles;
  333.   MFList.Free;
  334.   FDrawOpt.Free;
  335.   FOrientations.Free;
  336.   FPageTitles.Free;
  337.   Inherited;
  338. End;
  339.  
  340. Procedure TPreviewPrinter.Loaded;
  341. Var
  342.   ps: TPageSetupForm;
  343. Begin
  344.   Inherited;
  345.   If Not ( csDesigning In ComponentState ) Then
  346.     Begin
  347.       ps := TPageSetupForm.Create( Self );
  348.       ps.TextOpt := TextOptions;
  349.       ps.pp := Self;
  350.  
  351.       ps.GetDefaults;
  352.       ps.Free;
  353.     End;
  354. End;
  355.  
  356. Function TPreviewPrinter.GetPageOrientCount: LongInt;
  357. Begin
  358.   Result := FOrientations.Count;
  359. End;
  360.  
  361. Function TPreviewPrinter.GetPageTitleCount: LongInt;
  362. Begin
  363.   Result := FPageTitles.Count;
  364. End;
  365.  
  366. Procedure TPreviewPrinter.SetDrawOptions( NewOptions: TTextOptions );
  367. Begin
  368.   If FDrawOpt <> NewOptions Then
  369.     FDrawOpt.Assign( NewOptions );
  370. End;
  371.  
  372. Function TPreviewPrinter.PageSetupDlg: integer;
  373. Var
  374.   ps: TPageSetupForm;
  375. Begin
  376.   ps := TPageSetupForm.Create( Self );
  377.   ps.TextOpt := TextOptions;
  378.   ps.pp := Self;
  379.  
  380.   Result := ps.Execute;
  381.   ps.Free;
  382. End;
  383.  
  384. Procedure TPreviewPrinter.FreeMetaFiles;
  385. Var
  386.   i: integer;
  387. Begin
  388.   For i := 0 To MFList.Count - 1 Do
  389.     MetaFiles[ i ].Free;
  390.   MFList.Clear;
  391.   CurCanvas.Free;
  392.   CurCanvas := Nil;
  393.   FOrientations.Free;
  394.   FOrientations := TList.Create;
  395.   FPageTitles.Clear;
  396. End;
  397.  
  398. Function TPreviewPrinter.GetMetaFile( i: integer ): TMetaFile;
  399. Begin
  400.   Result := MFList[ i ];
  401. End;
  402.  
  403. Function TPreviewPrinter.GetMetaFileCount: LongInt;
  404. Begin
  405.   Result := MFList.Count;
  406. End;
  407.  
  408. Procedure TPreviewPrinter.SetUnits( Val: TUnits );
  409. Begin
  410.   FUnits := Val;
  411.   Case FUnits Of
  412.     unInches: ConvFac := 1;
  413.     unCentimeters: ConvFac := INCH_TO_CM;
  414.   End;
  415. End;
  416.  
  417. Procedure TPreviewPrinter.BeginDoc;
  418. Begin
  419.   FPrinting := True;
  420.   FreeMetaFiles;
  421.   NewPage;
  422. End;
  423.  
  424. Procedure TPreviewPrinter.BeginDocEx( Title: String; Orientation: TPrinterOrientation );
  425. Begin
  426.   FPrinting := True;
  427.   FreeMetaFiles;
  428.   NewPageEx( Title, Orientation );
  429. End;
  430.  
  431. Procedure TPreviewPrinter.BeginDocEx( Title: String; Orientation: TPrinterOrientation; pDC: HDC );
  432. Begin
  433.   FPrinting := True;
  434.   FreeMetaFiles;
  435.   NewPageEx( Title, Orientation );
  436. End;
  437.  
  438. Procedure TPreviewPrinter.InitPrinterVars( hdc: THandle );
  439. Begin
  440.   ppix := GetDeviceCaps( hdc, LOGPIXELSX );
  441.   ppiy := GetDeviceCaps( hdc, LOGPIXELSY );
  442.  
  443.   If ppix = 0 Then
  444.     ppix := Screen.PixelsPerInch;
  445.   If ppiy = 0 Then
  446.     ppiy := Screen.PixelsPerInch;
  447.  
  448.   sizex := GetDeviceCaps( hdc, PHYSICALWIDTH );
  449.   sizey := GetDeviceCaps( hdc, PHYSICALHEIGHT );
  450.  
  451.   If sizex = 0 Then
  452.     Begin
  453.       sizex := Round( 8.5 * Screen.PixelsPerInch );
  454.       sizey := Round( 11 * Screen.PixelsPerInch );
  455.     End;
  456.  
  457.   offx := GetDeviceCaps( hdc, PHYSICALOFFSETX );
  458.   offy := GetDeviceCaps( hdc, PHYSICALOFFSETY );
  459. End;
  460.  
  461. Procedure TPreviewPrinter.NewPageEx( Title: String; Orientation: TPrinterOrientation );
  462. Var
  463.   CurOr: PPrinterOrientation;
  464. Begin
  465.   New( CurOr );
  466.   CurOr^ := Orientation;
  467.   FOrientations.Insert( FOrientations.Count, CurOr );
  468.   FPageTitles.Add( Title );
  469.   Self.NewPage;
  470. End;
  471.  
  472. Procedure TPreviewPrinter.NewPage;
  473. Var
  474.   MetaFile: TMetaFile;
  475.   NewCanvas: TCanvas;
  476.   UseScreen: boolean;
  477.  
  478. //  lpszDriver: String;    // pointer to string specifying driver name
  479. //  lpszDevice: String;    // pointer to string specifying device name
  480. //  lpInitData: DEVMODE;
  481. Begin
  482.   Assert( FPrinting );
  483.  
  484.   MetaFile := TMetaFile.Create;
  485.   MetaFile.Enhanced := True;
  486.   MFList.Add( MetaFile );
  487.  
  488.    // Setup up the Metafile Canvas
  489.    // Use the Default Printer if one is available, otherwise use the Screen
  490.  
  491.   UseScreen := True;
  492.   NewCanvas := Nil;
  493.  
  494.   If Printer.Printers.Count > 0 Then
  495.     Begin
  496.  
  497.     (*  lpszDriver := 'BZ-Printer' + #0;
  498.       lpszDevice := 'B.Z.-Printer' + #0;
  499.       lpInitData.dmDeviceName := 'B.Z.-Printer';
  500.       lpInitData.dmSpecVersion := 100;
  501.       lpInitData.dmDriverVersion := 100;
  502.       case Self.Orientation of
  503.         poLandScape: begin
  504.                        lpInitData.dmOrientation := DMORIENT_LANDSCAPE;
  505.                        lpInitData.dmPaperLength := 27940;
  506.                        lpInitData.dmPaperWidth := 21590;
  507.                        lpInitData.dmPelsWidth := 6360;
  508.                        lpInitData.dmPelsHeight := 4900;
  509.                      end;
  510.          poPortrait: begin
  511.                        lpInitData.dmOrientation := DMORIENT_PORTRAIT;
  512.                        lpInitData.dmPaperLength := 21590;
  513.                        lpInitData.dmPaperWidth := 27940;
  514.                        lpInitData.dmPelsWidth := 4900;
  515.                        lpInitData.dmPelsHeight := 6360;
  516.                      end;
  517.       end;
  518.  
  519.       lpInitData.dmPaperSize := DMPAPER_LETTER;
  520.       lpInitData.dmScale := 100;
  521.       lpInitData.dmCopies := 1;
  522.       lpInitData.dmDefaultSource := 0;
  523.       lpInitData.dmPrintQuality := DMRES_HIGH;
  524.       lpInitData.dmColor := DMCOLOR_COLOR;
  525.       lpInitData.dmDuplex := DMDUP_SIMPLEX;
  526.       lpInitData.dmYResolution := 6300;
  527.       lpInitData.dmTTOption := DMTT_DOWNLOAD;
  528.       lpInitData.dmCollate := DMCOLLATE_FALSE;
  529.       lpInitData.dmFormName := 'Letter';
  530.       lpInitData.dmLogPixels := 600;
  531.       lpInitData.dmBitsPerPel := 8;
  532.  
  533.       FWorkingDC := Windows.CreateDC( nil, @lpszDevice, nil, @lpInitData );
  534.       //FWorkingDC := Windows.CreateDC( 'DISPLAY', nil, nil, nil );
  535.  
  536.       if FWorkingDC = 0 then
  537.         Application.MessageBox( 'NULL DC','',0 );*)
  538.  
  539.       UseScreen := False;
  540.       Try
  541.         If Not ( FBundlePrint ) And ( FOrientations.Count > 0 ) Then
  542.           Printer.Orientation := TPrinterOrientation( FOrientations[ FOrientations.Count - 1 ] )
  543.         Else
  544.           Printer.Orientation := Orientation;
  545.         NewCanvas := TBenMetaFileCanvas.Create( MetaFile, Printer.Handle );
  546.         InitPrinterVars( Printer.Handle );
  547.         //NewCanvas := TBenMetaFileCanvas.Create( MetaFile, FWorkingDC );
  548.         //InitPrinterVars( FWorkingDC );
  549.         //Windows.DeleteDC( FWorkingDC );
  550.       Except
  551.         UseScreen := True;
  552.         NewCanvas.Free;
  553.       End;
  554.     End;
  555.  
  556.    // Use the screen if there is no Default Printer or printers installed
  557.   If UseScreen Then
  558.     Begin
  559.       NewCanvas := TBenMetaFileCanvas.Create( MetaFile, 0 );
  560.       InitPrinterVars( NewCanvas.Handle );
  561.     End;
  562.  
  563.   ( NewCanvas As TBenMetaFileCanvas ).PPI := ppiy;
  564.   NewCanvas.Font.PixelsPerInch := ppiy; // Delphi must not do this right, that's why I have to do it manually here
  565.   If CurCanvas <> Nil Then
  566.     Begin
  567.       NewCanvas.Font := CurCanvas.Font;
  568.       NewCanvas.Brush := CurCanvas.Brush;
  569.       NewCanvas.Pen := CurCanvas.Pen;
  570.     End
  571.   Else
  572.     Begin
  573.       NewCanvas.Font.Name := 'Arial';   // Need a TrueType font that can scale (MS Sans Serif doesn't scale well)
  574.       NewCanvas.Font.Size := 10;
  575.       NewCanvas.Brush.Style := bsClear;
  576.     End;
  577.  
  578.   CurCanvas.Free;
  579.   CurCanvas := NewCanvas;
  580.   UsedPage := False;
  581.  
  582.   If Assigned( OnStatus ) Then
  583.     OnStatus( Self, Format( 'Paginating page %d', [ MFList.Count ] ), MFList.Count, stPaginating );
  584. End;
  585.  
  586. Function TPreviewPrinter.UnitToX( x: double ): integer;
  587. Begin
  588.   If ConvFac <> 0 Then
  589.     Result := Round( x * ppix / ConvFac )
  590.   Else
  591.     Result := 0;
  592. End;
  593.  
  594. Function TPreviewPrinter.UnitToY( y: double ): integer;
  595. Begin
  596.   If ConvFac <> 0 Then
  597.     Result := Round( y * ppiy / ConvFac )
  598.   Else
  599.     Result := 0;
  600. End;
  601.  
  602. Function TPreviewPrinter.XToUnit( x: integer ): double;
  603. Begin
  604.   If ppix <> 0 Then
  605.     Result := x / ppix * ConvFac
  606.   Else
  607.     Result := 0;
  608. End;
  609.  
  610. Function TPreviewPrinter.YToUnit( y: integer ): double;
  611. Begin
  612.   If ppiy <> 0 Then
  613.     Result := y / ppiy * ConvFac
  614.   Else
  615.     Result := 0;
  616.  
  617. End;
  618.  
  619. Procedure TPreviewPrinter.EndDoc;
  620. Var
  621.   i: integer;
  622. Begin
  623.   FPrinting := False;
  624.   CurCanvas.Free;                       // This is to close out the MetaFile
  625.   CurCanvas := Nil;
  626.  
  627.    // This is incase they called NewPage, but never drew anything on it
  628.   If UsedPage = False Then
  629.     Begin
  630.       i := MFList.Count - 1;
  631.       MetaFiles[ MFList.Count - 1 ].Free;
  632.       MFList.Delete( i );
  633.     End;
  634.  
  635.   If Assigned( OnStatus ) Then
  636.     OnStatus( Self, 'Pagination Complete', -1, stPaginationFinished );
  637. End;
  638.  
  639. Function TPreviewPrinter.GetPreviewForm: TForm;
  640. Var
  641.   pf: TPreviewForm;
  642. Begin
  643. //   Assert(FPrinting = False); // Change this later when allow threaded printing
  644.   If FPrinting = False Then
  645.     Begin
  646.  
  647.       pf := TPreviewForm.Create( Nil );
  648.       pf.PrevPrinterObj := Self;
  649.       pf.GridBut.Down := ShowGrid;
  650.  
  651.       Case ZoomOption Of
  652.         zoFitToPage: pf.ZoomBox.ItemIndex := 0;
  653.         zoFitToWidth: pf.ZoomBox.ItemIndex := 1;
  654.         zoTwoPages: pf.TwoPageBut.Down := True;
  655.         zoCustom:
  656.           Begin
  657.             pf.ZoomBox.ItemIndex := 11;
  658.             pf.Zoom := ZoomVal;
  659.           End;
  660.       End;
  661.       pf.ScrollBox1Resize( Nil );
  662.  
  663.       Result := pf;
  664.     End
  665.   Else
  666.     Result := Nil;
  667. End;
  668.  
  669. Procedure TPreviewPrinter.Preview;
  670. Var
  671.   pf: TPreviewForm;
  672. Begin
  673.   pf := GetPreviewForm As TPreviewForm;
  674.  
  675.   If pf <> Nil Then
  676.     Begin
  677.       pf.ShowModal;
  678.       pf.Free;
  679.     End
  680.   Else
  681.     Application.MessageBox( 'Error on getting preview form', 'Error', MB_OK );
  682. End;
  683.  
  684. Function TPreviewPrinter.PrintDialog: boolean;
  685. Var
  686.   pd: TPrintDialog;
  687.   Start, Stop, Copy: integer;
  688.   StartIndex: Integer;
  689. //   MMPageW, MMPageH  : Integer;
  690. Begin
  691.   If ( Printer.PrinterIndex = -1 ) Or ( Printer.Printers.Count = 0 ) Then
  692.     Raise Exception.Create( 'NO PRINTERS AVAILABLE' );
  693.  
  694.   Result := False;
  695.  
  696.    // If the printer changes the resolution of the metafiles
  697.    //may also change.  We need to make sure we can adjust the metafiles
  698.   StartIndex := Printer.PrinterIndex;
  699.  
  700.   pd := TPrintDialog.Create( Nil );
  701.   pd.FromPage := 1;
  702.   pd.MinPage := 1;
  703.   pd.MaxPage := LastAvailPage;
  704.   pd.ToPage := LastAvailPage;
  705.   pd.Options := [ poPageNums, poSelection ];
  706.  
  707.   Try
  708.     If pd.Execute Then
  709.       Begin
  710.         Result := True;
  711.  
  712.         If pd.PrintRange = prAllPages Then
  713.           Begin
  714.             Start := 0;
  715.             Stop := LastAvailPage - 1;
  716.           End
  717.         Else If pd.PrintRange = prSelection Then
  718.           Begin
  719.             Start := FCurPageIndex;
  720.             Stop := FCurPageIndex;
  721.           End
  722.         Else
  723.           Begin
  724.             Start := pd.FromPage - 1;
  725.             Stop := pd.ToPage - 1;
  726.           End;
  727.  
  728.         Printer.Refresh;
  729.         For Copy := 1 To pd.Copies Do
  730.           Begin
  731.             PrintRange( Start, Stop );
  732.           End;
  733.       End;
  734.   Finally
  735.     pd.Free;
  736.   End;
  737. End;
  738.  
  739. Procedure TPreviewPrinter.Print;
  740. Begin
  741.   PrintRange( 0, LastAvailPage - 1 );
  742. End;
  743.  
  744. // Returns False if user cancels print job
  745.  
  746. Function TPreviewPrinter.PrintRange( StartPage, StopPage: integer ): boolean;
  747. Var
  748.   Page: integer;
  749.   tmp: TPrinterOrientation;
  750.  
  751. Begin
  752.   Screen.Cursor := crHourGlass;
  753.   Try
  754.     Result := True;
  755.     Printer.Refresh;
  756.       { Print bundling is defined as this:
  757.       Windows does not allow the changing of the printer orientation between
  758.    a single begindoc/enddoc sequence.  Therefore, if you want multiple orientations,
  759.    you must do seperate begindoc/enddoc sequences.
  760.       }
  761.  
  762.       {
  763.         Okay, here's the problem... These metafiles can cause problems when we just
  764.      lay them out there.  The metafiles are created based upon the information about the
  765.      selected printer AT THE TIME OF PRINTING.  Then when "PrintDialog" is called, there
  766.      is a possiblility of the printer to change to something with a different resolution.
  767.      And since everything just used TCanvas.Draw, the printout was just thrown out there
  768.      without regaurd to what the resolution was.  If you want from 72 Dpi to 700 dpi, you
  769.      got a thumbnail.
  770.         So how do you resolve it?  I've tried a number of things.  The first idea is that
  771.      the whole reason you use metafiles instead of a bitmap, you can resize them without
  772.      causing it to "pixelate" or even loosing detail.  It's a vectored graphic.  So why
  773.      not use the "StretchDraw" function?  Well, what I discovered is that there is a really
  774.      curous dis-connect between the starting of a metafile by the numbers and the starting
  775.      of the metafile by the picture.  I drew a rectangle on the metafile from 0,0,PageWidth,PageWidth
  776.      and hoped that would be at the edges of the page, but the end result was all over the
  777.      place.  So, as a compromise, I just picked a happy median, and stuck with it.
  778.         What would make this really good is if I could create the metafiles based upon
  779.      a device context that I created dynamically, and then used the printer only when
  780.      I actually wanted to print.  Ah, well.
  781.  
  782.         08Feb01 -jgw     }
  783.  
  784.     If FBundlePrint Then
  785.       Begin
  786.         Printer.Orientation := Orientation;
  787.         Printer.Title := Title;
  788.         InitPrinterVars( Printer.Handle );
  789.  
  790.         Printer.BeginDoc;
  791.         For Page := StartPage To StopPage Do
  792.           Begin
  793.             If Assigned( OnStatus ) Then
  794.               OnStatus( Self, Format( 'Printing page %d', [ Page ] ), Page, stPrinting );
  795.              // Print the Page
  796.              //Printer.Canvas.Draw(-offx, -offy, MetaFiles[Page]);
  797.             Case Orientation Of
  798.               poLandscape:
  799.                 Printer.Canvas.StretchDraw( Rect( 0 {(Round(ppix*0.15))}, 0 {(Round(ppiy*0.15))},
  800.                   ( Round( ppix * 10.25 ) ), ( Round( ppiy * 7.75 ) ) ), MetaFiles[ Page ] );
  801.               poPortrait:
  802.                 Printer.Canvas.StretchDraw( Rect( 0 {(Round(ppiy*0.15))}, 0 {(Round(ppix*0.15))},
  803.                   ( Round( ppiy * 7.75 ) ), ( Round( ppix * 10.25 ) ) ), MetaFiles[ Page ] );
  804.             End;
  805.  
  806.             If Page < StopPage Then Printer.NewPage;
  807.           End;
  808.         Printer.EndDoc;
  809.       End
  810.     Else
  811.       Begin                             // You can't change the printer orientation in mid-stream... go figure...
  812.         For Page := StartPage To StopPage Do
  813.           Begin
  814.             If Assigned( OnStatus ) Then
  815.               OnStatus( Self, Format( 'Printing page %d', [ Page ] ), Page, stPrinting );
  816.  
  817.              // Print the Page
  818.             If ( Page > -1 ) And ( Page < FPageTitles.Count ) Then
  819.               Printer.Title := FPageTitles[ Page ]
  820.             Else
  821.               Printer.Title := Title;
  822.  
  823.             If ( Page > -1 ) And ( Page < FOrientations.Count ) Then
  824.               Printer.Orientation := TPrinterOrientation( FOrientations[ Page ]^ )
  825.             Else
  826.               Printer.Orientation := Orientation;
  827.             InitPrinterVars( Printer.Handle );
  828.             Printer.BeginDoc;
  829.             //Printer.Canvas.Draw( -offx, -offy, MetaFiles[ Page ] );
  830.             Case Printer.Orientation Of
  831.               poLandscape:
  832.                 Printer.Canvas.StretchDraw( Rect( 0 {(Round(ppix*0.15))}, 0 {(Round(ppiy*0.15))},
  833.                   ( Round( ppix * 10.25 ) ), ( Round( ppiy * 7.75 ) ) ), MetaFiles[ Page ] );
  834.               poPortrait:
  835.                 Printer.Canvas.StretchDraw( Rect( 0 {(Round(ppiy*0.15))}, 0 {(Round(ppix*0.15))},
  836.                   ( Round( ppiy * 7.75 ) ), ( Round( ppix * 10.25 ) ) ), MetaFiles[ Page ] );
  837.             End;
  838.             Printer.EndDoc;
  839.           End;
  840.       End;
  841.     If Assigned( OnStatus ) Then
  842.       OnStatus( Self, 'Print Job Complete', -1, stPrintFinished );
  843.   Finally
  844.     Screen.Cursor := crDefault;
  845.   End;
  846. End;
  847.  
  848. Function TPreviewPrinter.GetPageNum: integer;
  849. Begin
  850.   Result := MFList.Count;
  851. End;
  852.  
  853. Function TPreviewPrinter.GetLastAvailPage: integer;
  854. Begin
  855.    // TODO:  This will change with threading
  856.   Result := GetPageNum;
  857. End;
  858.  
  859. Procedure TPreviewPrinter.SetOrientation( Index: LongInt; Value: TPrinterOrientation );
  860. Begin
  861.   If ( Index > -1 ) And ( Index < FOrientations.Count ) Then
  862.     PPrinterOrientation( FOrientations[ Index ] )^ := Value
  863.   Else
  864.     Raise Exception.CreateFmt( 'Index out-of-bounds on Setting Orientation (%d)', [ Index ] );
  865. End;
  866.  
  867. Function TPreviewPrinter.GetOrientation( Index: LongInt ): TPrinterOrientation;
  868. Begin
  869.   If ( Index > -1 ) And ( Index < FOrientations.Count ) Then
  870.     Result := PPrinterOrientation( FOrientations[ Index ] )^
  871.   Else
  872.     Raise Exception.CreateFmt( 'Index out-of-bounds on Getting Orientation (%d)', [ Index ] );
  873. End;
  874.  
  875. Function TPreviewPrinter.GetCanvas: TCanvas;
  876. Begin
  877.   Assert( FPrinting, 'Canvas is not available before BeginDoc' );
  878.   Result := CurCanvas;
  879.   UsedPage := True;
  880. End;
  881.  
  882. Procedure TPreviewPrinter.RestoreFont( Font: TFont; PPI: integer );
  883. Var
  884.   OldSize: integer;
  885. Begin
  886.   OldSize := Font.Size;
  887.   Font.PixelsPerInch := PPI;
  888.   Font.Size := OldSize;
  889. End;
  890.  
  891. Procedure TPreviewPrinter.FixFont( Font: TFont );
  892. Begin
  893.    // RestoreFont(Font, ppiy);
  894. End;
  895.  
  896. Procedure TPreviewPrinter.DrawAlignText( y: integer; Align: TAlignment; Const Text: String; Font: TFont );
  897. Var
  898.   OldFont: TFont;
  899.   x, tmp: integer;
  900. Begin
  901.   OldFont := TFont.Create;
  902.   OldFont.Assign( Canvas.Font );
  903.  
  904.   If Font <> Nil Then Canvas.Font := Font;
  905.   tmp := Canvas.TextWidth( Text );
  906.   Case Align Of
  907.     taLeftJustify: x := UnitToX( TextOptions.MarginLeft );
  908.     taCenter: x := PageWidth Div 2 - tmp Div 2;
  909.     taRightJustify: x := PageWidth - UnitToX( TextOptions.MarginRight ) - tmp;
  910.     Else
  911.       x := 0;
  912.   End;
  913.  
  914.   Canvas.TextOut( x, y, Text );
  915.  
  916.   Canvas.Font := OldFont;
  917.   OldFont.Free;
  918. End;
  919.  
  920. Procedure TPreviewPrinter.DrawHdrFtrPage( PageNum: integer );
  921. Var
  922.   y1, y2: integer;
  923.   PageStr: String;
  924. Begin
  925.   With TextOptions Do
  926.     Begin
  927.       // Draw the Header, Footer, & Page Num
  928.       y1 := UnitToY( HeaderMargin );
  929.       y2 := PageHeight - UnitToY( FooterMargin );
  930.       DrawAlignText( y1, HeaderAlign, Header, HeaderFont );
  931.       DrawAlignText( y2, FooterAlign, Footer, FooterFont );
  932.       PageStr := Format( PageNumText, [ PageNum ] );
  933.       Case PrintPageNumber Of
  934.         pnTop: DrawAlignText( y1, PageNumAlign, PageStr, PageNumFont );
  935.         pnBottom: DrawAlignText( y2, PageNumAlign, PageStr, PageNumFont );
  936.       End;
  937.     End;
  938. End;
  939.  
  940. Procedure TPreviewPrinter.DrawStringList( Strings: TStrings );
  941. Var
  942.   Line: integer;
  943.   Page: integer;
  944.   x, y: integer;
  945.   h: integer;
  946.   R: TRect;
  947.   ForceNewPage: boolean;
  948.   tm: TTextMetric;
  949. Begin
  950.   Screen.Cursor := crHourGlass;
  951.   BeginDoc;
  952.  
  953.    // Init the fonts
  954.   With TextOptions Do
  955.     Begin
  956.       FixFont( BodyFont );
  957.       FixFont( HeaderFont );
  958.       FixFont( FooterFont );
  959.       FixFont( PageNumFont );
  960.     End;
  961.  
  962.   Page := 0;
  963.  
  964.    // Begin Page
  965.   x := UnitToX( TextOptions.MarginLeft );
  966.   y := -2;
  967.  
  968.   For Line := 0 To Strings.Count - 1 Do
  969.     With TextOptions Do
  970.       Begin
  971.       // Get our current line height (and check for New Page)
  972.         Canvas.Font := BodyFont;
  973.         h := -1;
  974.         ForceNewPage := False;
  975.         If Assigned( OnOwnerHeight ) Then OnOwnerHeight( Self, Line, h, ForceNewPage );
  976.         If ForceNewPage Then
  977.           y := -1;
  978.         If DrawStyle <> dsOwnerDrawVariable Then
  979.           Begin
  980.             h := Canvas.TextHeight( 'f' );
  981.             GetTextMetrics( Canvas.Handle, tm );
  982.             h := h + tm.tmInternalLeading + tm.tmExternalLeading;
  983.           End;
  984.         If h = -1 Then Exception.Create( 'OnOwnerHeight Event returned an invalid height!' );
  985.  
  986.       // Check if we need a new page
  987.         If ( y < 0 ) Or ( y + h > UnitToY( YToUnit( PageHeight ) - TextOptions.MarginBottom ) ) Then
  988.           Begin
  989.             Page := Page + 1;
  990.             If Assigned( OnNewPage ) Then OnNewPage( Self, Page );
  991.             If y >= -1 Then NewPage;
  992.             y := UnitToY( MarginTop );
  993.  
  994.             DrawHdrFtrPage( Page );
  995.           End;
  996.  
  997.       // Draw the current line
  998.         R := Rect( UnitToX( MarginLeft ), y, PageWidth - UnitToX( MarginRight ) + 1, y + h + 1 {PageHeight - lower "g" getting clipped?} );
  999.         Case DrawStyle Of
  1000.           dsOwnerDrawFixed,
  1001.             dsOwnerDrawVariable:
  1002.             If Assigned( OnOwnerDraw ) Then OnOwnerDraw( Self, Page, Line, R, Canvas );
  1003.           Else
  1004.             Canvas.TextRect( R, x, y, Strings[ Line ] );
  1005.         End;
  1006.  
  1007.         y := y + h;
  1008.       End;
  1009.  
  1010.   EndDoc;
  1011.   Screen.Cursor := crDefault;
  1012. End;
  1013.  
  1014. Procedure RE_To_Canvas( RE: TCustomRichEdit; Canvas: TCanvas; Var R: TRect;
  1015.   Var Pos: integer; Var NeedWrap: boolean );
  1016. Var
  1017.   Range: TFormatRange;
  1018.   OutDC: HDC;
  1019.   LastChar, MaxLen,
  1020.     LogX, LogY, OldMap: Integer;
  1021. Begin
  1022.   Assert( RE <> Nil );
  1023.   Assert( IsWindow( RE.Handle ) );
  1024.  
  1025.   FillChar( Range, SizeOf( TFormatRange ), 0 );
  1026.  
  1027.   LastChar := Pos;
  1028.  
  1029.   OutDC := Canvas.Handle;
  1030.   Range.hdc := OutDC;
  1031.   Range.hdcTarget := OutDC;
  1032.   LogX := GetDeviceCaps( OutDC, LOGPIXELSX );
  1033.   LogY := GetDeviceCaps( OutDC, LOGPIXELSY );
  1034.   If IsRectEmpty( RE.PageRect ) Then
  1035.     Begin
  1036.       Range.rc := R;
  1037.     End
  1038.   Else
  1039.     Begin
  1040.       Range.rc.left := RE.PageRect.Left * 1440 Div LogX;
  1041.       Range.rc.top := RE.PageRect.Top * 1440 Div LogY;
  1042.       Range.rc.right := RE.PageRect.Right * 1440 Div LogX;
  1043.       Range.rc.bottom := RE.PageRect.Bottom * 1440 Div LogY;
  1044.     End;
  1045.   Range.rcPage := Range.rc;
  1046.   MaxLen := RE.GetTextLen;
  1047.   Range.chrg.cpMax := -1;
  1048.  
  1049.    // ensure the output DC is in text map mode
  1050.   OldMap := SetMapMode( Range.hdc, MM_TEXT );
  1051.   SendMessage( RE.Handle, EM_FORMATRANGE, 0, 0 ); // flush buffer
  1052.  
  1053.   Range.chrg.cpMin := LastChar;
  1054.   LastChar := SendMessage( RE.Handle, EM_FORMATRANGE, 1, Longint( @Range ) );
  1055.   NeedWrap := ( LastChar < MaxLen ) And ( LastChar <> -1 );
  1056.  
  1057.   SendMessage( RE.Handle, EM_FORMATRANGE, 0, 0 ); // flush buffer
  1058.   SetMapMode( OutDC, OldMap );
  1059.  
  1060.   Pos := LastChar;
  1061.   R := Range.rc;
  1062. End;
  1063.  
  1064. Procedure TPreviewPrinter.DrawRichText( RE: TCustomRichEdit );
  1065. Var
  1066.   x1, y1: integer;
  1067.   x2, y2: integer;
  1068.   R: TRect;
  1069.   Pos, Page: integer;
  1070.   NeedWrap: boolean;
  1071.   OldUnits: TUnits;
  1072. Begin
  1073.   Screen.Cursor := crHourGlass;
  1074.   OldUnits := Units;
  1075.   Units := unInches;
  1076.   Try
  1077.     Pos := 0;
  1078.     Page := 0;
  1079.     Repeat
  1080.       If Pos = 0 Then
  1081.         BeginDoc
  1082.       Else
  1083.         NewPage;
  1084.  
  1085.       Page := Page + 1;
  1086.       DrawHdrFtrPage( Page );
  1087.  
  1088.       x1 := Round( 1440.0 * TextOptions.MarginLeft );
  1089.       y1 := Round( 1440.0 * TextOptions.MarginTop );
  1090.       x2 := Round( 1440.0 * ( XToUnit( PageWidth ) - TextOptions.MarginRight ) );
  1091.       y2 := Round( 1440.0 * ( XToUnit( PageHeight ) - TextOptions.MarginBottom ) );
  1092.       R := Rect( x1, y1, x2, y2 );
  1093.  
  1094.       RE_To_Canvas( RE, Canvas, R, Pos, NeedWrap );
  1095.     Until Not NeedWrap;
  1096.  
  1097.     EndDoc;
  1098.   Finally
  1099.     Units := OldUnits;
  1100.     Screen.Cursor := crDefault;
  1101.   End;
  1102. End;
  1103.  
  1104. Procedure Register;
  1105. Begin
  1106.   RegisterComponents( 'Print Preview', [ TPreviewPrinter ] );
  1107. End;
  1108.  
  1109. End.
  1110.  
  1111.