Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 343
  • Last Modified:

Prining a Canvas on XP or Win2000

Hi

I have a control (with source) that draws great gant charts.  The trouble is that there is no option to print!

I did no realise this until I bought it and the vendor justs says 'tough'.

I have altered the component to expose several canvasses on which the control is drawn.

The canvasees are a top (containing the captions) and the actual chart itself.

I can copy the canvasses into image controls - so I know that I have grabbed the canvasses ok.  Now I need to stretch  the canvas and print it.

Note: It is a Canvas and not an image that I need to print and I want it to be the width of a standard A4 printer and proportional to the original.

My Canvas is pgGhant.scrollboxcanvas

Thanks

Voodooman
0
Voodooman
Asked:
Voodooman
1 Solution
 
Wim ten BrinkCommented:
If you recently bought the component, demand a refund... Otherwise, see if the component has some export method of the image so you can export it to some image file and then print that image file.
0
 
kretzschmarCommented:
if you can grab it correctly, why not copy it on the printer-canvas?
0
 
mokuleCommented:
Something like

uses printers;

StretchBlt(Printer.Handle , 0, 0, Printer.PageWidth, Printer.PageHeight,
      SrcCanvas.Handle, 0, 0, SrcWidth, SrcHeight, SRCCOPY);
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
Pierre CorneliusCommented:
If you've managed to link to the canvas on which the images are drawn, you could easily capture them as bitmaps and enlarge them with Canvas.StretchDraw or StretchBlt (as suggested per Mokule and also implemented by the Canvas.Stretchdraw function) or even StretchDIBits.

Why enlarge them? Well the quality (resolution) on the screen (the device used for the DC of the canvas) is much lower than that for printers. E.g. The screens resolution is normally 96 dpi whereas the printer would most likely be around 600dpi. This means that if you don't enlarge them, when you print them, they will appear very small on the printout. I explain. A bitmap of say 96 by 96 will be 1 inch wide and 1 inch tall on the screen but on a printer will be 0.16 inch wide by 0.16 tall on a printer with 600 dpi (96/600).

Therefore, I think you're biggest problem is going to be quality. Images enlarged to printer dpi's will be distorted. Unless you have access to the paint function of the controls and can adjust them to perform the rendering/painting in a device-independant manner, you sit with this problem.

How do you get it device-independant?
Well, lets take the canvas wrapper function moveto to explain. If you say canvas.moveto(10,10) it moves to that canvas's 10th x and y pixel. This is device dependant.

What we are after is that calling moveto moves the same distance on any canvas regardless of its dpi. I.e. move to a spot 1 inch along the X-plain and 1-inch along the Y-plain, regardless of whether it is on the screen or on a printer (or whatever). To accomplish this, we need something like this (from head):

procedure MoveToInInches(Canv: TCanvas; xInch, yInch: integer);
var
  xdpi, ydpi: integer;
begin
  xdpi:= GetdeviceCaps(Canv.Handle, LOGPIXELSX);
  ydpi:= GetdeviceCaps(Canv.Handle, LOGPIXELSY);

  Canvas.MoveTo(xInch * xdpi, yInch * ydpi);
end;

Hope this helps

Regards
Pierre
 
0
 
Pierre CorneliusCommented:
You say you have the source, if you paste an example of one of the components paint functions, I could be of more assistance.
0
 
VoodoomanAuthor Commented:
Hi

Experts - thanks for your help.  After reading the above comments I realise that I have not been thinking properly.

Workshop Alex says I should get my money back, however I have been searching for a data aware Gant/timeline control for sometime and have found it imposssible to find. If anyone has any suggestions I would be very interested.  I have tried the TMS Scheduler as a time line and it leaves a lot to be desired.

One problem is that I do not know the height and width of the canvas.  My conception of the Canvas is that it would have a height and width which now seems to be untrue.

I have been looking at the source for the control and can see that I need to expose the height and width of the TGraphic as well as the Canvas.

I take on board what PierreC says about the resolution and appreciate that I might need to redraw the grahic onto anothe canvas (possibly the printer directly).

In the meantime, printing the graphic onto the printer would be a good start.

here is some of the code.

This is the simplest bit, it simply draws a header for the chart with the months 1 to 12

procedure TdgDBYearSchedule.PaintGcTop(Sender: TObject);
var i: integer;
    yWidth: real;
