Solved

Delphi : How do you print a Bitmap with TPanels superimposed on it, on a RAVE Report.

Posted on 2010-09-05
32
1,060 Views
Last Modified: 2013-11-22
I have a TPanel (FloorPlanPnl) with a TImage (FloorPlanImg) as child.  FloorPlanImg has a picture of TBitmap, of a a floor plan.  FloorPlanPnl contains several children of TPanel representing furniture ojects on a floor plan which can be positioned by dragging around with the mouse.

I need to print the FloorPlanImg with TBitmap background including the TPanel object representing the furniture on a Rave Report, using code.
0
Comment
Question by:HenryM2
  • 16
  • 11
  • 5
32 Comments
 
LVL 25

Expert Comment

by:epasquier
ID: 33609056
you have first to merge your furnitures images on the floor image, and set that new image as source for an image report component.

Here is how to merge your image :
Var

 MergedBMP:TBitmap;

begin

 MergedBMP:=TBitmap.Create;

 MergedBMP.Assign(FloorPlanImg.Picture.Bitmap);

 for i:=0 to NbFurnitures-1 do

  begin

   MergedBMP.Canvas.Draw(X,Y,FurnitureImage[i].Picture.Graphic);

// Where X,Y are the positions of each FurnitureImages relative to the floor image

  end;

end;

Open in new window

0
 
LVL 25

Expert Comment

by:epasquier
ID: 33609076
Just one question : do you have, as I understand, your furnitures images on TPanels ? like :

FloorPlanPnl
  + FloorPlanImg
  + Furniture1Panel
     + Furniture1Image
  + Furniture2Panel
     + Furniture2Image

If so, why not having the furnitures images directly as TImages at the same level than the Floor image ?

The above code would be then the function below, that you call with your panel and the floor image

Function MergeAllImages(Parent:TPanel;Floor:TImage):TBitmap;

Var

 Img:TImage;

begin

 Result:=TBitmap.Create;

 Result.Assign(FloorPlanImg.Picture.Bitmap);

 for i:=0 to Parent.ComponentCount-1 do 

  if (Parent.Components[i]<>Floor) 

      And Parent.Components[i] Is TImage Then 

  begin

   Img:=TImage(Parent.Components[i]);

   Result.Canvas.Draw(

    Img.Left-FloorPlanImg.Left,

    Img.Top -FloorPlanImg.Top,

    Img.Picture.Graphic);

  end;

end;

Open in new window

0
 

Author Comment

by:HenryM2
ID: 33609134
The furniture objects are text only and not a graphical immages, hence just a label with a name representing the furniture code.  It would have been nice to have a graphical immage of the furniture, but space on the floor plan is a problem as it needs to print on A4 paper.  Below, a sample of the floor plan.
Floor-Plan-Example.png
0
 
LVL 25

Expert Comment

by:epasquier
ID: 33609190
then you can use PaintTo to make the panel (and everything it might contain) to paint itself on the bitmap canvas. Let's say we keep the TImages draw as well, just in case you use it one day.
And let's say that instead of managing only TPanel, we will manage all windowed controls (TWinControl descendants, including TPanel)
Function MergeAllImages(Parent:TPanel;Floor:TImage):TBitmap;

Var

 Img:TImage;

 WinCtrl:TWinControl;

begin

 Result:=TBitmap.Create;

 Result.Assign(FloorPlanImg.Picture.Bitmap);

 for i:=0 to Parent.ComponentCount-1 do 

  if Parent.Components[i]<>Floor Then

   begin

    if Parent.Components[i] Is TImage Then 

     begin

      Img:=TImage(Parent.Components[i]);

      Result.Canvas.Draw(

       Img.Left-FloorPlanImg.Left,

       Img.Top -FloorPlanImg.Top,

       Img.Picture.Graphic);

     end;

    if Parent.Components[i] Is TWinControl Then 

     begin

      WinCtrl:=TWinControl (Parent.Components[i]);

      WinCtrl.PaintTo(Result.Handle,

       WinCtrl.Left-FloorPlanImg.Left,

       WinCtrl.Top -FloorPlanImg.Top,

     end;

   end;

end;

Open in new window

0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 33609269
Just programmatically do a screen shot :-)
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 33609277
0
 

Author Comment

by:HenryM2
ID: 33609389
Epasquier, I now have my code as below.

In line 24 of your code above, I ended the line with ).  Not sure if this is correct.

I added a TImage (ReportImg) in my Reports Unit.

