Solved

Delphi, I need to print a String Grid, derived from TStringGrid with ability to to display vertical text, how could I do this?

Posted on 2010-08-31
20
1,045 Views
Last Modified: 2012-05-10
I have implemented a String Grid with Vertical Text solution from Aflarin ID: 33564654.  I need to print this String Grid to a printer.  Using a simple canvas copy of the String Grid works but the quality is not good.  How can this be done?
0
Comment
Question by:HenryM2
  • 10
  • 10
20 Comments
 
LVL 13

Expert Comment

by:aflarin
ID: 33570879
Use the attached function like this:

procedure TForm1.Button1Click(Sender: TObject);
var
  w, h: Integer;
begin
  // Variant 1 - Auto scaling
  PrintGrid(StringGrid1, 'Grid Title', 100, 100 );

  // Variant 1 - Setting Width and Height
  w:= Round( StringGrid1.Width   * GetDeviceCaps(Printer.Handle, LOGPIXELSX)/PixelsPerInch );
  h:= Round( StringGrid1.Height * GetDeviceCaps(Printer.Handle, LOGPIXELSY)/PixelsPerInch );
  PrintGrid(StringGrid1, 'Grid Title', 100, 100, False, 100+w, 100+h );
end;

uses

  Printers;



procedure AngleTextOut(ACanvas: TCanvas; Angle, X, Y: Integer; Rect: TRect; Str: string);

var

  LogRec: TLogFont;

  OldFontHandle,

  NewFontHandle: hFont;

begin

  GetObject(ACanvas.Font.Handle, SizeOf(LogRec), Addr(LogRec));

  LogRec.lfEscapement := Angle*10;

  LogRec.lfOutPrecision:= OUT_TT_ONLY_PRECIS;

  NewFontHandle := CreateFontIndirect(LogRec);

  OldFontHandle := SelectObject(ACanvas.Handle, NewFontHandle);

  ACanvas.TextRect(Rect, X, Y, Str);

  NewFontHandle := SelectObject(ACanvas.Handle, OldFontHandle);

  DeleteObject(NewFontHandle);

end;



procedure PrintGrid(sGrid: TStringGrid; const sTitle: String; X, Y: Integer; AutoScale: Boolean = True; Width: Integer = 0; Height: Integer = 0);

var

  scaleX, scaleY: Double;



  function sX(Pixels: Integer): Integer;

  begin

    Result:= Round(Pixels * scaleX);

  end;



  function sY(Pixels: Integer): Integer;

  begin

    Result:= Round(Pixels * scaleY);

  end;



var

  mWidth, mHeight : Integer;

  iCol, IRow : Integer;

  CurX, CurY: Integer;

  nextX, nextY: Integer;

  s: TSize;

  m: Integer;

begin

  Printer.Title:=sTitle;

  Printer.BeginDoc;

  try

    Printer.Canvas.Font:= sGrid.Font;



    if AutoScale then

    begin

      Width:= mWidth;

      Height:= mHeight;

    end;



    mWidth:= 0;

    for iCol:= 0 to sGrid.ColCount-1 do

      Inc(mWidth, sGrid.ColWidths[iCol] );



    mHeight:= 0;

    for iRow:= 0 to sGrid.RowCount-1 do

      Inc(mHeight, sGrid.RowHeights[iRow] );



    // title

    s:= Printer.Canvas.TextExtent( sTitle );

    m:= (Width - s.cx) div 2;

    Printer.Canvas.TextOut(X+m, Y, Printer.Title);

    m:= Round(s.cy * 1.1);

    Inc(Y, m);



    if AutoScale then

    begin

      scaleX:= GetDeviceCaps(Printer.Handle, LOGPIXELSX)/Screen.PixelsPerInch ;

      scaleY:= GetDeviceCaps(Printer.Handle, LOGPIXELSY)/Screen.PixelsPerInch ;

    end

    else

    begin

      scaleX:= Width / mWidth;

      scaleY:= (Height + m) / mHeight;

    end;



    CurX:= X;

    for iCol:=0 to sGrid.ColCount-1 do

    begin



      CurY:= Y;

      nextY:= CurY + sY(sGrid.RowHeights[0]);

      nextX:= CurX + sX(sGrid.ColWidths[iCol]);



      AngleTextOut(Printer.Canvas, 90, CurX, nextY, Rect(CurX, CurY, nextX, nextY), sGrid.Cells[iCol, 0] );



      for iRow:=1 to sGrid.RowCount-1 do

      begin

        CurY:= nextY;

        nextY:= CurY + sY(sGrid.RowHeights[iRow]);

        Printer.Canvas.TextRect(Rect(CurX, CurY, NextX, NextY), CurX, CurY, sGrid.Cells[iCol, iRow] );

      end;



      CurX:= NextX;

    end;

  finally

    Printer.EndDoc;

  end;

end;

Open in new window

0
 

Author Comment

by:HenryM2
ID: 33574295
Thanks.

You have:
procedure PrintGrid(sGrid: TStringGrid; const sTitle: String; X, Y: Integer; AutoScale: Boolean = True; Width: Integer = 0; Height: Integer = 0);

