Copyright © 2012 by CODE4SALE, LLC All Rights Reserved -
Contact CODE4SALE, LLC.
CODE4SALE, LLC - TExcellent home page
TExcellent documentation home page!
Try TExcellentFormPrinter!
Buy TExcellentFormPrinter!
Try TExcellentImagePrinter!
Buy TExcellentImagePrinter!
Product names, trademarks, and servicemarks mentioned are owned by their respective owners.
Your spelling is terrible!
How do I install the product?
What components work with TExcellentFormPrinter?
How can I improve the reliability of printing from my application?
Why does the TExcellentDemo have more options than TExcellentExample?
Can I print VCL forms and images to devices contexts other than a printer?
How can I reduce the print job size?
Whats the deal with uneven printer margins?
Can I use different Windows mapping modes?
Help! My printouts are still blank, garbled, or only partially print!
Help! My Xerox WorkCentre does not print from Delphi and C++Builder!
TExcellent prints lines across the printout!
Can I print other things on the page in addition to my form?
How can I print VCL forms without displaying them on the screen?
Can TExcellentFormPrinter print controls instead of the form?
Help! My Woll2Woll TwwDBRichEdit does not print!
Help! My SpeedButton does not print in the down position!
Help! My Async Terminal does not print!
What happened to the debug variables in TExcellentProducts?
What happened with the short circuit boolean evaluation problem?
Help! I want to scale my bitmap to fit...!
Help! I want to scale my form to fit...!
What components work with TExcellentFormPrinter?
TExcellentFormPrinter should work with most all Delphi/BCB graphic controls. TExcellentFormPrinter also contains hooks that allow you to tap into the printing system and successfully print most problematic VCL controls! TExcellentFormPrinter has been tested to work with the following standard controls*:TAnimate TBdChart TBevel TBitBtn TButton TCalendar TCCalendar TCColorGrid TCDirectoryOutline TCGauge TChart TCheckBox TCheckListBox TColorGrid TComboBox TCoolBar TCSpinButton TCSpinEdit TDateTimePicker TDBCheckBox TDBComboBox TDBCtrlGrid TDBEdit TDBGrid TDBImage TDBListBox TDBLookupCombo TDBLookupComboBox TDBLookupList TDBLookupListBox TDBMemo TDBNavigator TDBRadioGroup TDBRichEdit TDBText TDirectoryListBox TDirectoryOutline TDrawGrid TDriveComboBox TEdit TFileListBox TFilterComboBox TGauge TGroupBox THeader THeaderControl THotKey TImage TLabel TListBox TListView TMaskEdit TMediaPlayer TMemo TMonthCalendar TNotebook TOleContainer TOutline TPageControl TPaintBox TPanel TPerformanceGraph TPie TProgressBar TRadioButton TRadioGroup TRichEdit TScrollBar TScrollBox TShape TSpeedButton TSpinButton TSpinEdit TSplitter TStaticText TStatusBar TStringGrid TTabbedNotebook TTabControl TTabSet TTabSheet TToolBar TToolButton TTrackBar TTreeView TUpDown TVtChart The following controls have not tested: TDecisionCube The following controls do not currently print correctly: TControlBar TPageScroller The following 3rd party controls do not currently print correctly: TChartfx TGraph THTML TWebBrowser TF1Book TPdf
How can I improve the reliability of printing from my application?
Most of us have run into applications that fail to print on one printer or another. As a former support engineer, I have certainly had my share of calls that start out with "well it prints from my paint applicaiton"!Why does the TExcellentDemo have more options than TExcellentExample?
The TExcellentDemo uses a hack that we used for the Borland TPrinter unit. Since we are not able to ship the modified version of the TPrinter unit, we could not include it in the TExcellentExample source code.Can I print VCL forms and images to devices contexts other than a printer?
Absolutely! We do recommend that you print to a true memory dc (not a dc based on a DIBSection).How can I reduce the print job size?
We strive to produce the smallest possible print job size that is *reliably* possible. Please remember that TExcellentPrinter products use the full resolution of the printer to produce a quality print job.var JP : TJPEGImage; BM : TBitmap; begin Bm := TBitmap.Create; Bm.LoadFromFile('test.bmp'); Jp := TJPEGImage.Create; Jp.Assign(bm); Jp.CompressionQuality := 100; Jp.SaveToFile('temp.jpg'); Jp.Free; Jp := TJPEGImage.Create; Jp.PixelFormat := jf8Bit; Jp.LoadFromFile('temp.jpg'); bm.PixelFormat := pf8bit; bm.Assign(Jp); bm.SaveToFile('temp.bmp'); Jp.Free; bm.Free; {Load the temp bitmap for use with TExcellentImagePrinter} end;
What's the deal with uneven printer margins?
Most printers are not capable of printing on the entire page area. In this case, there will be a small amount of space between the edge of the paper, and the beginning of the imagable area. We call this unprintable area the "margin". On most printers, there will be a margin on all four sides of the page (left, top, right, and bottom). Its worth noting that on most printers, the margin on one side of the page are not always equal with the margin on the opposite side. In other words, commonly, a printer will have a different size margin for the left and right sides, or the top and bottom side of the page. Usually, this difference is quite small and unnoticable, however, often enough, on some printers, there will be a significant difference that can be very noticeable if you have designed your page output to appear centered on the paper.Can I use different Windows mapping modes?
Yes! While TExcellentImagePrinter and TExcellentFormPrinter were designed to be used in MM_TEXT mode (where 1 unit = 1 pixel), if you use other mapping modes, simply use the Windows LPtoDP() function to convert coordinates in the mapping mode that you are using to device pixels, temporarily switch back to MM_TEXT mode, make your TExcellent call, then switch back to the mapping mode you were previously using.Help! My printouts are still blank, garbled, or only partially print!
TExcellentProducts are designed to overcome just about every problem associated with printing images to a graphics capible device, and the system works well with over 99 percent of the printers on the market. However, there are always exceptions.Help! My Xerox WorkCentre does not print from Delphi and C++Builder!
We have investigated this problem, and have found it to be a printer manufacturer issue. None the less, we have found a work-around.unit PrnPatch; interface uses Windows; var WorkProc : procedure(Prn: HDC; Error: Integer); implementation initialization @WorkProc := nil; finalization end.
implementation uses Consts, PrnPatch; {Patch}
function AbortProc(Prn: HDC; Error: Integer): Bool; stdcall; begin Application.ProcessMessages; {Patch Start} if (@PrnPatch.WorkProc <> nil) then begin PrnPatch.WorkProc(Prn, Error); end; {Patch End} Result := not FPrinter.Aborted; end;
uses Printers, PrnPatch; procedure MyWorkProc(Prn : HDC; Error : integer); begin if (WeWantToSleepForBuggySpoolersAndDrivers) then begin Sleep(1000); end; end; procedure TForm1.Button1Click(Sender: TObject); begin Prnpatch.WorkProc := @MyWorkProc; Printer.BeginDoc; Printer.Canvas.TextOut(100,100,'TEST'); Printer.EndDoc; end;
#include "printers.hpp" #include "PrnPatch.hpp" void __fastcall MyWorkProc(HDC Prn, int Error) { if (WeWantToSleepForBuggySpoolersAndDrivers) { Sleep(1000); } } void __fastcall TForm1::Button1Click(TObject *Sender) { Prnpatch::WorkProc = MyWorkProc; Printer()->BeginDoc(); Printer()->Canvas->TextOut(100,100,"TEST"); Printer()->EndDoc(); }
TExcellent prints lines across the printout!
Simply register the product!Can I print other things on the page in addition to my form?
Yes!How can I print VCL forms without displaying them on the screen?
Temporarily set the left and top properties of the form to Screen.Width and Screen.Height, show the form (off the screen), print it, then hide the form and set the left and top properties back where they were!Can TExcellentFormPrinter print controls instead of the form?
Yes! TExcellentFormPrinter has a clipping rectangle that is set when calling the print function, and it is possible to set the clipping rectangle to print only a selected control. Additionally it is possible to use TExcellentFormPrinter to print the *entire* area of other controls descending from a TScrollingWinControl! This whole operation can be done easily (and transparently to the user). For example, if you wanted to print the entire contents of a grid, you could use a secondary form to hold a copy of the grid, and show the form off the screen while printing the grid! In the case of a Grid type control, you must get the grid (or a copy of the grid) onto the top left hand corner of the secondary form. Make sure that the grid is displaying the starting cell you wish to print from. You then turn off the scroll bars for the grid control, and make the grid as large as all the cells you wish to print, then simply request TExcellentFormPrinter to print the ScrollBox or other form!Help! My Woll2Woll TwwDBRichEdit does not print!
The Woll2Woll "TwwDBRichEdit" (and probably other descendants of the "TwwCustomRichEdit") control do not correctly print with TExcellentFormPrinter when used with embedded OLE objects.function TForm1.OnPaintControlCallbackEvent(TheControl : TControl; dc : HDC) : BOOL; var Range : TFormatRange; lpx : integer; lpy : integer; LastChar : integer; MaxLen : integer; SaveIndex : integer; begin if (HasClass(TheControl, 'TwwDBRichEdit')) then begin {Save the state of the device context} SaveIndex := SaveDc(Dc); {Zero out the Range variable} FillChar(Range, sizeof(Range), 0); {Set the dc members} Range.hdc := dc; Range.hdcTarget := dc; {Get the DPI of the device context} lpx := GetDeviceCaps(dc, LOGPIXELSX); lpy := GetDeviceCaps(dc, LOGPIXELSY); {Lets fix up a rectangle to print to. The rectangle needs to be in TWIPS.} {Some folks may perfer to use the Controls ClientRect and offset the rectangle {to get a more accurate rendering and account for possibly drawing a border} Range.rc.Left := 0; Range.rc.Top := 0; Range.rc.Right := TheControl.Width * 1440 div lpx; Range.rc.Bottom := TheControl.Height * 1440 div lpy; Range.rcPage := Range.rc; {We will loop through and print the RichEdit in bands using the EM_FORMATRANGE message} LastChar := 0; MaxLen := TRichEdit(TheControl).GetTextLen; Range.chrg.cpMax := -1; repeat Range.chrg.cpMin := LastChar; LastChar := SendMessage(TWinControl(TheControl).Handle, EM_FORMATRANGE, 1, DWORD(@Range)); until (LastChar >= MaxLen) or (LastChar = -1); {Windows docs say we must do this when done to clear some internal RichEdit buffers!} SendMessage(TWinControl(TheControl).Handle, EM_FORMATRANGE, 0, 0); {Restore the state of the device context} RestoreDc(Dc, SaveIndex); {Let TExcellentFormPrinter know we handled the printing of this control!} result := TRUE; {We are done!} exit; end; {If we got here, then we should let TExcellentFromPrinter Handle printing the control} result := FALSE; end;
Help! My SpeedButton does not print in the down position!
We have seen this on some video cards in 15, 16, or 32 bit color modes. We handle this internally if the parent contol of the SpeedButton is the same as the control that you are printing. If the parent is another embedded control, the only remedy is to use the following PrintableSpeedButton component, and set the IsPrinting property to true when you are printing:unit PrintableSpeedButton; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Buttons; type TPrintableSpeedButton = class(TSpeedButton) private { Private declarations } FIsPrinting: Boolean; protected { Protected declarations } procedure Paint; override; public { Public declarations } property IsPrinting: Boolean read FIsPrinting write FIsPrinting; published { Published declarations } end; procedure Register; implementation procedure Register; begin RegisterComponents('Samples', [TPrintableSpeedButton]); end; procedure TPrintableSpeedButton.Paint; var ScreenDc : HDC; MemDc : HDC; MemBitmap : hBitmap; OldMemBitmap : hbitmap; OldCanvasHandle : THandle; ColorDepth : integer; begin Inherited Paint; if (NOT FIsPrinting) then begin exit; end; ScreenDc := GetDc(0); ColorDepth := (GetDeviceCaps(ScreenDc, BITSPIXEL) * GetDeviceCaps(ScreenDc, PLANES)); if ((ColorDepth <> 15) AND (ColorDepth <> 16) AND (ColorDepth <> 32)) then begin ReleaseDc(0, ScreenDc); exit; end; //Paint it again using a memory dc for transfer! MemDc := CreateCompatibleDc(ScreenDc); MemBitmap := CreateCompatibleBitmap(ScreenDc, Width, Height); ReleaseDc(0, ScreenDc); OldMemBitmap := SelectObject(MemDc, MemBitmap); PatBlt(MemDc, 0, 0, Width, Height, WHITENESS); OldCanvasHandle := Canvas.Handle; Canvas.Handle := MemDc; Inherited Paint; Canvas.Handle := OldCanvasHandle; BitBlt(Canvas.Handle, 0, 0, Width, Height, MemDc, 0, 0, SRCCOPY); SelectObject(MemDc, OldMemBitmap); DeleteObject(MemBitmap); DeleteDc(MemDc); exit; end; end.
Help! My Async Terminal does not print!
The Async Terminal does not correctly print with TExcellentFormPrinter.function TForm1.OnPaintControlCallbackEvent(TheControl : TControl; dc : HDC) : BOOL; var m : TMessage; ScreenDc : HDC; MemDc : HDC; MemBitmap : hBitmap; OldMemBitmap : hbitmap; begin if (HasClass(TheControl, 'TAdTerminal')) then begin ScreenDc := GetDc(0); MemDc := CreateCompatibleDc(ScreenDc); MemBitmap := CreateCompatibleBitmap(ScreenDc, TheControl.Width, TheControl.Height); ReleaseDc(0, ScreenDc); OldMemBitmap :=SelectObject(MemDc, MemBitmap); PatBlt(MemDc, 0, 0, TheControl.Width, TheControl.Height, WHITENESS); m.Msg := WM_ERASEBKGND; m.WParam := MemDc; m.LParam := 0; m.Result := 0; TheControl.Dispatch(m); m.Msg := WM_PRINT; m.WParam := MemDc; m.LParam := PRF_NONCLIENT OR PRF_OWNED; m.Result := 0; TheControl.Dispatch(m); m.Msg := WM_PAINT; m.WParam := MemDc; m.LParam := 0; m.Result := 0; TheControl.Dispatch(m); BitBlt(Dc, 0, 0, TheControl.Width, TheControl.Height, MemDc, 0, 0, SRCCOPY); SelectObject(MemDc, OldMemBitmap); DeleteObject(MemBitmap); DeleteDc(MemDc); result := TRUE; exit; end; result := FALSE; end;
What happened to the debug variables in TExcellentProducts?
Many of our CBuilder users have asked that we make it easier to set these variables, so we have added functions to set the variables with a simple function call. Additional information on the use of these functions can be found in the online product documentation for the TExcellentImagePrinter and TExcellentFormPrinter products.What happened with the short circuit boolean evaluation problem?
In earlier Delphi versions of the TExcellent product line (version 2.0 and below), turning off short circuit boolean evaluation would cause access violations unless you manually added the {$B-} compiler directive to the units header.Help! I want to scale my bitmap to fit...!
We recommend you take a look at the documentation for the PrnUtils unit where there are a number of scaling functions to assist you in scaling any sort of output to any size you need.procedure TForm1.Button1Click(Sender: TObject); var BitmapInfo : PBITMAPINFO; {pointer to a BitmapInfo structure} Bits : pointer; {pointer to the bits} BitmapWidth : integer; {bitmap width} BitmapHeight : integer; {bitmap height} ReturnValue : integer; {success or error code} PagesWide : integer; {How many pages wide you want.} PagesHigh : integer; {How many pages high you want.} CenterImageOnPage : BOOL; {Center on the page or print at the top left corner} AbortDialog : TAbortDialog; {The Abort Dialog} AppCallbackData : TAppCallbackData; {Our callback data structure} PrnPageInfo : TPrnPageInfo; {Info about the page to be printed} TotalImageWidth : integer; {Total width of the printout (including multiple pages)} TotalImageHeight : integer; {Total height of the printout (including multiple pages)} PrintedImageWidth : integer; {Total printed width of the image (including multiple pages)} PrintedImageHeight : integer; {Total printed height of the umage (including multiple pages)} PrintedImageOffset : TPoint; {How much to offset the image to print it centered on the page(s)} i : integer; {Loop variable (for i := 1 to PagesHigh do)} j : integer; {Loop variable (for j := 1 to PagesWide do)} SaveIndex : integer; {Used to save the state of the printer} APageCaption : string; {Used to print a caption on the page and update the abort dialog} PageRect : TRect; {The clipping rectangle for the page} ScaleInfo : TScaleInfo; {Used to calculate scaling} begin {Center the output on the page? (FALSE prints the image at the top left of the page).} {Note that on large printouts - some pages may be blank if you choose to center} {the printout, as it will truely center the image, and some pages may not contain} {any part of the image} CenterImageOnPage := TRUE; {Note: we are done with configurable print choices!} {Are we printing something right now?} if (Printer.Printing) then begin ShowMessage('Already printing... Please try again later!'); exit; end; {Load a bitmap} if (NOT LoadDIBFromFile('test.bmp', pointer(BitmapInfo), Bits, BitmapWidth, BitmapHeight)) then begin ShowMessage('Bitmap load error'); exit; end; {disable the window so the user cannot click on anything while we are printing.} EnableWindow(self.Handle, FALSE); {try to fire up the printjob!} try Printer.BeginDoc; except {error! clean up and bail out!} EnableWindow(self.Handle, TRUE); ShowMessage('Printer corrupt or not installed!'); FreeMemEx(BitmapInfo); FreeMemEx(Bits); exit; end; {create our abort dialog} AbortDialog := CreateAbortDialog(Application.Handle, self); if (AbortDialog = nil) then begin {error! clean up and bail out!} Printer.Abort; EnableWindow(self.Handle, TRUE); ShowMessage('Abort dialog could not be created!'); FreeMemEx(BitmapInfo); FreeMemEx(Bits); exit; end; {Set our callback information} AppCallbackData.AbortDialog := AbortDialog; {get the page information} GetPrnPageInfo(Printer.Canvas.Handle, @PrnPageInfo); {do some calculations to stretch and position} {the image to fit the page (or pages).} PageRect.Left := 0; PageRect.Top := 0; PageRect.Right := PrnPageInfo.AdjustedPageArea.x; PageRect.Bottom := PrnPageInfo.AdjustedPageArea.y; { You can use the following code if you simply } { want to scale to some amount of inches (6 inches is used as an example) } { and have the number of pages high and wide calculated for you. } { -------------------------------------------------------------- } { TotalImageWidth := 6 * PrnPageInfo.DPI.x; TotalImageHeight := 6 * PrnPageInfo.DPI.y; PagesWide := TotalImageWidth div PrnPageInfo.AdjustedPageArea.x; if ((TotalImageWidth mod PrnPageInfo.AdjustedPageArea.x) <> 0) then begin inc(PagesWide); end; PagesHigh := TotalImageHeight div PrnPageInfo.AdjustedPageArea.y; if ((TotalImageHeight mod PrnPageInfo.AdjustedPageArea.y) <> 0) then begin inc(PagesHigh); end; } { You can use the following code if you simply } { want to scale to some amount of pixels } { and have the number of pages high and wide calculated for you. } { -------------------------------------------------------------- } { TotalImageWidth := Set_this_to_the_number_of_pixels_wide_you_wish; TotalImageHeight := Set_this_to_the_number_of_pixels_high_you_wish; PagesWide := TotalImageWidth div PrnPageInfo.AdjustedPageArea.x; if ((TotalImageWidth mod PrnPageInfo.AdjustedPageArea.x) <> 0) then begin inc(PagesWide); end; PagesHigh := TotalImageHeight div PrnPageInfo.AdjustedPageArea.y; if ((TotalImageHeight mod PrnPageInfo.AdjustedPageArea.y) <> 0) then begin inc(PagesHigh); end; } { You can use the following code if you simply } { want to scale the image width to fit a single } { page, and let the height freeflow to the number } { of pages needed : } { -------------------------------------------------------------- } { ScaleInfo.OriginalSize_X := BitmapWidth; ScaleInfo.OriginalSize_Y := BitmapHeight; ScaleInfo.ScaledSize_X := PrnPageInfo.AdjustedPageArea.x; ScaleToFitX(@ScaleInfo); PagesWide := 1; TotalImageWidth := Trunc(ScaleInfo.ScaledSize_X); TotalImageHeight := Trunc(ScaleInfo.ScaledSize_Y); PagesHigh := TotalImageHeight div PrnPageInfo.AdjustedPageArea.y; if ((TotalImageHeight mod PrnPageInfo.AdjustedPageArea.y) <> 0) then begin inc(PagesHigh); end; } { You can use the following code if you simply } { want to scale the image height to fit a single } { page, and let the width freeflow to the number } { of pages needed : } { ScaleInfo.OriginalSize_X := BitmapWidth; ScaleInfo.OriginalSize_Y := BitmapHeight; ScaleInfo.ScaledSize_Y := PrnPageInfo.AdjustedPageArea.y; ScaleToFitY(@ScaleInfo); PagesHigh := 1; TotalImageWidth := Trunc(ScaleInfo.ScaledSize_X); TotalImageHeight := Trunc(ScaleInfo.ScaledSize_Y); PagesWide := TotalImageWidth div PrnPageInfo.AdjustedPageArea.x; if ((TotalImageWidth mod PrnPageInfo.AdjustedPageArea.x) <> 0) then begin inc(PagesWide); end; } PrintedImageWidth := TotalImageWidth; PrintedImageHeight := TotalImageHeight; if (NOT CenterImageOnPage) then begin PrintedImageOffset.x := 0; PrintedImageOffset.y := 0; end else begin PrintedImageOffset.x := ((PrnPageInfo.AdjustedPageArea.x * PagesWide) div 2) - (TotalImageWidth div 2); PrintedImageOffset.y := ((PrnPageInfo.AdjustedPageArea.y * PagesHigh) div 2) - (TotalImageHeight div 2); end; {loop through and print the pages!} for i := 1 to PagesHigh do begin for j := 1 to PagesWide do begin {set our callback information} AppCallbackData.PageAcross := j; AppCallbackData.PageDown := i; {construct a caption for the abort dialog (we will} {also print this at the top of each printed page).} APageCaption := 'Printing Page ' + IntToStr(j) + ':' + IntToStr(i) + ' - 0%'; {set the abort dialog's caption to give the user a status update} AbortDialogSetCaption(AbortDialog, pChar(APageCaption)); {test to see if the user canceled the print job} if (AbortDialogUserHasCanceled(AbortDialog)) then begin {user canceled the print job - clean up and bail out!} Printer.Abort; FreeAbortDialog(AbortDialog); EnableWindow(self.Handle, TRUE); FreeMemEx(BitmapInfo); FreeMemEx(Bits); ShowMessage('Printing Aborted!'); exit; end; {save the printer state} SaveIndex := SaveDc(Printer.Canvas.Handle); {move our origin to account for a perfect printing margin} MoveWindowOrg(Printer.Canvas.Handle, PrnPageInfo.AdjustedMarginOffset.x, PrnPageInfo.AdjustedMarginOffset.y); {allow drawing only within our prefect margins} IntersectClipRect(Printer.Canvas.Handle, 0, 0, PrnPageInfo.AdjustedPageArea.x, PrnPageInfo.AdjustedPageArea.y); {Print the image! We will need to "back up" the offset of where we print} {the image to account for printing across multiple pages. No need to fear,} {TExcellentImagePrinter will allow us to print up to 2 billion pixels high} {and wide, even under Windows 95 and 98!} ReturnValue := PrintDIBitmapEx(Printer.Canvas.Handle, -((j - 1) * PrnPageInfo.AdjustedPageArea.x) + PrintedImageOffset.x, -((i - 1) * PrnPageInfo.AdjustedPageArea.y) + PrintedImageOffset.y, PrintedImageWidth, PrintedImageHeight, 0, 0, BitmapInfo^.bmiHeader.biWidth, BitmapInfo^.bmiHeader.biHeight, BitmapInfo, Bits, 0, TRUE, FALSE, PageRect, @AppCallbackFn, DWORD(@AppCallbackData)); if (ReturnValue < NOTHING_TO_PRINT) then begin {This will happen if the user has canceled the print job} {or an error occured. We will not trap the NOTHING_TO_PRINT} {error, as when the image is stretched across several pages, it} {is possible that a few pages many not contain part of the image} {due to page and image centering, and TExcellentImage printer will} {report there is nothing to print for those pages. If we have another} {kind of error then we will clean up and bail out!} RestoreDc(Printer.Canvas.Handle, SaveIndex); FreeAbortDialog(AbortDialog); Printer.Abort; FreeMemEx(BitmapInfo); FreeMemEx(Bits); EnableWindow(self.Handle, TRUE); case (ReturnValue) of BAD_PARAMETER : begin ShowMessage('BAD_PARAMETER'); end; MEMORY_ALLOC_FAILED : begin ShowMessage('MEMORY_ALLOC_FAILED'); end; MEMORY_READ_FAILED : begin ShowMessage('MEMORY_READ_FAILED'); end; MEMORY_WRITE_FAILED : begin ShowMessage('MEMORY_WRITE_FAILED'); end; USER_ABORT_OR_OTHER_ERROR : begin ShowMessage('USER_ABORT_OR_OTHER_ERROR!'); end; end; exit; end; {we can print some lovely page captions here (or anything else we care to print)!} Printer.Canvas.Font.Name := 'Arial'; {fix a Delphi issue that pops up sometimes with font scaling!} Printer.Canvas.Font.PixelsPerInch := PrnPageInfo.DPI.y; Printer.Canvas.Font.Size := 10; {create a page caption to print} APageCaption := 'Printing Page ' + IntToStr(j) + ' : ' + IntToStr(i); Printer.Canvas.TextOut(0, 0, APageCaption); {we are done printing on this page, restore the printer state!} RestoreDc(Printer.Canvas.Handle, SaveIndex); {if not the last page, we need to call NewPage!} if (NOT ((j = PagesWide) AND (i = PagesHigh))) then begin Printer.NewPage; end; end; {for j := 1 to PagesWide} end; {for i := 1 to PagesHigh} {We are done! Time to clean up!} FreeMemEx(BitmapInfo); FreeMemEx(Bits); FreeAbortDialog(AbortDialog); Printer.EndDoc; EnableWindow(self.Handle, TRUE); ShowMessage('Printing Completed!'); end;
Help! I want to scale my form to fit...!
We recommend you take a look at the documentation for the PrnUtils unit where there are a number of scaling functions to assist you in scaling any sort of output to any size you need.procedure TForm1.Button1Click(Sender: TObject); var PagesWide : integer; {How many pages wide you want.} PagesHigh : integer; {How many pages high you want.} CenterFormOnPage : BOOL; {Center on the page or print at the top left corner} AbortDialog : TAbortDialog; {The Abort Dialog} AppCallbackData : TAppCallbackData; {Our callback data structure} PrnPageInfo : TPrnPageInfo; {Info about the page to be printed} VirtualFormSize : TSize; {Size of the form (including non visible scrolled areas} TotalImageWidth : integer; {Total width of the printout (including multiple pages)} TotalImageHeight : integer; {Total height of the printout (including multiple pages)} PrintedImageOffset : TPoint; {How much to offset the form to print it centered on the page(s)} i : integer; {Loop variable (for i := 1 to PagesHigh do)} j : integer; {Loop variable (for j := 1 to PagesWide do)} SaveIndex : integer; {Used to save the state of the printer} AnAbortCaption : string; {Used to update the abort dialog} PageRect : TRect; {The clipping rectangle for the page} PrintFormData : TPrintFormData; {Additional parameters} ScaleInfo : TScaleInfo; {Used to calculate scaling} begin {center the output on the page? (FALSE prints the form at the top left of the page).} CenterFormOnPage := TRUE; FillChar(PrintFormData, sizeof(PrintFormData), 0); PrintFormData.StrucSize := sizeof(PrintFormData); {Note: We are done with configurable print choices!} {disable the window so the user cannot click on anything while we are printing.} EnableWindow(self.Handle, FALSE); {try to fire up the printjob!} try Printer.BeginDoc; except {error! clean up and bail out!} EnableWindow(self.Handle, TRUE); ShowMessage('Printer corrupt or not installed!'); exit; end; {create our abort dialog} AbortDialog := CreateAbortDialog(Application.Handle, self); {Set our static callback information} AppCallbackData.AbortDialog := AbortDialog; {get the page information} GetPrnPageInfo(Printer.Handle, @PrnPageInfo); VirtualFormSize := VirtualClientSize(self); {do some calculations to stretch and position} {the image to fit the page (or pages).} PageRect.Left := 0; PageRect.Top := 0; PageRect.Right := PrnPageInfo.AdjustedPageArea.x; PageRect.Bottom := PrnPageInfo.AdjustedPageArea.y; { You can use the following code if you simply } { want to scale to some amount of inches (6 inches is used as an example) } { and have the number of pages high and wide calculated for you. } { -------------------------------------------------------------- } { TotalImageWidth := 16 * PrnPageInfo.DPI.x; TotalImageHeight := 16 * PrnPageInfo.DPI.y; PagesWide := TotalImageWidth div PrnPageInfo.AdjustedPageArea.x; if ((TotalImageWidth mod PrnPageInfo.AdjustedPageArea.x) <> 0) then begin inc(PagesWide); end; PagesHigh := TotalImageHeight div PrnPageInfo.AdjustedPageArea.y; if ((TotalImageHeight mod PrnPageInfo.AdjustedPageArea.y) <> 0) then begin inc(PagesHigh); end; } { You can use the following code if you simply } { want to scale to some amount of pixels } { and have the number of pages high and wide calculated for you. } { -------------------------------------------------------------- } { TotalImageWidth := Set_this_to_the_number_of_pixels_wide_you_wish; TotalImageHeight := Set_this_to_the_number_of_pixels_high_you_wish; PagesWide := TotalImageWidth div PrnPageInfo.AdjustedPageArea.x; if ((TotalImageWidth mod PrnPageInfo.AdjustedPageArea.x) <> 0) then begin inc(PagesWide); end; PagesHigh := TotalImageHeight div PrnPageInfo.AdjustedPageArea.y; if ((TotalImageHeight mod PrnPageInfo.AdjustedPageArea.y) <> 0) then begin inc(PagesHigh); end; } { You can use the following code if you simply } { want to scale the form width to fit a single } { page, and let the height freeflow to the number } { of pages needed : } { -------------------------------------------------------------- } { ScaleInfo.OriginalSize_X := VirtualFormSize.cx; ScaleInfo.OriginalSize_Y := VirtualFormSize.cy; ScaleInfo.ScaledSize_X := PrnPageInfo.AdjustedPageArea.x; ScaleToFitX(@ScaleInfo); PagesWide := 1; TotalImageWidth := Trunc(ScaleInfo.ScaledSize_X); TotalImageHeight := Trunc(ScaleInfo.ScaledSize_Y); PagesHigh := TotalImageHeight div PrnPageInfo.AdjustedPageArea.y; if ((TotalImageHeight mod PrnPageInfo.AdjustedPageArea.y) <> 0) then begin inc(PagesHigh); end; } { You can use the following code if you simply } { want to scale the form height to fit a single } { page, and let the width freeflow to the number } { of pages needed : } { ScaleInfo.OriginalSize_X := VirtualFormSize.cx; ScaleInfo.OriginalSize_Y := VirtualFormSize.cy; ScaleInfo.ScaledSize_Y := PrnPageInfo.AdjustedPageArea.y; ScaleToFitY(@ScaleInfo); PagesHigh := 1; TotalImageWidth := Trunc(ScaleInfo.ScaledSize_X); TotalImageHeight := Trunc(ScaleInfo.ScaledSize_Y); PagesWide := TotalImageWidth div PrnPageInfo.AdjustedPageArea.x; if ((TotalImageWidth mod PrnPageInfo.AdjustedPageArea.x) <> 0) then begin inc(PagesWide); end; } if (NOT CenterFormOnPage) then begin PrintedImageOffset.x := 0; PrintedImageOffset.y := 0; end else begin PrintedImageOffset.x := ((PrnPageInfo.AdjustedPageArea.x * PagesWide) div 2) - (TotalImageWidth div 2); PrintedImageOffset.y := ((PrnPageInfo.AdjustedPageArea.y * PagesHigh) div 2) - (TotalImageHeight div 2); end; {loop through and print the pages!} for i := 1 to PagesHigh do begin for j := 1 to PagesWide do begin {set our dynamic callback information} AppCallbackData.PageAcross := j; AppCallbackData.PageDown := i; {set the abort dialog's caption to give the user a status update} AnAbortCaption := 'Printing Page ' + IntToStr(j) + ':' + IntToStr(i) + ' - 0%'; AbortDialogSetCaption(AbortDialog, pChar(AnAbortCaption)); {test to see if the user canceled the print job} if (AbortDialogUserHasCanceled(AbortDialog)) then begin {user canceled the print job - clean up and bail out!} Printer.Abort; FreeAbortDialog(AbortDialog); EnableWindow(self.Handle, TRUE); ShowMessage('Printing Aborted!'); exit; end; {save the printer state} SaveIndex := SaveDc(Printer.Canvas.Handle); {move our origin to account for a perfect printing margin} MoveWindowOrg(Printer.Canvas.Handle, PrnPageInfo.AdjustedMarginOffset.x, PrnPageInfo.AdjustedMarginOffset.y); {allow drawing only within our prefect margins} IntersectClipRect(Printer.Canvas.Handle, 0, 0, PrnPageInfo.AdjustedPageArea.x, PrnPageInfo.AdjustedPageArea.y); {Print the form! We will need to "back up" the offset of where we print} {the form to account for printing across multiple pages. No need to fear,} {TExcellentPrinter will allow us to print up to 2 billion pixels high} {and wide, even under Windows 95 and 98!} if (NOT PrintTScrollingWinControlEx(self, Printer.Canvas.Handle, -((j - 1) * PrnPageInfo.AdjustedPageArea.x) + PrintedImageOffset.x, -((i - 1) * PrnPageInfo.AdjustedPageArea.y) + PrintedImageOffset.y, TotalImageWidth, TOtalImageHeight, PageRect, AppPrintingCallbackFn, DWORD(@AppCallbackData), OnPaintCallbackEvent, OnPaintControlCallbackEvent, @PrintFormData)) then begin {this will happen if the user has canceled the print job} {or an error occured. Clean up and bail out!} RestoreDc(Printer.Canvas.Handle, SaveIndex); FreeAbortDialog(AbortDialog); Printer.Abort; EnableWindow(self.Handle, TRUE); ShowMessage('Printing Aborted!'); exit; end; {we can print a lovely page caption here!} Printer.Canvas.Font.Name := 'Arial'; {fix a VCL issue that pops up sometimes with font scaling!} Printer.Canvas.Font.PixelsPerInch := PrnPageInfo.DPI.y; Printer.Canvas.Font.Size := 10; Printer.Canvas.TextOut(0, 0, 'Printing Page ' + IntToStr(j) + ':' + IntToStr(i)); {we are done printing on this page, restore the printer state!} RestoreDc(Printer.Canvas.Handle, SaveIndex); {if not the last page, we need to call NewPage!} if (NOT ((j = PagesWide) AND (i = PagesHigh))) then begin Printer.NewPage; end; end; end; {We are done! Time to clean up!} FreeAbortDialog(AbortDialog); Printer.EndDoc; EnableWindow(self.Handle, TRUE); ShowMessage('Printing Completed!'); end;