As code as now produces a compiler error:
[DCC Error] SiteDesignerMainUnt.pas(18494): E2010 Incompatible types: 'TPicture' and 'TBitmap' on line
  ReportsFrm.ReportImg.Picture := MergeAllImages(FloorPlanPnl, FloorPlanImg);
 in my calling procedure to TMainFrm.MergeAllImmages


Function TMainFrm.MergeAllImages(Parent:TPanel;Floor:TImage):TBitmap;

Var

 Img:TImage;

 WinCtrl:TWinControl;

 i : Integer;

begin

 Result:=TBitmap.Create;

 Result.Assign(FloorPlanImg.Picture.Bitmap);

 for i:=0 to Parent.ComponentCount-1 do

   if Parent.Components[i]<>Floor Then

   begin

     if Parent.Components[i] Is TImage Then

     begin

      Img:=TImage(Parent.Components[i]);

      Result.Canvas.Draw(

       Img.Left-FloorPlanImg.Left,

       Img.Top -FloorPlanImg.Top,

       Img.Picture.Graphic);

     end;

     if Parent.Components[i] Is TWinControl Then

     begin

       WinCtrl:=TWinControl (Parent.Components[i]);

       WinCtrl.PaintTo(Result.Handle,

       WinCtrl.Left-FloorPlanImg.Left,

       WinCtrl.Top -FloorPlanImg.Top)

     end;

   end;

end;



procedure TMainFrm.PrintFloorPlanWithEquipmentClick(Sender: TObject);

begin

  ReportsFrm.ReportImg.Picture := MergeAllImages(FloorPlanPnl, FloorPlanImg);

end;

Open in new window

0
 
LVL 25

Expert Comment

by:epasquier
ID: 33609596
except that a screenshot will have difficulties with scrollbars, and most of the time, when an interface let the user move objects around with the mouse, there are some GUI elements that show the user which element is selected (like the 8 black squares in Delphi IDE), and/or a grid etc... all these things that are not welcome in the final bitmap.
BTW, a screen capture will do about the same, drawing all the elements one after another using the Z-Order.
Now that we talk about Z-Order, the above function does not manage the relative Z position of the elements while rendering the final bitmap, so if you happen to have some panels overlapping, the result might differ in that regard. The solution for that would be not to loop through all controls with Components array property but with Controls array property.

I tested it, and fixed a few mistakes. This one is working like a charm (and if you have images that are stretched, it is handled)
Function MergeAllImages(Parent:TWinControl;Floor:TImage):TBitmap;

Var

 Img:TImage;

 WinCtrl:TWinControl;

 i,X,Y:integer;

begin

 Result:=TBitmap.Create;

 Result.Width:=Floor.Width;

 Result.Height:=Floor.Height;

 Result.Canvas.StretchDraw(0,0,Floor.Picture.Graphic);

 for i:=0 to Parent.ControlCount-1 do

  if Parent.Controls[i]<>Floor Then

   begin

    if Parent.Controls[i] Is TImage Then

     begin

      Img:=TImage(Parent.Controls[i]);

      X:=Img.Left-Floor.Left;

      Y:=Img.Top -Floor.Top;

      Result.Canvas.StretchDraw(

       Rect(X,

            Y,

            X+Img.Width,

            Y+Img.Height),

       Img.Picture.Graphic);

     end;

    if Parent.Controls[i] Is TWinControl Then

     begin

      WinCtrl:=TWinControl (Parent.Controls[i]);

      Result.Canvas.Lock;

      WinCtrl.PaintTo(Result.Canvas.Handle,

       WinCtrl.Left-Floor.Left,

       WinCtrl.Top -Floor.Top);

      Result.Canvas.Unlock;

     end;

   end;

end;

Open in new window

0
 
LVL 25

Expert Comment

by:epasquier
ID: 33609603
Usage :

ReportsFrm.ReportImg.Picture.Bitmap := MergeAllImages(FloorPlanPnl, FloorPlanImg);
0
 

Author Comment

by:HenryM2
ID: 33610348
I am getting a copile error
[DCC Error] SiteDesignerMainUnt.pas(18475): E2010 Incompatible types: 'TRect' and 'Integer'
for line
 Result.Canvas.StretchDraw(0,0,Floor.Picture.Graphic);
0
 
LVL 25

Expert Comment

by:epasquier
ID: 33610388
Oups sorry, how come I let this one slip through

Ah, yes I know, I commented this line to test something before I changed Draw to StretchDraw
Result.Canvas.StretchDraw(Rect(0,0,Floor.Width,Floor.Height),Floor.Picture.Graphic);

Open in new window

0
 

Author Comment