I changed it to, below. else it wont compile:
procedure TReportsFrm.PrintGrid(sGrid: TStringGrid; sTitle: String; X, Y: Integer; AutoScale: Boolean; Width: Integer; Height: Integer);

Is this correct?

With this, the output to the printer shows only the very bottom part of the characters in Row 0 and nothing else.  In the attached picture, the top picture shows the top few lines of the String Grid  (works very well now).  The string grid is about 12 pages long and only one page is printed.  The bottom picture shows the printed result, which I just printed to Cute PDF Writer.  Also, the header consisting of row 0 must repeat on each page perhaps with a page number.  Page number can also be in the footer.
Incedentaly, the square brackets in the Grid represents the cells where a check mark is to be made when the item is commisioned.  To keep it simple, it will be good enough to use the dots to keep streight lines instead of printing the lines of the grid.
Grid-and-Printing-Output.jpg
0
 
LVL 13

Expert Comment

by:aflarin
ID: 33574695
>> Is this correct?

It depends on how you use it. what parameters do you pass there?

But It just a sample how to print grid manually. It prints the grid on one page. Of course it can be modified to print on several pages and to repeat header on each page, but you will have to modify the code each time when you need to change something like font size, position, header etc

I think you should use Rave Report to printing. It will more easy. Take a look at this tutorial:

http://edn.embarcadero.com/article/30329
http://edn.embarcadero.com/article/30331
0
 

Author Comment

by:HenryM2
ID: 33574792
I have considdered Rave reports and looked at the tutorials as mentions.  I do agree that it will be the better option but I am not experienced in Rave and thaught it may be a too long learning curve to get going.  However, is there a quick way to transfer the String Grid to Rave, including the row 0 vertical cells.
0
 
LVL 13

Expert Comment

by:aflarin
ID: 33574983
I don't think it will be quick, if you haven't an experience in Rave.

It would be better if you use Rave Visual Designer, but you have to know what is band in Rave and how to bind it to RvConnection and how to make CustomConnection...

Another way is to print directly as described in the first link. It can be made quickly, but it will have to modify code each time when you need to change visual representation.

What way you prefer?
0
 

Author Comment

by:HenryM2
ID: 33575017
I think the code based method is more familiar to me as it uses Delphi code.  Changing Fonts etc. seems quite streight forward.  I think its more a matter of understanding how the Tab settings work e.g.
 SetTab(0.2, pjLeft, 1.7, 0, 0, 0);  Will one use the Tab setting to manage the cells from the String Grid or is there another way?  The headers and footers also seem easy enough.
0
 
LVL 13

Expert Comment

by:aflarin
ID: 33575231
ok, drop RvSystem on your form and try something like this:

procedure TForm1.Button1Click(Sender: TObject);
begin
  RvSystem1.Execute;
end;

procedure TForm1.RvSystem1Print(Sender: TObject);

  procedure PrintGridHeader;
  var
    iCol, iRow : Integer;
  begin
    with Sender as TBaseReport do
    begin
      SetFont('Arial', 10);
      FontRotation:= 90;
      Bold := True;
      NewLine;
      NewLine;
      for iCol:= 0 to StringGrid1.ColCount-1 do
        PrintTab(StringGrid1.Cells[iCol, 0]);
      Bold := False;
      FontRotation:= 0;
      NewLine;
      NewLine;
      NewLine;
      NewLine;
      NewLine;
      NewLine;
    end;
  end;


var
  iCol, iRow : Integer;
begin
  with Sender as TBaseReport do
  begin
    SetFont('Arial', 15);
    NewLine;
    PrintCenter('Title', 4);
    NewLine;
    ClearTabs;
    SetTab(0.2, pjLeft, 1.7, 0, 0, 0);
    for iCol:= 1 to StringGrid1.ColCount-1 do
      SetTab(1.7 + iCol*0.5, pjRight, 0.5, 0, 0, 0);

    PrintGridHeader;
    Bold := False;
    for iRow := 1 to StringGrid1.RowCount - 1 do
    begin
      for iCol:= 0 to StringGrid1.ColCount-1 do
        PrintTab(StringGrid1.Cells[iCol, iRow]);
      NewLine;

      // 50 lines per page
      if (iRow mod 50) = 0 then
      begin
        NewPage;
        PrintGridHeader;
      end;

    end;
  end;
end;

0
 
LVL 13

Expert Comment

by:aflarin
ID: 33575304
BTW, your sample of data looks like database, why don't you use data components?
0
 

Author Comment

by:HenryM2
ID: 33575316
This works very well.  I just need to make columns 1 to x narrower.  I assume its determined in this line:
      SetTab(1.7 + iCol*0.5, pjRight, 0.5, 0, 0, 0);
Would you please explain to me what each parameter above does.
0
 
LVL 13

Expert Comment

by:aflarin
ID: 33575356
1.7 is the first column width. You have to modify it in two lines:

    SetTab(0.2, pjLeft, 1.7, 0, 0, 0);
    for iCol:= 1 to StringGrid1.ColCount-1 do
        SetTab(1.7 + iCol*0.5, pjRight, 0.5, 0, 0, 0);
