{ KOLReport v2.0 (C) 2002 by Vladimir Kladov. See Demo project attached for documentation. All other documentation planned to be added later. In version 2.0: [+] metafiles used, spooling size became less, printing quality increased. [+] with new version of KOLPrinters by Boguslaw Brandys, printer setup dialog provided. } unit KOLReport; interface //{$DEFINE use_MHPRINTER} // (uncomment line above to use TKOLMHPrinter prior to TKOLPrinter) uses Windows, Messages, KOL, {$IFDEF use_MHPRINTER} KOLMHPrinters {$ELSE} KOLPrinters, KOLPageSetupDialog {$ENDIF}; type {$IFDEF use_MHPRINTER} PPrinter = PMHPrinter; {$ENDIF} TPaperSize = ( psA4, psA5, psA6, psA3, psLetter, psCustom ); {* Available paper sizes. } TBandLayout = ( blLeft, blCenter, blRight, blExpandRight ); {* Available band layouts. } TMargins = TRect; TMF = HDC; // used as a handle to memory-based EnhMetafile. PReport = ^TReport; PPreviewObj = ^TPreviewObj; TPreviewObj = object( TObj ) {* Preview form container object. } private procedure SetCurPage(const Value: Integer); procedure SetFitMode(const Value: Integer); public Form: PControl; {* Form. } TB: PControl; {* Toolbar. } SB: PControl; {* Scrollbar. } PB: PControl; {* PaintBox. } LB: PControl; PSD: PPageSetupDlg; {Brandys} Options: TPageSetupOptions; {* Label to show current page number and total pages count. } FFitMode: Integer; {* Fit mode: 0 - fit height, 1 - fit width, 2 - 1:1 } ViewMenu: PMenu; {* Drop down menu for toolbar button TBView. } FBuf: PBitmap; {* Buffer where current page stored (scaled). } protected FReport: PReport; {* Reference to parent Report object. } FCurPage: Integer; {* Current page index. } FBufPage: Integer; {* Buffered page index. } procedure TBClick( Sender: PObj ); procedure TBDropDownViewMenu( Sender: PObj ); procedure TBViewMenuClick( Sender: PMenu; Item: Integer ); procedure AdjustButtons( Sender: PObj ); procedure PaintPage( Sender: PControl; DC: HDC ); procedure AdjustFitMode; procedure PrinterSetup; procedure ResizePreviewForm( Sender: PObj ); public destructor Destroy; virtual; {* } property CurPage: Integer read FCurPage write SetCurPage; {* Current page index (starting from 0). } function PageCount: Integer; {* Total pages count. Could be 0, if a report is empty (nothing to show). } procedure PrintAllPages; {* Call this method to print all pages. } property FitMode: Integer read FFitMode write SetFitMode; {* Fit mode: 0 - fit height, 1 - fit width, 2 - 1:1. } end; TReport = object( TObj ) {* Report object. It is used to create report and to print or preview it} private FDocName: String; FReplaceFontHeight0: Integer; FMargins: TMargins; function GetPages(Idx: Integer): TMF; function GetImages(Idx: Integer): HENHMETAFILE; procedure SetMargins(const Value: TMargins); function GetMarginsPixels( const Index: Integer ): TMargins; protected FPageTop: Boolean; FY: Integer; FOnNewPage: TOnEvent; FPrinter: PPrinter; FX: Integer; FPrinting: Boolean; FDCPages: PList; FHDPages: PList; FStage: Integer; FOnPrint: TOnEvent; FPreviewForm: PPreviewObj; FBottom: Integer; FPagePixelsSize: TSize; FPaperSize: TPaperSize; FCustomPaperSize: TSize; FPageWidth: Integer; FPageHeight: Integer; FDoubleBufferedPreview: Boolean; FCurBandHeight: Integer; FOnEndBand: TOnEvent; fNewPageHandling: Boolean; fNewBandHandling: Boolean; procedure SetPageTop(const Value: Boolean); procedure SetPrinter(const Value: PPrinter); function GetPageCount: Integer; function GetPreviewForm: PPreviewObj; procedure SetPreviewForm(const Value: PPreviewObj); function GetCurrentPage: TMF; function GetPrinter: PPrinter; function GetPagePixelsSize: TSize; function GetOrientation: TPrinterOrientation; procedure SetPaperSize(const Value: TPaperSize); procedure SetCustomPaperSize(const Value: TSize); function GetPageHeight: Integer; function GetPageWidth: Integer; procedure GetPageWidthHeight; procedure SetX(const Value: Integer); procedure SetY(const Value: Integer); protected function AddPage: TMF; function PaintBand( MF: TMF; Band: PControl; Xpos, Ypos: Integer ): Integer; function ScaleX( W: Integer ): Integer; function ScaleY( H: Integer ): Integer; procedure DoPrintPreview( Proc: TObjectMethod ); procedure DoPrint; procedure DoPreview; procedure DoPreviewModal; public Destructor Destroy; virtual; procedure Clear; {* Call this method to make report empty. If the preview form is active for the report, it is closed too. } procedure ClearPages; {* Clears all pages. } property PreviewForm: PPreviewObj read GetPreviewForm write SetPreviewForm; {* Access to preview form object. } property DoubleBufferedPreview: Boolean read FDoubleBufferedPreview write FDoubleBufferedPreview; {* Set this value to TRUE, if you wish from PreviewForm to be shown DoubleBuffered. } property PagePixelsSize: TSize read GetPagePixelsSize; {* Size of a page in screen pixels. } property Orientation: TPrinterOrientation read GetOrientation; {* Orientation of a Printer. } property PaperSize: TPaperSize read FPaperSize write SetPaperSize; {* Paper size type (psA4, psA3, ... psCustom). } property CustomPaperSize: TSize read FCustomPaperSize write SetCustomPaperSize; {* Custom paper size in millimeters. } property PageWidth: Integer read GetPageWidth; {* Paper width in Printer canvas pixels. } property PageHeight: Integer read GetPageHeight; {* Paper height in Printer canvas pixels. } property CurrentPage: TMF read GetCurrentPage; {* Current page metafile DC. Valid only while drawing the page. } property Printer: PPrinter read GetPrinter write SetPrinter; {* Printer object. } property PageTop: Boolean read FPageTop write SetPageTop; {* True, if current position is on top of current page. (It is set to True just after calling OnNewPage event, i.e. *after* printing top page colontitles). } property X: Integer read FX write SetX; {* Current X position. } property Y: Integer read FY write SetY; {* Current Y position. } public procedure AddBand( Band: PControl ); {* Call this method to add a band. Band could be any control, not only created with NewBand or NewReportLabel etc. Before adding a band, change its contant as you wish (change Caption, adjust Frames, Color, Font, etc.) } procedure AddBandEx( Band: PControl; BandLayout: TBandLayout ); {* Call this method to add a band with special aligning option. } procedure AddFooter( Band: PControl ); {* Adds a footer band to a current page. It is possible to add several footers, in such case the first is added to the bottom, and all the follows above it. } procedure AddFooterEx( Band: PControl; BandLayout: TBandLayout ); {* Adds a footer with special aligning option. } procedure AddRight( Band: PControl ); {* Adds a band or a cell just right, without shifting current Y position onto a height of a Band, like in AddBand or AddBandEx. Calling AddRight ands AddRightEx several times it is possible to construct desired band from prepared cells dynamically. If there are no place for a new band between X position and right margin of the page, new band is added from the starting of the next horizontal band automatically. } procedure AddRightEx( Band: PControl; BandLayout: TBandLayout ); {* Adds a band or a cell just right, and with additional layout options. } procedure NewPage; {* Forces new page. If called twice, empty page will be printed. } property Bottom: Integer read FBottom; {* Bottom available position (in screen pixels). Valid while drawing onto current page. } property PageCount: Integer read GetPageCount; {* Total number of pages. } property Pages[ Idx: Integer ]: TMF read GetPages; {* Access to pages metafiles DC. Valid while drawing pages. } property Images[ Idx: Integer ]: HENHMETAFILE read GetImages; {* Access to page metafiles handles. If a handle for a certain page is accessed, its metafile DC become unavailable. } procedure Print; {* Call this method to print all the pages. } procedure PrintPages( FromPage, ToPage: Integer ); {* Call this method to print given pages range. } procedure Preview; {* Call this method to show preview non-modal. Be sure, that the Report object is existing while preview is active. } procedure PreviewModal; {* Call this method to show preview form modal. } procedure Abort; {* Call this method to stop current printing. } property Printing: Boolean read FPrinting; {* True, if pages are currently printing. } property Stage: Integer read FStage; {* If OnPrint event is called, this value 1 or 2 shows a stage of printing. In the first call of OnPrint event, it has value 1, in the second its value is 2. } property OnPrint: TOnEvent read FOnPrint write FOnPrint; {* If this event is assigned, perform adding all bands in this event handler. Please remember, that OnPrint is called twice. Be sure, that all your initializations made correctly for both stages. Mainly, this method is used to provide printing some data which depends on total pages count (e.g. to print Page 1 From 10. You should store total pages count after stage 1, and use this information on stage 2). Also, this event allows to repeat printing after showing Printer setup dialog in case when some settings are changed (page size, layout, margins, etc.) } property OnNewPage: TOnEvent read FOnNewPage write FOnNewPage; {* This event is called when new page is started (by any reason). You can add here page header or footers, if you wish. } property OnEndBand: TOnEvent read FOnEndBand write FOnEndBand; {* This event can be useful when bands are created dynamically from cells calling AddRight or AddRightEx. } function HeightAvailable: Integer; {* Pixels available vertically on current page (in screen pixels). If this value is not sufficient to add a band, new page is started. It is possible to check this value manually to ensure that a certain number of bands could be fit, and to force new page if you wish from some data to be located always together, e.g. subdetail title + column header + at least 1 band of subdetail data. } property DocumentName: String read FDocName write FDocName; {* Assign a name of your document here. This value is shown in spooler queue and helps to identify your report among other printing documents. } property ReplaceFontHeight0: Integer read FReplaceFontHeight0 write FReplaceFontHeight0; {* Change this value, if default value 18 pixels is not satisfying you. While adding a band, all its fonts with FontHeight=0 are replaced by this value to provide correct scaling onto Printer device. } property Margins: TMargins read FMargins write SetMargins; {* Margins in 0.01 millimeters. } property MarginsPrinterPixels: TMargins index 1 read GetMarginsPixels; {* Margins in Printer's pixels. } property MarginsScreenPixels: TMargins index 2 read GetMarginsPixels; {* Margins in screen pixels. } end; TFrame = ( frLeft, frTop, frRight, frBottom ); {* Frames for special band control. } TFrames = set of TFrame; {* } TPaddings = packed record {* Paddings. } LeftPadding, TopPadding, RightPadding, BottomPadding: Integer; end; const AllFrames: TFrames = [ frLeft, frTop, frRight, frBottom ]; {* Use this constant to tell that all the frames are turned on. } function NewReport: PReport; {* Call this function to create report object. } procedure NewPreviewForm( var PreviewObj: PPreviewObj; AParent: PControl ); {* This function is called automatically when Preview or PreviewModal method is called for TReport object. } function NewBand( AParent: PControl; Frames: TFrames ): PControl; {* Call this function to create special band control. It is very similar to a panel, and can contain other controls as children. } function NewReportLabel( AParent: PControl; const Caption: String; Frames: TFrames ): PControl; {* Call this function to create band label. It can be used along or as a child of a band. } function NewWordWrapReportLabel( AParent: PControl; const Caption: String; Frames: TFrames ): PControl; {* Like NewReportLabel, but with WordWrap. } procedure SetPaddings( BandCtl: PControl; LeftPadding, TopPadding, RightPadding, BottomPadding: Integer ); {* Use this function to change band paddings. } type TKOLReport = PReport; TKOLBand = PControl; TKOLReportLabel = PControl; implementation const TBFrst = 0; TBPrev = 1; TBNext = 2; TBLast = 3; TBPrnt = 4; TBSetu = 5; TBView = 6; TBExit = 7; function GetProviderPrinter: PPrinter; begin Result := Printer; end; function NewReport: PReport; begin new( Result, Create ); Result.FDocName := 'Report 1'; Result.FDCPages := NewList; Result.FHDPages := NewList; Result.FCustomPaperSize.cx := 210; Result.FCustomPaperSize.cy := 270; Result.FReplaceFontHeight0 := -12; Result.FMargins := MakeRect( 500, 500, 500, 500 ); end; procedure NewPreviewForm( var PreviewObj: PPreviewObj; AParent: PControl ); var Pn: PControl; begin new( PreviewObj, Create ); PreviewObj.Form := NewForm( AParent, 'Preview' ).SetSize( 600, 600 ) .SetPosition( (GetSystemMetrics( SM_CXSCREEN ) - 600) div 2, (GetSystemMetrics( SM_CYSCREEN ) - 600) div 2 ); {Brandys} PreviewObj.Options := [psdMargins,psdSamplePage,psdPaperControl,psdPrinterControl,psdWarning,psdHundredthsOfMillimeters,psdUseMargins,psdUseMinMargins]; PreviewObj.PSD := NewPageSetupDialog(PreviewObj.Form,PreviewObj.Options); PreviewObj.PSD.SetMinMargins(500,500,500,500); PreviewObj.Form.Border := 0; Pn := NewPanel( PreviewObj.Form, esNone ).SetSize( 0, 25 ).SetAlign( caTop ); PreviewObj.TB := NewToolbar( Pn, caNone, [ tboNoDivider ], THandle(-1), [ '<<', '<', '>', '>>', ' Print', 'Setup', '^View', 'Close' ], [ -1, -1, -1, -1, 14, -2 ] ).SetAlign( caLeft ).SetSize( 440, 0 ); PreviewObj.TB.OnClick := PreviewObj.TBClick; PreviewObj.TB.OnTBDropDown := PreviewObj.TBDropDownViewMenu; NewMenu( PreviewObj.Form, 0, [ '' ], nil ); PreviewObj.ViewMenu := NewMenu( PreviewObj.Form, 0, [ '-!Fit &Height', '-!Fit &Width', '-!&1:1' ], PreviewObj.TBViewMenuClick ); PreviewObj.LB := NewLabel( Pn, '' ).SetAlign( caClient ); PreviewObj.LB.TextAlign := taRight; PreviewObj.LB.VerticalAlign := vaCenter; PreviewObj.Form.OnShow := PreviewObj.AdjustButtons; PreviewObj.SB := NewScrollBoxEx( PreviewObj.Form, esLowered ).SetAlign( caClient ); PreviewObj.PB := NewPaintBox( PreviewObj.SB ); PreviewObj.PB.OnPaint := PreviewObj.PaintPage; //PreviewObj.TB.TBButtonVisible[ TBSetu ] := FALSE; PreviewObj.Form.OnResize := PreviewObj.ResizePreviewForm; end; type PFramesData = ^TFramesData; TFramesData = packed Record Frames: TFrames; Paddings: TPaddings; end; procedure PaintFrames( Self_, Sender: PControl; DC: HDC ); var Br: HBrush; R: TRect; procedure FillFrame( X1, Y1, X2, Y2: Integer ); begin if X2 <= X1 then Exit; if Y2 <= Y1 then Exit; FillRect( DC, MakeRect( X1, Y1, X2, Y2 ), Br ); end; var Data: PFramesData; W, H, B: Integer; Fmt: DWORD; OldFont: HFont; OldBk: Integer; begin Data := Self_.CustomData; Br := CreateSolidBrush( Color2RGB( Self_.Font.Color ) ); W := Self_.ClientWidth; H := Self_.ClientHeight; B := Self_.Border; R := Self_.ClientRect; if frLeft in Data.Frames then begin FillFrame( 0, 0, B, H ); Inc( R.Left, B ); end; if frTop in Data.Frames then begin FillFrame( 0, 0, W, B ); Inc( R.Top, B ); end; if frRight in Data.Frames then begin FillFrame( W - B, 0, W, H ); Dec( R.Right, B ); end; if frBottom in Data.Frames then begin FillFrame( 0, H - B, W, H ); Dec( R.Bottom, B ); end; DeleteObject( Br ); Br := CreateSolidBrush( Color2RGB( Self_.Color ) ); FillRect( DC, R, Br ); Inc( R.Left, Data.Paddings.LeftPadding ); Inc( R.Top, Data.Paddings.TopPadding ); Dec( R.Right, Data.Paddings.RightPadding ); Dec( R.Bottom, Data.Paddings.BottomPadding ); DeleteObject( Br ); case Self_.TextAlign of taCenter: Fmt := DT_CENTER; taRight: Fmt := DT_RIGHT; else Fmt := DT_LEFT; end; case Self_.VerticalAlign of vaTop: Fmt := Fmt or DT_TOP; vaCenter: Fmt := Fmt or DT_VCENTER; vaBottom: Fmt := Fmt or DT_BOTTOM; end; if Self_.WordWrap then Fmt := Fmt or DT_WORDBREAK else Fmt := Fmt or DT_SINGLELINE; OldFont := SelectObject( DC, Self_.Font.Handle ); OldBk := SetBkMode( DC, TRANSPARENT ); DrawText( DC, PChar( Self_.Caption ), Length( Self_.Caption ), R, Fmt ); SetBkMode( DC, OldBk ); SelectObject( DC, OldFont ); end; function NewBand( AParent: PControl; Frames: TFrames ): PControl; var Data: PFramesData; begin Result := NewPanel( AParent, esNone ); Result.Color := clWhite; Result.Border := 1; Data := AllocMem( Sizeof( TFramesData ) ); Result.CustomData := Data; Data.Frames := Frames; Data.Paddings.LeftPadding := 4; Data.Paddings.RightPadding := 4; Result.OnPaint := TOnPaint( MakeMethod( Result, @ PaintFrames ) ); Result.Width := 400; Result.Height := 40; Result.aAutoSzX := 12; end; procedure InitBandLabel( L: PControl; Frames: TFrames ); var Data: PFramesData; begin L.Color := clWhite; L.Border := 1; Data := AllocMem( Sizeof( TFramesData ) ); L.CustomData := Data; Data.Frames := Frames; Data.Paddings.LeftPadding := 4; Data.Paddings.RightPadding := 4; L.OnPaint := TOnPaint( MakeMethod( L, @ PaintFrames ) ); L.aAutoSzX := 12; end; function NewReportLabel( AParent: PControl; const Caption: String; Frames: TFrames ): PControl; begin Result := NewLabel( AParent, Caption ).AutoSize( TRUE ); InitBandLabel( Result, Frames ); end; function NewWordWrapReportLabel( AParent: PControl; const Caption: String; Frames: TFrames ): PControl; begin Result := NewWordWrapLabel( AParent, Caption ).AutoSize( TRUE ); InitBandLabel( Result, Frames ); end; procedure SetPaddings( BandCtl: PControl; LeftPadding, TopPadding, RightPadding, BottomPadding: Integer ); var Data: PFramesData; WasHPadding: Integer; begin Data := BandCtl.CustomData; WasHPadding := Data.Paddings.LeftPadding + Data.Paddings.RightPadding; Data.Paddings.LeftPadding := LeftPadding; Data.Paddings.TopPadding := TopPadding; Data.Paddings.RightPadding := RightPadding; Data.Paddings.BottomPadding := BottomPadding; BandCtl.aAutoSzX := BandCtl.aAutoSzX - WasHPadding + LeftPadding + RightPadding; if BandCtl.IsAutoSize then BandCtl.AutoSize( TRUE ); end; { TReport } procedure TReport.Abort; begin Clear; if Assigned( FPrinter ) then begin if Printer.Printing then Printer.Abort; end; end; procedure TReport.AddBand(Band: PControl); begin AddBandEx( Band, blLeft ); end; procedure TReport.AddBandEx(Band: PControl; BandLayout: TBandLayout); var MF: TMF; OldW: Integer; begin if FCurBandHeight > 0 then begin if not fNewBandHandling then if Assigned( OnEndBand ) then begin fNewBandHandling := TRUE; OnEndBand( @ Self ); fNewBandHandling := FALSE; end; FX := MarginsScreenPixels.Left; FY := FY + FCurBandHeight; end; if Band.Height > HeightAvailable then NewPage; MF := CurrentPage; case BandLayout of blLeft: FY := FY + PaintBand( MF, Band, X, Y ); blRight: FY := FY + PaintBand( MF, Band, PagePixelsSize.cx - Band.Width, Y ); blCenter: FY := FY + PaintBand( MF, Band, (PagePixelsSize.cx - Band.Width) div 2, Y ); blExpandRight: begin OldW := Band.Width; try Band.Width := PagePixelsSize.cx - MarginsScreenPixels.Right - X; FY := FY + PaintBand( MF, Band, X, Y ); finally Band.Width := OldW; end; end; end; FPageTop := FALSE; FCurBandHeight := 0; end; procedure TReport.AddFooter(Band: PControl); begin AddFooterEx( Band, blLeft ); end; procedure TReport.AddFooterEx(Band: PControl; BandLayout: TBandLayout); var MF: TMF; OldW: Integer; begin if Band.Height > HeightAvailable then NewPage; MF := CurrentPage; case BandLayout of blLeft: FBottom := FBottom - PaintBand( MF, Band, 0, FBottom - Band.Height ); blRight: FBottom := FBottom - PaintBand( MF, Band, PagePixelsSize.cx - Band.Width, FBottom - Band.Height ); blCenter: FBottom := FBottom - PaintBand( MF, Band, (PagePixelsSize.cx - Band.Width) div 2, FBottom - Band.Height ); blExpandRight: begin OldW := Band.Width; try Band.Width := PagePixelsSize.cx - MarginsScreenPixels.Left - MarginsScreenPixels.Right; FBottom := FBottom - PaintBand( MF, Band, 0, FBottom - Band.Height ); finally Band.Width := OldW; end; end; end; end; function TReport.AddPage: TMF; var MF: TMF; R: TRect; DC0: HDC; begin DC0 := GetDC( 0 ); R := MakeRect( 0, 0, MulDiv(PagePixelsSize.cx, GetDeviceCaps(DC0, HORZSIZE)*100, GetDeviceCaps(DC0, HORZRES)), MulDiv(PagePixelsSize.cy, GetDeviceCaps(DC0, VERTSIZE)*100, GetDeviceCaps(DC0, VERTRES)) ); MF := CreateEnhMetaFile( DC0, nil, @ R, '' ); ReleaseDC( 0, DC0 ); FDCPages.Add( Pointer( MF ) ); Result := MF; FPageTop := TRUE; FBottom := PagePixelsSize.cy - MarginsScreenPixels.Bottom; if not fNewPageHandling then if Assigned( OnNewPage ) then begin fNewPageHandling := TRUE; OnNewPage( @ Self ); fNewPageHandling := FALSE; end; end; procedure TReport.AddRight(Band: PControl); begin AddRightEx( Band, blLeft ); end; procedure TReport.AddRightEx(Band: PControl; BandLayout: TBandLayout); var MF: TMF; OldW: Integer; begin if Band.Height > HeightAvailable then NewPage; MF := CurrentPage; if Band.Width > PagePixelsSize.cx - MarginsScreenPixels.Right - X then begin if not fNewBandHandling then if Assigned( OnEndBand ) then begin fNewBandHandling := TRUE; OnEndBand( @ Self ); fNewBandHandling := FALSE; end; FX := MarginsScreenPixels.Left; FY := FY + FCurBandHeight; FCurBandHeight := 0; end; case BandLayout of blLeft: PaintBand( MF, Band, X, Y ); blRight: PaintBand( MF, Band, X + PagePixelsSize.cx - Band.Width, Y ); blCenter: PaintBand( MF, Band, X + (PagePixelsSize.cx - X - Band.Width) div 2, Y ); blExpandRight: begin OldW := Band.Width; try Band.Width := PagePixelsSize.cx - MarginsScreenPixels.Right - X; PaintBand( MF, Band, X, Y ); finally Band.Width := OldW; end; end; end; FX := X + Band.Width; if FCurBandHeight < Band.Height then FCurBandHeight := Band.Height; FPageTop := FALSE; end; procedure TReport.Clear; begin if FPreviewForm <> nil then FPreviewForm.Form.Free; ClearPages; end; destructor TReport.Destroy; begin Clear; FDCPages.Free; FHDPages.Free; FDocName := ''; inherited; end; procedure TReport.DoPreview; begin if PageCount = 0 then Exit; PreviewForm.Form.DoubleBuffered := DoubleBufferedPreview; PreviewForm.FReport := @ Self; PreviewForm.Form.Caption := FDocName; PreviewForm.Form.Show; end; procedure TReport.DoPreviewModal; begin if PageCount = 0 then Exit; PreviewForm.Form.DoubleBuffered := DoubleBufferedPreview; PreviewForm.FReport := @ Self; PreviewForm.Form.Caption := FDocName; PreviewForm.Form.ShowModal; FPreviewForm.Form.Free; FPreviewForm := nil; end; procedure TReport.DoPrint; begin PrintPages( 0, PageCount-1 ); end; procedure TReport.DoPrintPreview(Proc: TObjectMethod); begin if Printing then Abort; if Assigned( FOnPrint ) then begin Clear; FStage := 1; FOnPrint( @ Self ); if PageCount = 0 then Exit; Clear; FStage := 2; FOnPrint( @ Self ); end; Proc; end; function TReport.GetCurrentPage: TMF; begin if PageCount = 0 then Result := AddPage else Result := Pages[ PageCount-1 ]; end; function TReport.GetOrientation: TPrinterOrientation; begin Result := Printer.Orientation; end; function TReport.GetPageHeight: Integer; begin GetPageWidthHeight; Result := FPageHeight; end; function TReport.GetPagePixelsSize: TSize; var I: Integer; P: TPoint; DC0: HDC; begin if (FPagePixelsSize.cx = 0) or (FPagePixelsSize.cy = 0) then begin case PaperSize of psA3: P := MakePoint( 297, 420 ); psA4: P := MakePoint( 210, 297 ); psA5: P := MakePoint( 148, 210 ); psA6: P := MakePoint( 105, 148 ); psLetter: P := MakePoint( 216, 280 ); else P := MakePoint( FCustomPaperSize.cx, FCustomPaperSize.cy ); end; DC0 := GetDC( 0 ); FPagePixelsSize.cx := Round( (P.x * 0.039370) * GetDeviceCaps( DC0, LOGPIXELSX ) ); FPagePixelsSize.cy := Round( (P.y * 0.039370) * GetDeviceCaps( DC0, LOGPIXELSY ) ); ReleaseDC( 0, DC0 ); end; Result := FPagePixelsSize; if Orientation = poLandscape then begin I := Result.cx; Result.cx := Result.cy; Result.cy := I; end; end; function TReport.GetPageCount: Integer; begin Result := FDCPages.Count; end; function TReport.GetPageWidth: Integer; begin GetPageWidthHeight; Result := FPageWidth; end; procedure TReport.GetPageWidthHeight; begin if (FPageWidth <> 0) and (FPageHeight <> 0) then Exit; if Printer.Printing then begin FPageWidth := Printer.PageWidth; FPageHeight := Printer.PageHeight; end else begin Printer.BeginDoc; TRY FPageWidth := Printer.PageWidth; FPageHeight := Printer.PageHeight; FINALLY Printer.Abort; END; end; end; function TReport.GetPreviewForm: PPreviewObj; begin if FPreviewForm = nil then begin NewPreviewForm( FPreviewForm, Applet ); FPreviewForm.FReport := @ Self; end; Result := FPreviewForm; end; function TReport.GetPrinter: PPrinter; begin if FPrinter = nil then FPrinter := GetProviderPrinter; Result := FPrinter; end; function TReport.HeightAvailable: Integer; begin Result := FBottom - FY; end; procedure TReport.NewPage; begin FY := MarginsScreenPixels.Top; FX := MarginsScreenPixels.Left; AddPage; end; function TReport.PaintBand(MF: TMF; Band: PControl; Xpos, Ypos: Integer): Integer; procedure PaintBandWithChildren( Band: PControl; DC: HDC ); var I: Integer; C: PControl; P0, P: TPoint; R0, R1, R2: TRect; Save: Integer; FontHeight0Replaced: Boolean; begin FontHeight0Replaced := FALSE; if (ReplaceFontHeight0 <> 0) and (Band.Font.FontHeight = 0) then begin FontHeight0Replaced := TRUE; Band.Font.FontHeight := ReplaceFontHeight0; end; Band.Perform( WM_PRINT, DC, PRF_NONCLIENT ); GetClientRect( Band.Handle, R0 ); P0 := MakePoint( 0, 0 ); ClientToScreen( Band.Handle, P0 ); GetWindowOrgEx( DC, P ); GetWindowRect( Band.Handle, R1 ); OffsetRect( R0, P0.x - R1.Left, P0.y - R1.Top ); SetWindowOrgEx( DC, P.x - (P0.x - R1.Left), P.y - (P0.y - R1.Top), @ P ); IntersectClipRect( DC, R0.Left, R0.Top, R0.Right, R0.Bottom ); Band.Perform( WM_ERASEBKGND, DC, 0 ); Band.Perform( WM_PAINT, DC, 0 ); GetWindowRect( Band.Handle, R1 ); for I := 0 to Band.ChildCount-1 do begin Save := SaveDC( DC ); C := Band.Children[ I ]; GetWindowRect( C.Handle, R2 ); SetWindowOrgEx( DC, P.x - (R2.Left - R1.Left), P.y - (R2.Top - R1.Top), nil ); IntersectClipRect( DC, 0, 0, R2.Right - R2.Left, R2.Bottom - R2.Top ); PaintBandWithChildren( C, DC ); RestoreDC( DC, Save ); end; if FontHeight0Replaced then Band.Font.FontHeight := 0; end; var OldParent: PControl; WasVisible: Boolean; WasBR: TRect; P: TPoint; Save: Integer; begin OldParent := Band.Parent; OldParent.CreateWindow; WasVisible := Band.Visible; WasBR := Band.BoundsRect; try Band.Visible := FALSE; Band.Parent := Applet; Band.Top := Applet.Height; SetParent( Band.GetWindowHandle, Applet.Handle ); Band.Visible := TRUE; Save := SaveDC( MF ); GetWindowOrgEx( MF, P ); SetWindowOrgEx( MF, P.x - Xpos, P.y - Ypos, nil ); PaintBandWithChildren( Band, MF ); SetWindowOrgEx( MF, P.x, P.y, nil ); RestoreDC( MF, Save ); finally Band.Visible := FALSE; Band.Parent := OldParent; SetParent( Band.Handle, OldParent.Handle ); Band.BoundsRect := WasBR; Band.Visible := WasVisible; end; Result := Band.Height; end; procedure TReport.Preview; begin DoPrintPreview( DoPreview ); end; procedure TReport.PreviewModal; begin DoPrintPreview( DoPreviewModal ); end; procedure TReport.Print; begin DoPrintPreview( DoPrint ); end; function TReport.ScaleX(W: Integer): Integer; begin Result := Round( W * Printer.PageWidth / PagePixelsSize.cx ); end; function TReport.ScaleY(H: Integer): Integer; begin Result := Round( H * Printer.PageHeight / PagePixelsSize.cy ); end; procedure TReport.SetCustomPaperSize(const Value: TSize); const PapSizes: array[ TPaperSize, 1..2] of Integer = ( ( 210, 297 ), ( 148, 210 ), ( 105, 148 ), ( 297, 420 ), (216, 280), ( 0, 0 ) ); var PSidx: TPaperSize; begin FCustomPaperSize := Value; for PSidx := Low( TPaperSize ) to Pred( High( TPaperSize ) ) do begin if (PapSizes[ PSidx ][ 1 ] = Value.cx) and (PapSizes[ PSidx ][ 2 ] = Value.cy) then begin PaperSize := PSidx; exit; end; end; PaperSize := psCustom; end; procedure TReport.SetPageTop(const Value: Boolean); begin FPageTop := Value; end; procedure TReport.SetPaperSize(const Value: TPaperSize); begin if FPaperSize = Value then Exit; if FPrinting then Abort; FPaperSize := Value; end; procedure TReport.SetPreviewForm(const Value: PPreviewObj); begin if FPreviewForm = Value then Exit; if FPreviewForm <> nil then FPreviewForm.Form.Free; FPreviewForm := Value; end; procedure TReport.SetPrinter(const Value: PPrinter); begin if FPrinter = Value then Exit; if FPrinting then Abort; FPrinter := Value; FPageWidth := 0; FPageHeight := 0; end; procedure TReport.SetX(const Value: Integer); begin if FX = Value then Exit; FX := Value; end; procedure TReport.SetY(const Value: Integer); begin if FY = Value then Exit; FY := Value; FCurBandHeight := 0; end; function TReport.GetPages(Idx: Integer): TMF; begin Result := TMF( FDCPages.Items[ Idx ] ); end; function TReport.GetImages(Idx: Integer): HENHMETAFILE; begin while FHDPages.Count <= Idx do FHDPages.Add( nil ); if FHDPages.Items[ Idx ] = nil then begin FHDPages.Items[ Idx ] := Pointer( CloseEnhMetafile( Pages[ Idx ] ) ); FDCPages.Items[ Idx ] := nil; end; Result := HENHMETAFILE( FHDPages.Items[ Idx ] ); end; procedure TReport.PrintPages(FromPage, ToPage: Integer); var I: Integer; MF: HENHMETAFILE; PrintingStarted: Boolean; N: Integer; begin PrintingStarted := FALSE; TRY for I := FromPage to ToPage do begin MF := Images[ I ]; if I = 0 then begin Printer.Title := FDocName; Printer.BeginDoc; PrintingStarted := TRUE; end; N := 1; while PageWidth > PagePixelsSize.cx * N do Inc( N ); PlayEnhMetaFile( Printer.Canvas.Handle, MF, MakeRect( 0, 0, PageWidth-1, PageHeight-1 ) ); if I < ToPage then Printer.NewPage; end; FINALLY if PrintingStarted then Printer.EndDoc; END; end; procedure TReport.ClearPages; var I: Integer; begin for I := PageCount-1 downto 0 do DeleteEnhMetaFile( Images[ I ] ); FDCPages.Clear; FHDPages.Clear; FY := MarginsScreenPixels.Top; FX := MarginsScreenPixels.Left; FPagePixelsSize.cx := 0; // force recalculation of Page size end; procedure TReport.SetMargins(const Value: TMargins); begin if (fMargins.Left = Value.Left) and (fMargins.Top = Value.Top) and (fMargins.Right = Value.Right) and (fMargins.Bottom = Value.Bottom) then Exit; if FPrinting then Abort; FMargins := Value; end; function TReport.GetMarginsPixels( const Index: Integer ): TMargins; var DC: HDC; begin if Index = 1 then DC := Printer.Canvas.Handle else DC := GetDC( 0 ); Result.Left := Round( Margins.Left / 1000 / 2.55 * GetDeviceCaps( DC, LOGPIXELSX ) ); Result.Right := Round( Margins.Right / 1000 / 2.55 * GetDeviceCaps( DC, LOGPIXELSX ) ); Result.Top := Round( Margins.Top / 1000 / 2.55 * GetDeviceCaps( DC, LOGPIXELSY ) ); Result.Bottom := Round( Margins.Bottom / 1000 / 2.55 * GetDeviceCaps( DC, LOGPIXELSY ) ); if Index <> 1 then ReleaseDC( 0, DC ); end; { TPreviewObj } procedure TPreviewObj.AdjustButtons( Sender: PObj ); begin TB.TBButtonEnabled[ TBFrst ] := FCurPage > 0; TB.TBButtonEnabled[ TBPrev ] := FCurPage > 0; TB.TBButtonEnabled[ TBNext ] := FCurPage < PageCount - 1; TB.TBButtonEnabled[ TBLast ] := FCurPage < PageCount - 1; TB.TBButtonEnabled[ TBPrnt ] := PageCount > 0; {$IFDEF use_MHPRINTER} TB.TBButtonEnabled[ TBExit ] := TRUE; {$ENDIF} if PageCount = 0 then LB.Caption := '' else LB.Caption := 'Page ' + Int2Str( FCurPage + 1 ) + ' from ' + Int2Str( PageCount ); end; procedure TPreviewObj.AdjustFitMode; var K: Double; begin if PageCount = 0 then Exit; case FFitMode of 0: begin // fit Height PB.Height := SB.ClientHeight; K := FReport.PagePixelsSize.cx / FReport.PagePixelsSize.cy; PB.Width := Round( K * SB.ClientHeight ); SetScrollPos( SB.Handle, SB_VERT, 0, TRUE ); end; 1: begin // fit Width PB.Width := SB.ClientWidth; K := FReport.PagePixelsSize.cy / FReport.PagePixelsSize.cx; PB.Height := Round( K * SB.ClientWidth ); end; 2: begin // 1:1 PB.Width := FReport.PagePixelsSize.cx; PB.Height := FReport.PagePixelsSize.cy; end; end; end; destructor TPreviewObj.Destroy; begin FBuf.Free; PSD.Free;{Brandys} inherited; end; function TPreviewObj.PageCount: Integer; begin Result := FReport.FDCPages.Count; end; procedure TPreviewObj.PaintPage(Sender: PControl; DC: HDC); var MF: HENHMETAFILE; Tmp: PBitmap; R: TRect; begin if FCurPage >= PageCount then Exit; MF := FReport.Images[ FCurPage ]; AdjustFitMode; {if (PB.Width = FReport.PagePixelsSize.cx) and (PB.Height = FReport.PagePixelsSize.cy) then PlayEnhMetaFile( DC, MF, MakeRect( 0, 0, PB.Width, PB.Height ) ) else} begin if (FBufPage <> FCurPage) or (FBuf = nil) or (FBuf.Width <> PB.ClientWidth) or (FBuf.Height <> PB.ClientHeight) then begin FBuf.Free; FBufPage := FCurPage; FBuf := NewDIBBitmap( PB.ClientWidth, PB.ClientHeight, pf24bit ); FBuf.Canvas.Brush.Color := clWhite; FBuf.Canvas.FillRect( MakeRect( 0, 0, FBuf.Width, FBuf.Height ) ); SetStretchBltMode( FBuf.Canvas.Handle, HALFTONE ); SetBrushOrgEx( FBuf.Canvas.Handle, 0, 0, nil ); {R := MakeRect( FReport.MarginsScreenPixels.Left, FReport.MarginsScreenPixels.Top, FBuf.Width-1 - FReport.MarginsScreenPixels.Right, FBuf.Height-1 - FReport.MarginsScreenPixels.Bottom );} R := MakeRect( 0, 0, FBuf.Width-1, FBuf.Height-1 ); if FBuf.Width >= FReport.PagePixelsSize.cx then PlayEnhMetaFile( FBuf.Canvas.Handle, MF, R ) else begin Tmp := NewDIBBitmap( FReport.PagePixelsSize.cx, FReport.PagePixelsSize.cy, pf24bit ); Tmp.Canvas.Brush.Color := clWhite; Tmp.Canvas.FillRect( MakeRect( 0, 0, Tmp.Width, Tmp.Height ) ); PlayEnhMetaFile( Tmp.Canvas.Handle, MF, MakeRect( 0, 0, Tmp.Width-1, Tmp.Height-1 ) ); Inc( R.Right ); Inc( R.Bottom ); Tmp.StretchDraw( FBuf.Canvas.Handle, R ); Tmp.Free; end; end; FBuf.Draw( DC, 0, 0 ); end; end; {$IFDEF use_MHPRINTER} procedure TPreviewObj.PrinterSetup; begin ShowMessage( 'Not implementer.' ); end; {$ELSE} procedure TPreviewObj.PrinterSetup; var Orientation: TPrinterOrientation; PgSz: TSize; M: TRect; begin Orientation := Printer.Orientation; PgSz.cx := Printer.PageWidth; PgSz.cy := Printer.PageHeight; if not Assigned( FReport.OnPrint ) then Options := Options - [ psdOrientation ]; if PSD = nil then PSD := NewPageSetupDialog( Form, Options ); PSD.SetMargins( FReport.FMargins.Left, FReport.FMargins.Top, FReport.FMargins.Right, FReport.FMargins.Bottom ); if PSD.Execute then begin Printer.Assign(PSD.Info);//assign selected options to printer DC M := PSD.GetMargins; if Assigned( FReport.OnPrint ) then if (Printer.Orientation <> Orientation) or (Printer.PageWidth <> PgSz.cx) or (Printer.PageHeight <> PgSz.cy) or not CompareMem( @ M, @ FReport.FMargins, Sizeof( M ) ) then begin FReport.FMargins := M; FCurPage := 0; FReport.ClearPages; PgSz.cx := GetDeviceCaps( Printer.Canvas.Handle, HORZSIZE ); PgSz.cy := GetDeviceCaps( Printer.Canvas.Handle, VERTSIZE ); FReport.CustomPaperSize := PgSz; FReport.OnPrint( FReport ); end; Printer.AssignMargins(M,mgMillimeters); end; end; {$ENDIF} procedure TPreviewObj.PrintAllPages; begin FReport.DoPrint; end; procedure TPreviewObj.ResizePreviewForm(Sender: PObj); begin AdjustFitMode; end; procedure TPreviewObj.SetCurPage(const Value: Integer); begin FCurPage := Value; AdjustButtons( @ Self ); end; procedure TPreviewObj.SetFitMode(const Value: Integer); begin if FFitMode = Value then Exit; FFitMode := Value; AdjustFitMode; PB.Invalidate; end; procedure TPreviewObj.TBClick(Sender: PObj); begin case PControl(Sender).CurIndex of TBFrst: { << } FCurPage := 0; TBPrev: { < } if FCurPage > 0 then Dec( FCurPage ); TBNext: { > } if FCurPage < PageCount - 1 then Inc( FCurPage ); TBLast: { >> } FCurPage := PageCount - 1; TBPrnt: {Print} PrintAllPages; TBSetu: {Setup} PrinterSetup; TBView: {View} TBDropDownViewMenu( TB ); TBExit: {Close} begin Form.Close; Exit; end; end; AdjustButtons( @ Self ); end; procedure TPreviewObj.TBDropDownViewMenu(Sender: PObj); var R: TRect; begin R := TB.TBButtonRect[ TBView ]; R.Top := R.Bottom; R.TopLeft := TB.Client2Screen( R.TopLeft ); ViewMenu.RadioCheck( FitMode ); ViewMenu.Popup( R.Left, R.Top ); end; procedure TPreviewObj.TBViewMenuClick(Sender: PMenu; Item: Integer); begin FitMode := Item; end; end.