by:HenryM2
ID: 33610524
Thanks, its working very well.  I am just having a problem now to align the TPanel componets with the immages on the bitmap.  Hence, my ReportsFrm.ReportImg.Picture.Bitmap must be exactly the same dimentions as the original FloorPlanImg picture.

Then I have to get this to the RaveReport canvas I guess.
0
 
LVL 25

Expert Comment

by:epasquier
ID: 33610693
I'm not sure I understand what your problem is. Do you still need help ?
0
 

Author Comment

by:HenryM2
ID: 33611223
I was trying to figure it out myself, but yes, I need help:

On the attached scrern print the left part of the picture shows the original bitmap immage with the TPanels on it.  The right part of the image shows the ReportsFrm with the Merged Panels on it.  The panels are exactly right in size and position but for some reason, which I can't spot, the newly created bitmap is stretcehd in the vertical but the horizontal seem fine.  I would have immagined that the code:
  Result.Width:=Floor.Width;
  Result.Height:=Floor.Height;
would have taken care of this but that does not seem to be the case.
0
 

Author Comment

by:HenryM2
ID: 33611237
Here is the screen print:

Floor-Plan-Stretched.png
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 33611276
That is why I suggested a simple screen capture :-)
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 25

Expert Comment

by:epasquier
ID: 33611325
That is probably because your report image component has a Stretch property to true
these lines :
  Result.Width:=Floor.Width;
  Result.Height:=Floor.Height;
are only setting the size of the bitmap

but when you assign a bitmap to a TImage, the bitmap can be resized to fit the TImage width/height (if Stretch = True )
Only if you have TImage.AutoSize = True can you be sure that the TImage will be exactly the same size of it's inner image source (the bitmap, or gif, jpeg, whatever image format...)
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 33611384
Of course, epasquier is right in saying that the screen capture will have problem with scrollbars. But if your interface currently limits the floorplan drawing to only the visible form portion, then the screenshot will be an easier quickfix.
0
 
LVL 25

Expert Comment

by:epasquier
ID: 33611420
>> That is why I suggested a simple screen capture :-)
The problem would be exactly the same. That is the TImage component that is responsible for the actual display size/proportions of the image.

I finally found Rave Report help, and it would seem that the TRaveBitmap property that control this is MatchSide, and it should be = msInside (fit proportionally within the designated component area).
You have probably the value msBoth (=Stretch true for TImage)
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 33611902
If it's a  screen capture, at least the whole thing will be scaled proportionately?
0
 
LVL 25

Expert Comment

by:epasquier
ID: 33612032
Henry, there might be some misunderstanding.

Check that your FloorPlanImg.AutoSize:=True , because if its display size is not = to the component size, the function would not work properly. You have 2 possibilities : either use Stretch = True, or AutoSize=True. But not Align=alClient without Stretch nor Autosize, that would be wrong

Also, when you have checked the above , can you do this test :
With MergeAllImages(FloorPlanPnl, FloorPlanImg) do

 begin

  SaveToFile('C:\Merge.Bmp');

  Free;

 end;



and post the image ? so that we know if the problem is from the Bitmap generation or the Bitmap display in the report ? 

Open in new window

0
 

Author Comment

by:HenryM2
ID: 33612557
DragonSlayer, It would have been nice to do a simple screen capture.  Unfortunately, the bitmap is not entirely visible as it has a ScrollBox base beneath the FloorPlanPnl..

Epasquier, attached the BMP as requested.. The problem persists where the BMP immage is stretced in the vertical but not in the horizontal.

The parent structure for the BMP is as follows:
MainFrm
  ScrollBox2  > alClient, AutoScroll = True, Auto Size = False,
    FloorPlanPnl > alNone,  AutoSize = False, Height = 920, Width = 1210
      FloorPlanImg > alClient, AutoSize = True, Height = 920, Stretch  = False, Width = 1200

When I did this, I had quite a bit of problems to keep the TPanel object on FloorPlanPnl aligned with the backdrop of the bit map.  The above combination is about all that worked reliably.



Merge.Bmp
0
 
LVL 25

Accepted Solution

by:
epasquier earned 500 total points
ID: 33612585
As I said, this is wrong :
FloorPlanImg > alClient, AutoSize = True,

Set Align:= alNone, Left:=0, Top:=0
0
 
LVL 25

Expert Comment

by:epasquier
ID: 33612611
And that would also give better results : FloorPlanPnl > alNone, AutoSize := True
to make sure that it encapsulate nicely the floor.
By the way, I'm thinking that this panel is probably useless, except if you wanted to display borders around the image. If not, then you can call the MergeAllImages with the scrollbox as parent
0
 