> Would you please explain to me what each parameter above does

from Rave help:

procedure SetTab(NewPos: double; NewJustify: TPrintJustify; NewWidth: double; NewMargin: double; NewLines: byte; NewShade: byte);

NewPos      - defines the starting position of the tab. If NewPos is set to the constant, NA, then the tab will start immediately after the previous tab box.
NewJustify - defines whether the tab is left (pjLeft), right (pjRight) or center (pjCenter) justified. If a non-zero width is given, then a tab box is defined and the text will be justified within the tab box rather than justified at the tab position.
NewMargin - defines the distance between the tab box side and the text in 1/100ths of an inch.
NewLines - uses the BoxLineXxxx constants to define where lines are to be drawn around the tab box.
NewShade - defines the percent of background shading to use for this tab box.
0
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 

Author Comment

by:HenryM2
ID: 33575554
Thanks, two more small adjustments.
OK got the column widths sorted out.  I just need the vertical header columns to bottom alignd.  Then I need to adjust the header space as its cutting off the text.


Header.jpg
0
 
LVL 13

Expert Comment

by:aflarin
ID: 33575811
try to modify PrintGridHeader procedure like this:

  procedure PrintGridHeader;
  var
    iCol: Integer;
    curY: Double;
  begin
    with Sender as TBaseReport do
    begin
      NewLine;
      NewLine;
      SetFont('Arial', 10);
      FontRotation:= 90;
      Bold := True;
      TabJustify:= tjLeft;
      for iCol:= 0 to StringGrid1.ColCount-1 do
      begin
        curY:= YPos;
        PrintTab(StringGrid1.Cells[iCol, 0]);
        YPos:= curY;
      end;
      Bold := False;
      FontRotation:= 0;
    end;
  end;
0
 

Author Comment

by:HenryM2
ID: 33575896
Alignment is now correct. I just need to understand how to make the header section wider to prevent the text from being truncated as per my last picture.  In the String Grid I did this automatically by taking the longest title string (HeadingLength) and setting the RowHeight accordingly.

ReportsFrm.ReportStringGrid.RowHeights[0] := HeadingLength  * 6;

I now need to apply something simmilar to the Rave Report Heading.  
0
 
LVL 13

Expert Comment

by:aflarin
ID: 33575955
 procedure PrintGridHeader;
  var
    iCol: Integer;
    curY: Double;
  begin
    with Sender as TBaseReport do
    begin
      // here is two empty lines that specify the height of header
      NewLine;
      NewLine;

      // You can:
      // 1. Add new line like this:
      NewLine;
      // 2.  Or you can modify current Y pos like this
      YPos:= YPos + 1 /*inch*/;
 
      // other code
      SetFont('Arial', 10);
      ....


0
 

Author Comment

by:HenryM2
ID: 33576168
Adding the NewLine a few times increases the distance from the Title to the top of the Vertical Text headings, or the top of the page to the vertical text headings.  The headings however remain trunkated at the same place, about 5 or 6 characters max.  See picture, above the red line, the original 2 NewLine s and below the red line if 8 x NewLine s added.
Header-Cut-off-text.png
0
 
LVL 13

Expert Comment

by:aflarin
ID: 33576269

in the

procedure PrintGridHeader;
  ...
   // change this line
   // PrintTab(StringGrid1.Cells[iCol, 0]);

   // to this line
   Print( #9 + StringGrid1.Cells[iCol, 0]);
0
 

Author Comment

by:HenryM2
ID: 33576423
We are getting there.  Truncation now resolved, but now the header tabs are out of alignment with the column tabs below each vertical header string.  I tried to set the XPos before writing the header tabs but that seem to move all the tabs over.
0
 
LVL 13

Accepted Solution

by:
aflarin earned 500 total points
ID: 33576817
I could not do it with tabs, so I changed PrintTab to PrintXY. I don't know your current widths of columns, so correct then yourself:

  procedure PrintGridHeader;
  var
    iCol: Integer;
    curY: Double;
  begin
    with Sender as TBaseReport do
    begin
      YPos:= YPos + 1.1;
      SetFont('Arial', 10);
      FontRotation:= 90;
      Bold := True;
      TabJustify:= tjLeft;
      for iCol:= 1 to StringGrid1.ColCount-1 do
      begin
        curY:= YPos;

        // Correct these widths
        PrintXY( 1.7{Col[0] width } + iCol*0.5 {Col width} + 0.3{some shift}, YPos, StringGrid1.Cells[iCol, 0]);
        YPos:= curY;
      end;
      NewLine;
      Bold := False;
      FontRotation:= 0;
    end;
  end;
0
 

Author Comment

by:HenryM2
ID: 33577055
Aflarin.  Thank you for all your help.  Everything is working very well.  If I could, I would have awarded you more points as this means a lot to me.
0
 

Author Closing Comment

by:HenryM2
ID: 33577091
Thanks again, its working very well. Rave reports was a good choice.  I have learned a lot and can now continue.
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now