begin
 yWidth:=gcTop.width/12;
 gcTop.Canvas.Brush.Color:=FCaptionBackColor;
 gcTop.Canvas.Pen.Color:=FCaptionBackColor;
 gcTop.Canvas.Rectangle(0,0,gcTop.Width,gcTop.Height);
 gcTop.Canvas.Font.Assign(FCaptionFont);
 for i:=0 to 11 do begin
  if FMonthes.Count>i then gcTop.Canvas.TextOut(round((i)*yWidth+15),5,FMonthes[i]);
 end;
end;

If you can show me how to draw this on the printer - I would appreciate any input.

I will expose the height and width of the TGraphic in the meantime and try your suggestions.

Thanks again

Voodooman



0
 
Pierre CorneliusCommented:
The TCanvas merely encapsulates a Windows device context (e.g. screen, printer, etc). It provides you a way of drawing on the surface of the device context.

To make your sample device-independant try this:

procedure TdgDBYearSchedule.PaintGcTopToCanvas(SomeCanvas: tCanvas);
var i: integer;
    yWidth: real;
    gcDPIx, gcDPIy: integer;
    canvDPIx, canvDPIy: integer;
    ConvertFactorX, ConvertFactorY: Double;
begin
  gcDPIx:= GetDeviceCaps(gcTop.Canvas, LOGPIXELSX);
  gcDPIy:= GetDeviceCaps(gcTop.Canvas, LOGPIXELSY);
  canvDPIx:= GetDeviceCaps(SomeCanvas, LOGPIXELSX);
  canvDPIy:= GetDeviceCaps(SomeCanvas, LOGPIXELSy);

  ConvertFactorX:= canvDPIx / gcDPIx;
  ConvertFactorY:= canvDPIy / gcDPIy;

  yWidth:= round(gcTop.width/12 *   ConvertFactorX);
  gcTop.Canvas.Brush.Color:=FCaptionBackColor;
  gcTop.Canvas.Pen.Color:=FCaptionBackColor;
  gcTop.Canvas.Rectangle(0,0, round(gcTop.Width * ConvertFactorX), round(gcTop.Height * ConvertFactorY));
  gcTop.Canvas.Font.Assign(FCaptionFont);
  for i:=0 to 11 do
    if FMonthes.Count>i
      then gcTop.Canvas.TextOut(round((i*yWidth+15)*ConvertFactorX),round(5 * ConvertFactorY,FMonthes[i]);

end;


Since the above is device-independant, it does not matter whether you try to print it on the screen or on a printer, the physical size should be the same. I.e. if it appears 5cm wide on the screen then it would also appear 5cm wide on the printer. (Not 100% accurate statement because displays can be zoomed along vertical and horz axes using its display driver but still report 96 dpi - but this is minor differences)

You can now paint your gcTop to any canvas e.g.

  PaintGcTopToCanvas(Printer.Canvas);
  PaintGcTopToCanvas(SomeBitmap.canvas);

You could even override its paint method as follows:
  procedure TdgDBYearSchedule.PaintGcTop(Sender: TObject);
  begin
      PaintGcTopToCanvas(gcTop.Canvas);
  {BUT this is completely unecessary since it was specifically written for
  the Display DC which is where the control renders
  itself (thus device-independance is not required here).}
  end;

Kind regards
Pierre

P.S. This is just from head, so please excuse possible typos and/or "grammar" errors
0
 
Pierre CorneliusCommented:
Correction:
  gcDPIx:= GetDeviceCaps(gcTop.Canvas.Handle, LOGPIXELSX);
  gcDPIy:= GetDeviceCaps(gcTop.Canvas.Handle, LOGPIXELSY);
  canvDPIx:= GetDeviceCaps(SomeCanvas.Handle, LOGPIXELSX);
  canvDPIy:= GetDeviceCaps(SomeCanvas.Handle, LOGPIXELSy);
0
 
Pierre CorneliusCommented:
P.S.
To print, you would still need to call begin and end doc

i.e.

Printer.BeginDoc;
PaintGcTopToCanvas(Printer.Canvas);
Printer.EndDoc;
0
 
Pierre CorneliusCommented:
SORRY. Saw some more errors:

Here's corrected version:

procedure TdgDBYearSchedule.PaintGcTopToCanvas(SomeCanvas: tCanvas);
var i: integer;
    yWidth: real;
    gcDPIx, gcDPIy: integer;
    canvDPIx, canvDPIy: integer;
    ConvertFactorX, ConvertFactorY: Double;
begin
  gcDPIx:= GetDeviceCaps(gcTop.Canvas.Handle, LOGPIXELSX);
  gcDPIy:= GetDeviceCaps(gcTop.Canvas.Handle, LOGPIXELSY);
  canvDPIx:= GetDeviceCaps(SomeCanvas.Handle, LOGPIXELSX);
  canvDPIy:= GetDeviceCaps(SomeCanvas.Handle, LOGPIXELSy);

  ConvertFactorX:= canvDPIx / gcDPIx;
  ConvertFactorY:= canvDPIy / gcDPIy;

  yWidth:= round(gcTop.width/12 *   ConvertFactorX);
  SomeCanvas.Brush.Color:=FCaptionBackColor;
  SomeCanvas.Pen.Color:=FCaptionBackColor;
  SomeCanvas.Rectangle(0,0, round(gcTop.Width * ConvertFactorX), round(gcTop.Height * ConvertFactorY));
  SomeCanvas.Font.Assign(FCaptionFont);
  for i:=0 to 11 do
    if FMonthes.Count>i
      then SomeCanvas.TextOut(round((i*yWidth+15)*ConvertFactorX),round(5 * ConvertFactorY,FMonthes[i]);

end;
0
 
VoodoomanAuthor Commented:
Hi

I can see that this is going to be very interesting!

I can't take a look until thursday as tomorrow is my late night I have to work 0800-2000....

Thanks

Will report back asap

Voodooman

0
 
VoodoomanAuthor Commented:
Hi Experts

I have been looking at the answers and doing some testing.

Kretzschmar suggested copying it to the printer canvas.  I had already tried this, but as PierrC comments, it is very small and poor quality.

Mokule suggested:

StretchBlt(Printer.Handle , 0, 0, Printer.PageWidth, Printer.PageHeight,
      SrcCanvas.Handle, 0, 0, SrcWidth, SrcHeight, SRCCOPY);

I looked at StretchBlt  before but did not pursue it as apparently it does not work in all circumstances.  I have now exposed the height and width of the TGraphic and done a trial.  Unfortunately it does not work - the canvas is blank - apparently this happens with some printers.

PierreC offerred a solution that was better than my question required, and was beyond my expectation.

This is where I was hoping to be in a couple of weeks (this is an evening interest - not my main programming effort).

It worked almost out of the box there were a couple of problems on almost the last line but I am extremely pleased - very professional!

procedure TdgDBYearSchedule.PaintGcTopToCanvas(SomeCanvas: tCanvas);
var i: integer;
    yWidth: real;
    gcDPIx, gcDPIy: integer;
    canvDPIx, canvDPIy: integer;
    ConvertFactorX, ConvertFactorY: Double;
begin
  gcDPIx:= GetDeviceCaps(gcTop.Canvas.Handle, LOGPIXELSX);
  gcDPIy:= GetDeviceCaps(gcTop.Canvas.Handle, LOGPIXELSY);
  canvDPIx:= GetDeviceCaps(SomeCanvas.Handle, LOGPIXELSX);
  canvDPIy:= GetDeviceCaps(SomeCanvas.Handle, LOGPIXELSy);

  ConvertFactorX:= canvDPIx / gcDPIx;
  ConvertFactorY:= canvDPIy / gcDPIy;

  yWidth:= round(gcTop.width/12 *   ConvertFactorX);

  SomeCanvas.Brush.Color:=FCaptionBackColor;
  SomeCanvas.Pen.Color:=FCaptionBackColor;
  SomeCanvas.Rectangle(0,0, round(gcTop.Width * ConvertFactorX), round(gcTop.Height * ConvertFactorY));
  SomeCanvas.Font.Assign(FCaptionFont);


for i:=0 to 11 do
    if FMonthes.Count>i
      then SomeCanvas.TextOut(round(i*yWidth+15),round(5 * ConvertFactorY),FMonthes[i]); //***Changed here****




 end;

Thanks to all, especially PierreC - great job!

I am going to rework the whole control over the next few weeks (there are some other problems).  This is part of a series of Project Management Software I am working on. You can see my software at Http://www.project-eo.com

Thanks again

Voodooman
0
 
Pierre CorneliusCommented:

Glad I could help. Good luck.
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now