Author Comment

by:HenryM2
ID: 33612706
Its working extremely well now. Making  FloorPlanPnl > AutoSize := True didn't realy make a difference but I am leaving that one True now.

Yes, the Panel originally was before I had the Scrollbox, so I will try it without it.

Whats now the best way to get this merged.bmp to a printer, is it Rave reports or is there a simpler way?
0
 
LVL 25

Expert Comment

by:epasquier
ID: 33612918
If that is the only thing you want to print in the page, you could well print the image directly on the printer. That is not that hard to do, but a report tool will avoid you the hassle to compute the optimum size & position depending on the printer resolution, page orientation, margins etc...
So use the TRaveBitmap object on a report and set its content to the bitmap you just created :

Bmp:=MergeAllImages(FloorPlanPnl, FloorPlanImg);
RaveBitmap.Image.Assign(Bmp);
Bmp.Free;

that should be it
0
 

Author Comment

by:HenryM2
ID: 33613122
I dont quite get it when you say: RaveBitmap.Image.Assign(Bmp);

I put the code below together from example snips.  It works butt only the left half of the Bitmap image is visible.

I have a  MyRvSystem of TRvSystem on the form.

In the OnPrint event of MyRvSystem I have:

procedure TReportsFrm.MyRvSystemPrint(Sender: TObject);
Var Report: TBaseReport;
begin
  PrintGraphicsReport(Sender as TBaseReport)
end;

procedure TReportsFrm.PrintGraphicsReport(Report: TBaseReport);
var
  Bitmap : TBitmap;
begin
  with Report do
  begin
    Bitmap := TBitmap.Create;
    try
      BitMap := MainFrm.MergeAllImages(MainFrm.FloorPlanPnl, MainFrm.FloorPlanImg);
      PrintBitmap(3.5,0.3,1,1, Bitmap); // Obviously this is the problem but how do I fix it?
    finally
      Bitmap.Free;
    end;
  end;
end;
0
 
LVL 25

Expert Comment

by:epasquier
ID: 33613166
first, this is an error :

   Bitmap := TBitmap.Create; // You create a bitmap object an dstore its ref in Bitmap variable
    try
 // and here you overwrite this reference with the bitmap created by the function : you old bitmap ref is lost and will never be destroyed
      BitMap := MainFrm.MergeAllImages(MainFrm.FloorPlanPnl, MainFrm.FloorPlanImg);
      PrintBitmap(3.5,0.3,1,1, Bitmap); // Obviously this is the problem but how do I fix it?
    finally
      Bitmap.Free;
    end;

This is how it's done :
// Bitmap := TBitmap.Create; // No creation here



 BitMap := MainFrm.MergeAllImages(MainFrm.FloorPlanPnl, MainFrm.FloorPlanImg);

 PrintBitmap(3.5,0.3,1,1, Bitmap);

 Bitmap.Free;



Simply... As for the thing that needs fixing, I don't know rave enough. Maybe I can have a look tomorrow, but you'd better ask in another post how to assign a bitmap to a rave report. That question is too long for another expert come and look here

Open in new window

0
 

Author Comment

by:HenryM2
ID: 33613199
epasquier  My question originally was to get the whole thing to Rave.  I however do agree that the whole thing escalated beyond expectation to get the merging right which I think was the bulk of the problem.  Thanks for your help, I will address this in a separate post tomorrow if I don't come right.  Been a long day.
0
 
LVL 25

Expert Comment

by:epasquier
ID: 33613822
Thank you, yes that has been a long day.
Just for the record, you should have selected this post #33609596 as answer, if somebody else need a solution for this same kind of problem, that is the real thing.

But don't bother really - good night
0
 

Author Comment

by:HenryM2
ID: 33615019
Thanks for pointing out the correct post that I should have selected.  I thaught I should select the last post that brough the problem to finality.
0
 
LVL 25

Expert Comment

by:epasquier
ID: 33616136
You can select multiple answers, but the big one with code solving 95% of the problem should be awarded more points to be sure it will popup as THE answer. Other users looking for solutions will see this one first, and that will have to be the most complete to make them open the post and read all others details. And they will start by reading all the selected posts before reading all the "work in progress posts" - if they ever need those.

That is all the more true when the post selected as parts of solution comes from different expert. The balancing of points is best when proportional to the help you feel they brought (on complex issues at least, sometimes an automated equal sharing is good enough)

If you follow those guide lines, you will be a very effective asker :o)
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.

705 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

21 Experts available now in Live!

Get 1:1 Help Now