unit GenericPrint; interface uses classes, printers, windows, graphics, SysUtils, Dialogs, db, dbTables; procedure PrintDataSet( qryData:TDataSet; sPrintHeader: string); procedure PrintQuery( qryData:TQuery; sPrintHeader: string); procedure PrintTable( tblData:TTable; sPrintHeader: string); implementation uses DateFunctions, Progress, GenFns; var PixelsInInchx: Integer; LineHeight: Integer; // Keeps track of vertical space in pixels, printed on a page. AmountPrinted: Integer; pageno : Integer; // Number of pixels in 1/10 of an inch. This is used for line spacing. TenthsOfInchPixelsY: Integer; nLenServiceID, nLenReason : Integer; nLenRefno, nLenCustno, nLenDate, nLenAmount, nLenInteger : Integer; nLenLMargin, nLenRMargin, nLenTMargin, nLenBMargin : Integer; // totalrent, totalcall, totalother, totalall : Currency; procedure PrintLine(Items: TStringList); // This procedure prints a line. The parameter contains the data and the cell // width for each data item. If the width is negative this is interpreted // as requiring the data to be written with right-justify in the data cell. var OutRect: TRect; Inches: double; i: integer; RJustify: Boolean; // Indicates text Right justified. nPixelsDiff: LongInt; begin // First position the print rect on the print canvas OutRect.Left := 0; OutRect.Top := AmountPrinted; OutRect.Bottom := OutRect.Top + LineHeight; With Printer.Canvas do begin for i := 0 to Items.Count - 1 do begin Inches := longint(Items.Objects[i]) * 0.1; RJustify := (Inches < 0.00); // Right justify in column if true. Inches := Abs(Inches); // Size of only. // Determine Right edge OutRect.Right := OutRect.Left + round(PixelsInInchx*Inches); if RJustify then begin // Must recalculate the value of OutRect.Left. nPixelsDiff := (OutRect.Right-OutRect.Left) - Printer.Canvas.TextWidth(Items[i]); if nPixelsDiff > 0 then begin // Text prints in less than the column width. OutRect.Left := OutRect.Left + nPixelsDiff; end; // if nPixelsDiff > 0 then. end; // if RJustify then. if not Printer.Aborted then begin // Print the line. TextRect(OutRect, OutRect.Left, OutRect.Top, Items[i]); end; // if not Printer.Aborted then. // Adjust right edge, with 20 pixel separation between columns. OutRect.Left := OutRect.Right + 20; end; // for i := 0 to Items.Count - 1 do. end; // With Printer.Canvas do. // As each line prints, AmountPrinted must increase to reflect how // much of a page has been printed on based on the line height. AmountPrinted := AmountPrinted + TenthsOfInchPixelsY*2; end; // procedure PrintLine(). procedure PrintHeader(sHeader : string); var sPageno: String; // The page number being printed. sDate: String; SaveFont: TFont; FontSize : Integer; nLeftMargin : Integer; nRightMargin: Integer; begin // Define the Header to be printed. sPageno := 'Page ' + IntToStr(pageno); sDate := 'Date: '+ FormattedDateString(Date); // Save the current printer's font, then set a new print font based // on the selection for Edit1. SaveFont := TFont.Create; try Savefont.Assign(Printer.Canvas.Font); FontSize := Printer.Canvas.Font.Size; if FontSize < 0 then begin FontSize := FontSize - 2; end else begin FontSize := FontSize + 2; end; Printer.Canvas.Font.Size := FontSize; Printer.Canvas.Font.Style := [fsBold]; // Now set the specified margins. nLeftMargin := round(PixelsInInchx*nLenLMargin*0.1) + 2; nRightMargin := round(PixelsInInchx*nLenRMargin*0.1) + 2; // First print out the Header with Printer do begin if not Printer.Aborted then begin // Increment AmountPrinted by the Top Margin. AmountPrinted := AmountPrinted + nLenTMargin*TenthsOfInchPixelsY; // Page of this print output, at Left Margin. Canvas.TextOut(nLeftMargin, AmountPrinted, sPageno); // Title of this print output. Canvas.TextOut(nLeftMargin+((PageWidth-nLeftMargin-nRightMargin) div 2) -(Canvas.TextWidth(sHeader) div 2), AmountPrinted, sHeader); // Date of this print output. Canvas.TextOut(PageWidth-nRightMargin-Canvas.TextWidth(sDate), AmountPrinted, sDate); end; // Increment AmountPrinted by the LineHeight AmountPrinted := AmountPrinted + LineHeight + TenthsOfInchPixelsY; end; // Restore the old font to the Printer's Canvas property Printer.Canvas.Font.Assign(SaveFont); finally SaveFont.Free; end; end; // procedure PrintHeader(). procedure PrintFooter( acurTotals : Array of Currency; aColumnWidth : Array of Integer ); var TotalFooter: TStringList; nColumn : Integer; sAmount: String; begin // Create a TStringList to hold the positions where the width // of each column is based on values in the aColumnWidth array. TotalFooter := TStringList.Create; try // Print the footer using a bold/underline style. Printer.Canvas.Font.Style := [fsBold, fsUnderline]; with TotalFooter do begin // Store the column headers and widths in the TStringList object. AddObject('', pointer(nLenLMargin)); for nColumn := Low(acurTotals) to High(acurTotals) do begin if acurTotals[nColumn] = 0.00 then begin AddObject(' ', pointer(aColumnWidth[nColumn])); end else begin sAmount := FormatCurr('#,###,###.00', acurTotals[nColumn]); AddObject( sAmount, pointer(aColumnWidth[nColumn])); end; end; end; PrintLine(TotalFooter); Printer.Canvas.Font.Style := []; finally TotalFooter.free; end; end; // procedure PrintFooter(). procedure PrintColumnNames( ColumnHeaders : TStringList; aColumnWidth : Array of Integer ); var ColNames: TStringList; nColumn : Integer; begin // Create a TStringList to hold the column names and the // positions where the width of each column is based on values // in the TEdit controls. ColNames := TStringList.Create; try // Print the column headers using a bold/underline style Printer.Canvas.Font.Style := [fsBold, fsUnderline]; with ColNames do begin // Store the column headers and widths in the TStringList object AddObject('', pointer(nLenLMargin)); for nColumn := Low(aColumnWidth) to High(aColumnWidth) do begin AddObject(ColumnHeaders.Strings[nColumn], pointer(aColumnWidth[nColumn])); end; end; PrintLine(ColNames); Printer.Canvas.Font.Style := []; finally ColNames.Free; // Free the column name TStringList instance end; end; // procedure PrintColumnNames(). // ***************************************************************** // PRINT THE DATASET DATA PASSED // ***************************************************************** procedure PrintDataSet( qryData:TDataSet; sPrintHeader: string); var // First 3 - printer access variables used for printer setup. ADevice, ADriver, Aport : array [0..255] of char; DeviceHandle : THandle; DevMode : PDeviceMode; // A pointer to a TDeviceMode structure. Progress : TProgressForm; nCount : Integer; Factor : Real; iFieldNum : Integer; sFieldValue : string; nFieldWidth : Integer; nFieldDataType : TFieldType; Items: TStringList; slColumnNames : TStringList; aFieldWidth : array of Integer; nAmount : Currency; acurTotals : Array of Currency; nLengthOfString : Integer; begin // The table required for printing must already be open. nLenLMargin := 2; // 2/10 inches. Left margin. nLenRMargin := 2; // 2/10 inches. Right margin. nLenTMargin := 6; // 6/10 inches. Top margin. nLenBMargin := 6; // 6/10 inches. Bottom margin. nLenRefno := 3; // 3/10 inches. nLenCustno := 15; // 15/10 inches. nLenDate := 7; // 7/10 inches. nLenAmount := -7; // 7/10 inches, right justified. pageno := 0; SetLength( aCurTotals, qryData.FieldCount); // Get the column names from the fields in the query. slColumnNames := TStringList.Create; slColumnNames.Clear; for iFieldNum := 0 to (qryData.FieldCount-1) do begin slColumnNames.Add(qryData.Fields[iFieldNum].DisplayLabel); end; // Determine the width of each field. for iFieldNum := 0 to (qryData.FieldCount-1) do begin nFieldDataType := qryData.Fields[iFieldNum].DataType; if (nFieldDataType in [ftString,ftFixedChar,ftWideString]) then begin // A string value held. nLengthOfString := qryData.Fields[iFieldNum].DisplayWidth; if nLengthOfString > 10 then nFieldWidth := nLenCustno else if nLengthOfString > 5 then nFieldWidth := nLenDate else nFieldWidth := nLenRefno; end else if (nFieldDataType in [ftDate,ftDateTime]) then // Convert for date value only. nFieldWidth := nLenDate else if (nFieldDataType = ftCurrency) then // Right justify. nFieldWidth := nLenAmount else if (nFieldDataType in [ftInteger,ftWord,ftSmallInt,ftLargeInt]) then // Right justify. nFieldWidth := nLenAmount else if (nFieldDataType = ftBoolean) then // Logical value to be shown. nFieldWidth := nLenRefno else if (nFieldDataType = ftFloat) then // Right justify. nFieldWidth := nLenAmount else // Do not print this data field value. nFieldWidth := 0; SetLength( aFieldWidth, Length(aFieldWidth) + 1); aFieldWidth[iFieldNum] := nFieldWidth; aCurTotals[iFieldNum] := 0.0000; end; // Open the progress form. Progress := TProgressForm.Create(NIL); // Display a message to the user. Progress.Msg.Caption := 'Printing data ...'; Progress.Show; // Determine the parameters for the progress bar. nCount := qryData.RecordCount; if (nCount = 0) then begin // Avoid div by zero. Factor := 100.0; end else begin Factor := 100.0 / nCount; end; nCount := 0; // Now get the printer ready. // Create a TStringList instance to hold the fields and the widths // of the columns in which they'll be drawn based on the entries in // the edit controls. Items := TStringList.Create; // Determine pixels per inch horizontally PixelsInInchx := GetDeviceCaps(Printer.Handle, LOGPIXELSX); TenthsOfInchPixelsY := GetDeviceCaps(Printer.Handle, LOGPIXELSY) div 10; AmountPrinted := 0; try // Set the printer up the way I want it. // First obtain a handle to the TPrinter's Devicemode structure. Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle); // If the DeviceHandle is still 0, then the driver was not loaded. Set // the printer index to force the printer driver to load making the // handle available. if DeviceHandle = 0 then begin Printer.PrinterIndex := Printer.PrinterIndex; Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle); end; // If the DeviceHandle is still 0, then an error has occurred. Otherwise // use GlobalLock() to get the pointer to the TDeviceMode structure. if (DeviceHandle = 0) then Raise Exception.Create('Could Not Initialize Printer TDeviceMode' + ' Structure.') else DevMode := GlobalLock(Devicehandle); // NOW I can set up the printer!! with DevMode^ do begin // Set landscape mode. dmFields := dmFields or DM_ORIENTATION; dmOrientation := DMORIENT_LANDSCAPE; end; // Okay, all done with this setup. if ( DeviceHandle <> 0 ) then begin GlobalLock(Devicehandle); end; Printer.BeginDoc; // Calculate the line height based on text height using the // currently rendered font. LineHeight := Printer.Canvas.TextHeight('X') + TenthsOfInchPixelsY; // Start the printing. Inc(pageno); PrintHeader( sPrintHeader ); PrintColumnNames( slColumnNames, aFieldWidth ); qryData.First; while not qryData.Eof do begin // Add the left margin. with Items do begin AddObject('', pointer(nLenLMargin)); end; // Add each field in the query. for iFieldNum := 0 to (qryData.FieldCount-1) do begin nFieldDataType := qryData.Fields[iFieldNum].DataType; if (nFieldDataType in [ftString,ftFixedChar,ftWideString]) then begin // A string value held. sFieldValue := qryData.Fields[iFieldNum].AsString; end else if (nFieldDataType in [ftDate,ftDateTime]) then begin // Convert for date value only. sFieldValue := FormattedDateString( qryData.Fields[iFieldNum].AsDateTime); end else if (nFieldDataType = ftCurrency) then begin // Right justify. nAmount := qryData.Fields[iFieldNum].AsCurrency; sFieldValue := FormatCurr('#,###,###.00', nAmount); // acurTotals[iFieldNum] := curSum(acurTotals[iFieldNum], nAmount); acurTotals[iFieldNum] := acurTotals[iFieldNum] + nAmount; end else if (nFieldDataType in [ftInteger,ftWord,ftSmallInt,ftLargeInt]) then begin // Right justify. sFieldValue := IntToStr(qryData.Fields[iFieldNum].AsInteger); end else if (nFieldDataType = ftBoolean) then begin // Logical value to be shown. if (qryData.Fields[iFieldNum].AsBoolean) then sFieldValue := 'Yes' else sFieldValue := 'No'; end else if (nFieldDataType = ftFloat) then begin // Right justify. // Allow an accuracy of only 2 decimal place. sFieldValue := FormatFloat('#,###,###.00', qryData.Fields[iFieldNum].AsFloat); nAmount := qryData.Fields[iFieldNum].AsCurrency; // acurTotals[iFieldNum] := curSum(acurTotals[iFieldNum], nAmount); acurTotals[iFieldNum] := acurTotals[iFieldNum] + nAmount; end else begin // Do not print this data field value. sFieldValue := ''; end; // Print the data if the data is a known type and valid. with Items do begin AddObject(sFieldValue, pointer(aFieldWidth[iFieldNum])); end; end; // for iFieldNum := 0 to (qryData.FieldCount-1) do. PrintLine(Items); // Force print job to begin a new page if printed output has // exceeded page height. if ((AmountPrinted + LineHeight) > Printer.PageHeight) then begin AmountPrinted := 0; if not Printer.Aborted then begin Printer.NewPage; end; Inc(pageno); PrintHeader( sPrintHeader ); PrintColumnNames( slColumnNames, aFieldWidth ); end; Items.Clear; // Report progress. Inc(nCount); Progress.ProgressBar1.Position := Trunc(nCount * Factor); // Find the next item to be printed. qryData.Next; end; // while not qryData.Eof do. // Printing done, close the print job. PrintFooter( acurTotals, aFieldWidth ); Printer.EndDoc; finally // try. Items.Free; slColumnNames.Free; Progress.Close; end; // try. end; // procedure PrintDataSet(). // ***************************************************************** // PRINT THE QUERY DATA PASSED // ***************************************************************** procedure PrintQuery( qryData:TQuery; sPrintHeader: string); var // First 3 - printer access variables used for printer setup. ADevice, ADriver, Aport : array [0..255] of char; DeviceHandle : THandle; DevMode : PDeviceMode; // A pointer to a TDeviceMode structure. Progress : TProgressForm; nCount : Integer; Factor : Real; iFieldNum : Integer; sFieldValue : string; nFieldWidth : Integer; nFieldDataType : TFieldType; Items: TStringList; slColumnNames : TStringList; aFieldWidth : array of Integer; nAmount : Currency; acurTotals : Array of Currency; nLengthOfString : Integer; begin // The table required for printing must already be open. nLenLMargin := 2; // 2/10 inches. Left margin. nLenRMargin := 2; // 2/10 inches. Right margin. nLenTMargin := 6; // 6/10 inches. Top margin. nLenBMargin := 6; // 6/10 inches. Bottom margin. nLenServiceID := 20; // 2 inches - for ServiceID only. nLenReason := 10; // 1 inch - for the critical error description. nLenRefno := 4; // 3/10 inches. ******** nLenCustno := 15; // 15/10 inches. ******** nLenDate := 5; // 7/10 inches. ******** nLenAmount := -5; // 7/10 inches, right justified. ******** pageno := 0; SetLength( aCurTotals, qryData.FieldCount); // Get the column names from the fields in the query. slColumnNames := TStringList.Create; slColumnNames.Clear; for iFieldNum := 0 to (qryData.FieldCount-1) do begin slColumnNames.Add(qryData.Fields[iFieldNum].DisplayLabel); end; // Determine the width of each field. for iFieldNum := 0 to (qryData.FieldCount-1) do begin nFieldDataType := qryData.Fields[iFieldNum].DataType; if (slColumnNames.Strings[iFieldNum] = 'ServiceID') then begin // Special for ServiceID fields. nFieldWidth := nLenServiceID; end else if (slColumnNames.Strings[iFieldNum] = 'ServiceID') then begin // Special for critical error descriptions. nFieldWidth := nLenReason; end else if (nFieldDataType in [ftString,ftFixedChar,ftWideString]) then begin // A string value held. nLengthOfString := qryData.Fields[iFieldNum].DisplayWidth; if nLengthOfString > 10 then nFieldWidth := nLenCustno else if nLengthOfString > 5 then nFieldWidth := nLenDate else nFieldWidth := nLenRefno; end else if (nFieldDataType in [ftDate,ftDateTime]) then // Convert for date value only. nFieldWidth := nLenDate else if (nFieldDataType = ftCurrency) then // Right justify. nFieldWidth := nLenAmount else if (nFieldDataType in [ftInteger,ftWord,ftSmallInt,ftLargeInt]) then // Right justify. nFieldWidth := nLenAmount else if (nFieldDataType = ftBoolean) then // Logical value to be shown. nFieldWidth := nLenRefno else if (nFieldDataType = ftFloat) then // Right justify. nFieldWidth := nLenAmount else // Do not print this data field value. nFieldWidth := 0; SetLength( aFieldWidth, Length(aFieldWidth) + 1); aFieldWidth[iFieldNum] := nFieldWidth; aCurTotals[iFieldNum] := 0.0000; end; // Open the progress form. Progress := TProgressForm.Create(NIL); // Display a message to the user. Progress.Msg.Caption := 'Printing data ...'; Progress.Show; // Determine the parameters for the progress bar. nCount := qryData.RecordCount; if (nCount = 0) then begin // Avoid div by zero. Factor := 100.0; end else begin Factor := 100.0 / nCount; end; nCount := 0; // Now get the printer ready. // Create a TStringList instance to hold the fields and the widths // of the columns in which they'll be drawn based on the entries in // the edit controls. Items := TStringList.Create; // Determine pixels per inch horizontally PixelsInInchx := GetDeviceCaps(Printer.Handle, LOGPIXELSX); TenthsOfInchPixelsY := GetDeviceCaps(Printer.Handle, LOGPIXELSY) div 10; AmountPrinted := 0; try // Set the printer up the way I want it. // First obtain a handle to the TPrinter's Devicemode structure. Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle); // If the DeviceHandle is still 0, then the driver was not loaded. Set // the printer index to force the printer driver to load making the // handle available. if DeviceHandle = 0 then begin Printer.PrinterIndex := Printer.PrinterIndex; Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle); end; // If the DeviceHandle is still 0, then an error has occurred. Otherwise // use GlobalLock() to get the pointer to the TDeviceMode structure. if (DeviceHandle = 0) then Raise Exception.Create('Could Not Initialize Printer TDeviceMode' + ' Structure.') else DevMode := GlobalLock(Devicehandle); // NOW I can set up the printer!! with DevMode^ do begin // Set landscape mode. dmFields := dmFields or DM_ORIENTATION; dmOrientation := DMORIENT_LANDSCAPE; end; // Okay, all done with this setup. if ( DeviceHandle <> 0 ) then begin GlobalLock(Devicehandle); end; Printer.BeginDoc; // Calculate the line height based on text height using the // currently rendered font. LineHeight := Printer.Canvas.TextHeight('X') + TenthsOfInchPixelsY; // Start the printing. Inc(pageno); PrintHeader( sPrintHeader ); PrintColumnNames( slColumnNames, aFieldWidth ); qryData.First; while not qryData.Eof do begin // Add the left margin. with Items do begin AddObject('', pointer(nLenLMargin)); end; // Add each field in the query. for iFieldNum := 0 to (qryData.FieldCount-1) do begin nFieldDataType := qryData.Fields[iFieldNum].DataType; if (nFieldDataType in [ftString,ftFixedChar,ftWideString]) then begin // A string value held. sFieldValue := qryData.Fields[iFieldNum].AsString; end else if (nFieldDataType in [ftDate,ftDateTime]) then begin // Convert for date value only. sFieldValue := FormattedDateString( qryData.Fields[iFieldNum].AsDateTime); end else if (nFieldDataType = ftCurrency) then begin // Right justify. nAmount := qryData.Fields[iFieldNum].AsCurrency; sFieldValue := FormatCurr('#,###,###.00', nAmount); // acurTotals[iFieldNum] := curSum(acurTotals[iFieldNum], nAmount); acurTotals[iFieldNum] := acurTotals[iFieldNum] + nAmount; end else if (nFieldDataType in [ftInteger,ftWord,ftSmallInt,ftLargeInt]) then begin // Right justify. sFieldValue := IntToStr(qryData.Fields[iFieldNum].AsInteger); end else if (nFieldDataType = ftBoolean) then begin // Logical value to be shown. if (qryData.Fields[iFieldNum].AsBoolean) then begin sFieldValue := 'Yes'; end else begin sFieldValue := 'No'; end; end else if (nFieldDataType = ftFloat) then begin // Right justify. // Allow an accuracy of only 2 decimal place. sFieldValue := FormatFloat('#,###,###.00', qryData.Fields[iFieldNum].AsFloat); nAmount := qryData.Fields[iFieldNum].AsCurrency; // acurTotals[iFieldNum] := curSum(acurTotals[iFieldNum], nAmount); acurTotals[iFieldNum] := acurTotals[iFieldNum] + nAmount; end else begin // Do not print this data field value. sFieldValue := ''; end; // Print the data if the data is a known type and valid. with Items do begin AddObject(sFieldValue, pointer(aFieldWidth[iFieldNum])); end; end; // for iFieldNum := 0 to (qryData.FieldCount-1) do. PrintLine(Items); // Force print job to begin a new page if printed output has // exceeded page height. if ((AmountPrinted + LineHeight) > Printer.PageHeight) then begin AmountPrinted := 0; if not Printer.Aborted then begin Printer.NewPage; end; Inc(pageno); PrintHeader( sPrintHeader ); PrintColumnNames( slColumnNames, aFieldWidth ); end; Items.Clear; // Report progress. Inc(nCount); Progress.ProgressBar1.Position := Trunc(nCount * Factor); // Find the next item to be printed. qryData.Next; end; // while not qryData.Eof do. // Printing done, close the print job. PrintFooter( acurTotals, aFieldWidth ); Printer.EndDoc; finally // try. Items.Free; slColumnNames.Free; Progress.Close; end; // try. end; // procedure PrintQuery(). // ***************************************************************** // PRINT THE TABLE PASSED // ***************************************************************** procedure PrintTable( tblData:TTable; sPrintHeader: string); var // First 3 - printer access variables used for printer setup. ADevice, ADriver, Aport : array [0..255] of char; DeviceHandle : THandle; DevMode : PDeviceMode; // A pointer to a TDeviceMode structure. Progress : TProgressForm; nCount : Integer; Factor : Real; iFieldNum : Integer; sFieldValue : string; nFieldWidth : Integer; nFieldDataType : TFieldType; Items: TStringList; slColumnNames : TStringList; aFieldWidth : array of Integer; nAmount : Currency; acurTotals : Array of Currency; nLengthOfString : Integer; begin // The table required for printing must already be open. nLenLMargin := 2; // 2/10 inches. Left margin. nLenRMargin := 2; // 2/10 inches. Right margin. nLenTMargin := 6; // 6/10 inches. Top margin. nLenBMargin := 6; // 6/10 inches. Bottom margin. nLenRefno := 5; // 5/10 inches. nLenCustno := 14; // 15/10 inches. nLenDate := 7; // 7/10 inches. nLenAmount := -7; // 7/10 inches, right justified. nLenInteger := -5; // 6/10 inches, right justified. pageno := 0; SetLength( aCurTotals, tblData.FieldCount); // Get the column names from the fields in the query. slColumnNames := TStringList.Create; slColumnNames.Clear; for iFieldNum := 0 to (tblData.FieldCount-1) do begin slColumnNames.Add(tblData.Fields[iFieldNum].DisplayLabel); end; // Determine the width of each field. for iFieldNum := 0 to (tblData.FieldCount-1) do begin nFieldDataType := tblData.Fields[iFieldNum].DataType; if (nFieldDataType in [ftString,ftFixedChar,ftWideString]) then begin // A string value held. nLengthOfString := tblData.Fields[iFieldNum].DisplayWidth; if nLengthOfString > 10 then begin nFieldWidth := nLenCustno; end else if nLengthOfString > 5 then begin nFieldWidth := nLenDate; end else begin nFieldWidth := nLenRefno; end; end else if (nFieldDataType in [ftDate,ftDateTime]) then begin // Convert for date value only. nFieldWidth := nLenDate; end else if (nFieldDataType = ftCurrency) then begin // Right justify. nFieldWidth := nLenAmount; end else if (nFieldDataType in [ftInteger,ftWord,ftSmallInt,ftLargeInt]) then begin // Right justify. nFieldWidth := nLenInteger; end else if (nFieldDataType = ftBoolean) then begin // Logical value to be shown. nFieldWidth := nLenRefno; end else if (nFieldDataType = ftFloat) then begin // Right justify. nFieldWidth := nLenAmount; end else begin // Do not print this data field value. nFieldWidth := 0; end; SetLength( aFieldWidth, Length(aFieldWidth) + 1); aFieldWidth[iFieldNum] := nFieldWidth; aCurTotals[iFieldNum] := 0.0000; end; // Open the progress form. Progress := TProgressForm.Create(NIL); // Display a message to the user. Progress.Msg.Caption := 'Printing data ...'; Progress.Show; // Determine the parameters for the progress bar. nCount := tblData.RecordCount; if (nCount = 0) then begin // Avoid div by zero. Factor := 100.0; end else begin Factor := 100.0 / nCount; end; nCount := 0; // Now get the printer ready. // Create a TStringList instance to hold the fields and the widths // of the columns in which they'll be drawn based on the entries in // the edit controls. Items := TStringList.Create; // Determine pixels per inch horizontally PixelsInInchx := GetDeviceCaps(Printer.Handle, LOGPIXELSX); TenthsOfInchPixelsY := GetDeviceCaps(Printer.Handle, LOGPIXELSY) div 10; AmountPrinted := 0; try // Set the printer up the way I want it. // First obtain a handle to the TPrinter's Devicemode structure. Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle); // If the DeviceHandle is still 0, then the driver was not loaded. Set // the printer index to force the printer driver to load making the // handle available. if DeviceHandle = 0 then begin Printer.PrinterIndex := Printer.PrinterIndex; Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle); end; // If the DeviceHandle is still 0, then an error has occurred. Otherwise // use GlobalLock() to get the pointer to the TDeviceMode structure. if (DeviceHandle = 0) then begin Raise Exception.Create('Could Not Initialize Printer TDeviceMode' + ' Structure.'); end else begin DevMode := GlobalLock(Devicehandle); end; // NOW I can set up the printer!! with DevMode^ do begin // Set landscape mode. dmFields := dmFields or DM_ORIENTATION; dmOrientation := DMORIENT_LANDSCAPE; end; // Okay, all done with this setup. if ( DeviceHandle <> 0 ) then begin GlobalLock(Devicehandle); end; Printer.BeginDoc; // Calculate the line height based on text height using the // currently rendered font. LineHeight := Printer.Canvas.TextHeight('X') + TenthsOfInchPixelsY; // Start the printing. Inc(pageno); PrintHeader( sPrintHeader ); PrintColumnNames( slColumnNames, aFieldWidth ); tblData.First; while not tblData.Eof do begin // Add the left margin. with Items do begin AddObject('', pointer(nLenLMargin)); end; // Add each field in the query. for iFieldNum := 0 to (tblData.FieldCount-1) do begin nFieldDataType := tblData.Fields[iFieldNum].DataType; if (nFieldDataType in [ftString,ftFixedChar,ftWideString]) then begin // A string value held. sFieldValue := tblData.Fields[iFieldNum].AsString; end else if (nFieldDataType in [ftDate,ftDateTime]) then begin // Convert for date value only. if (tblData.Fields[iFieldNum].AsFloat = 0.0) then begin sFieldValue := ''; end else begin sFieldValue := FormattedDateString( tblData.Fields[iFieldNum].AsDateTime); end; end else if (nFieldDataType = ftCurrency) then begin // Right justify. nAmount := tblData.Fields[iFieldNum].AsCurrency; sFieldValue := FormatCurr('#,###,###.00', nAmount); // acurTotals[iFieldNum] := curSum(acurTotals[iFieldNum], nAmount); acurTotals[iFieldNum] := acurTotals[iFieldNum] + nAmount; end else if (nFieldDataType in [ftInteger,ftWord,ftSmallInt,ftLargeInt]) then begin // Right justify. sFieldValue := IntToStr(tblData.Fields[iFieldNum].AsInteger); end else if (nFieldDataType = ftBoolean) then begin // Logical value to be shown. if (tblData.Fields[iFieldNum].AsBoolean) then begin sFieldValue := 'Yes'; end else begin sFieldValue := 'No'; end; end else if (nFieldDataType = ftFloat) then begin // Right justify. // Allow an accuracy of only 1 decimal place. sFieldValue := FormatFloat('#,###,###.00', tblData.Fields[iFieldNum].AsFloat); nAmount := tblData.Fields[iFieldNum].AsCurrency; // acurTotals[iFieldNum] := curSum(acurTotals[iFieldNum], nAmount); acurTotals[iFieldNum] := acurTotals[iFieldNum] + nAmount; end else begin // Do not print this data field value. sFieldValue := ''; end; // Print the data if the data is a known type and valid. with Items do begin AddObject(sFieldValue, pointer(aFieldWidth[iFieldNum])); end; end; // for iFieldNum := 0 to (qryData.FieldCount-1) do. PrintLine(Items); // Force print job to begin a new page if printed output has // exceeded page height. if ((AmountPrinted + LineHeight) > Printer.PageHeight) then begin AmountPrinted := 0; if not Printer.Aborted then begin Printer.NewPage; end; Inc(pageno); PrintHeader( sPrintHeader ); PrintColumnNames( slColumnNames, aFieldWidth ); end; Items.Clear; // Report progress. Inc(nCount); Progress.ProgressBar1.Position := Trunc(nCount * Factor); // Find the next item to be printed. tblData.Next; end; // while not qryData.Eof do. // Printing done, close the print job. PrintFooter( acurTotals, aFieldWidth ); Printer.EndDoc; finally // try. Items.Free; slColumnNames.Free; Progress.Close; end; // try. end; // procedure PrintTable(). end.