You are not logged in.
Pages: 1
Hi Ab,
Your more than welcome I'm glad I can give something back. I do use the version from code.google.com although I'm using version 11.2 not 11.4 which is the latest version.
I've have opened a ticket on that project asking for two code changes if possible. The ticket number is 240. In that I mention this project and why I'm asking for the changes. If they make them then you don't need to rely on TMetaFilePrinter which is part of that project any more.
For those that want the modification to that project this is what I did:
File: vwPrinter.pas
Make all of the private variable protected i.e
ThtPrinter = class(TComponent)
private
FOffsetX: Integer; // Physical Printable Area x margin
FOffsetY: Integer; // Physical Printable Area y margin
FPaperHeight: Integer; // Physical Height in device units
FPaperWidth: Integer; // Physical Width in device units
FPgHeight: Integer; // Vertical height in pixels
FPgWidth: Integer; // Horizontal width in pixels
FPPIX: Integer; // Logical pixels per inch in X
FPPIY: Integer; // Logical pixels per inch in Y
FPrinting: Boolean;
FTitle: ThtString; // Printed Document's Title
protected
function GetCanvas: TCanvas; virtual; abstract;
function GetPageNum: Integer; virtual; abstract;
procedure CheckPrinting(Value: Boolean);
procedure GetPrinterCapsOf(Printer: TPrinter);
procedure SetPrinting(Value: Boolean);
public
procedure BeginDoc; virtual; abstract;
procedure NewPage; virtual; abstract;
procedure EndDoc; virtual; abstract;
procedure Abort; virtual; abstract;
procedure Assign(Source: TPersistent); override;
property Canvas: TCanvas read GetCanvas;
property OffsetX: Integer read FOffsetX;
property OffsetY: Integer read FOffsetY;
property PageNumber: Integer read GetPageNum;
property PageHeight: Integer read FPgHeight;
property PageWidth: Integer read FPgWidth;
property PaperHeight: Integer read FPaperHeight;
property PaperWidth: Integer read FPaperWidth;
property PixelsPerInchX: Integer read FPPIX;
property PixelsPerInchY: Integer read FPPIY;
property Printing: Boolean read FPrinting; // becomes True in BeginDoc and back to False in EndDoc.
property Title: ThtString read FTitle write FTitle;
end;
becomes
ThtPrinter = class(TComponent)
protected
FOffsetX: Integer; // Physical Printable Area x margin
FOffsetY: Integer; // Physical Printable Area y margin
FPaperHeight: Integer; // Physical Height in device units
FPaperWidth: Integer; // Physical Width in device units
FPgHeight: Integer; // Vertical height in pixels
FPgWidth: Integer; // Horizontal width in pixels
FPPIX: Integer; // Logical pixels per inch in X
FPPIY: Integer; // Logical pixels per inch in Y
FPrinting: Boolean;
FTitle: ThtString; // Printed Document's Title
function GetCanvas: TCanvas; virtual; abstract;
function GetPageNum: Integer; virtual; abstract;
procedure CheckPrinting(Value: Boolean);
procedure GetPrinterCapsOf(Printer: TPrinter);
procedure SetPrinting(Value: Boolean);
public
procedure BeginDoc; virtual; abstract;
procedure NewPage; virtual; abstract;
procedure EndDoc; virtual; abstract;
procedure Abort; virtual; abstract;
procedure Assign(Source: TPersistent); override;
property Canvas: TCanvas read GetCanvas;
property OffsetX: Integer read FOffsetX;
property OffsetY: Integer read FOffsetY;
property PageNumber: Integer read GetPageNum;
property PageHeight: Integer read FPgHeight;
property PageWidth: Integer read FPgWidth;
property PaperHeight: Integer read FPaperHeight;
property PaperWidth: Integer read FPaperWidth;
property PixelsPerInchX: Integer read FPPIX;
property PixelsPerInchY: Integer read FPPIY;
property Printing: Boolean read FPrinting; // becomes True in BeginDoc and back to False in EndDoc.
property Title: ThtString read FTitle write FTitle;
end;
File: htmlview.pas
Search for the line:
function THtmlViewer.Print(Prn: ThtPrinter; FromPage: Integer; ToPage: Integer; Mode: ThtPrintPreviewMode): Integer;
Blow this you will find a case statement which reads:
case Mode of
ppAuto:
if not (Prn is TMetaFilePrinter) then
Mode := ppMultiPrint
else
Mode := ppPreview;
ppPreview:
if not (Prn is TMetaFilePrinter) then
raise EIllegalArgument.CreateFmt('Previewing a print requires a printer based on TMetaFilePrinter but not a %s', [Prn.ClassName]);
ppNoOutput:
if not (Prn is TMetaFilePrinter) then
raise EIllegalArgument.CreateFmt('Getting the total number of pages to print requires a printer based on TMetaFilePrinter but not a %s', [Prn.ClassName]);
end;
Change it to
case Mode of
ppAuto:
if not (Prn is ThtPrinter) then
Mode := ppMultiPrint
else
Mode := ppPreview;
ppPreview:
if not (Prn is ThtPrinter) then
raise EIllegalArgument.CreateFmt('Previewing a print requires a printer based on TMetaFilePrinter but not a %s', [Prn.ClassName]);
ppNoOutput:
if not (Prn is ThtPrinter) then
raise EIllegalArgument.CreateFmt('Getting the total number of pages to print requires a printer based on TMetaFilePrinter but not a %s', [Prn.ClassName]);
end;
This will allow anything derived from ThtPrinter to be used in this function.
I hope this helps people who need it.
The company I work for use HTMLView and SynPDF a lot on the projects we write for customers. As I come across issues and am able to fix them I'll post the code back. This is the first time I've been able to contribute something useful back to an open source project
Take care,
Ryan
Hi All,
It has been a while since I posted but wanted to contribute some code back. I have been using SynPDF with THTMLView for a while but hit an issue with tables and HTML header and footers. First HTMLView table borders, when using SynPDF 1.18 I didn't get any standard table borders. Having checked the EMF output from HTMLView I discovered the borders are drawn using EMR_FILLRGN. Here is the code to add to EnumEMFFunc to deal with this EMF record:
EMR_FILLRGN: begin
// Code Copied from EMR_SELECTOBJECT
if integer(PEMRFillRgn(R)^.ihBrush)<0 then begin // stock object?
num := PEMRFillRgn(R)^.ihBrush and $7fffffff;
case num of
NULL_BRUSH:
brush.null := true;
WHITE_BRUSH..BLACK_BRUSH: begin
brush.color := STOCKBRUSHCOLOR[num];
brush.null := false;
end;
NULL_PEN: begin
pen.style := PS_NULL;
pen.null := true;
end;
WHITE_PEN, BLACK_PEN: begin
pen.color := STOCKPENCOLOR[num];
pen.null := false;
end;
end;
end else
if PEMRFillRgn(R)^.ihBrush-1<cardinal(length(E.Obj)) then // avoid GPF
with E.Obj[PEMRFillRgn(R)^.ihBrush-1] do
case Kind of // ignore any invalid reference
OBJ_PEN: begin
if E.fInLined and
((pen.color<>PenColor) or (pen.width<>PenWidth) or
(pen.style<>PenStyle)) then begin
E.fInLined := False;
if not pen.null then
E.Canvas.Stroke;
end;
pen.null := (PenWidth<0) or (PenStyle = PS_NULL); // !! 0 means as thick as possible
pen.color := PenColor;
pen.width := PenWidth;
pen.style := PenStyle;
end;
OBJ_BRUSH: begin
brush.null := BrushNull;
brush.color := BrushColor;
brush.style := BrushStyle;
end;
OBJ_FONT: begin
font.spec := FontSpec;
move(LogFont,font.LogFont,sizeof(LogFont));
end;
end;
// New code to fill the region
E.FillRectangle(PRgnDataHeader(@PEMRFillRgn(R)^.RgnData[0])^.rcBound);
end;
The other issues I has was with converting the HTML to a PDF with HTML headers and footers. The sample code reference on this site, and others, make use of the THTMLView function MakePagedMetaFiles. Unfortunately this function doesn't support HTML headers and footers. To get around this I created my own Printer class which was derived from the ThtPrinter base class. With a small modification to HTMLView you can then pass this printer class into the standard Print function and get a PDF document with HTML headers and footers inserted correctly.
uses Forms, SysUtils, Classes, Graphics, vwPrint, SynPDF;
type
TPDFPrinter = class(ThtPrinter)
protected
PDFDocGDI : TPdfDocumentGDI;
function GetCanvas: TCanvas; override;
function GetPageNum: integer; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
// Printer Methods
procedure BeginDoc; override;
procedure NewPage; override;
procedure EndDoc; override;
procedure Abort; override;
end;
implementation
{ TPDFPrinter }
procedure TPDFPrinter.Abort;
begin
Self.SetPrinting(False);
end;
procedure TPDFPrinter.BeginDoc;
var
NewCanvas : TCanvas;
begin
Self.SetPrinting(True);
Self.PDFDocGDI.NewDoc;
Self.PDFDocGDI.AddPage;
NewCanvas := Self.GetCanvas;
// This code replaces GetPrinterCapsOf
FPPIX := Self.PDFDocGDI.ScreenLogPixels;
FPPIY := Self.PDFDocGDI.ScreenLogPixels;
FPaperWidth := Self.PDFDocGDI.VCLCanvasSize.cx;
FPaperHeight := Self.PDFDocGDI.VCLCanvasSize.cy;
FOffsetX := 0;
FOffsetY := 0;
FPgWidth := Self.PDFDocGDI.VCLCanvasSize.cx;
FPgHeight := Self.PDFDocGDI.VCLCanvasSize.cy;
end;
constructor TPDFPrinter.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
PDFDocGDI := TPdfDocumentGDI.Create;
end;
destructor TPDFPrinter.Destroy;
begin
FreeAndNil(PDFDocGDI);
inherited;
end;
procedure TPDFPrinter.EndDoc;
begin
Self.SetPrinting(False);
end;
function TPDFPrinter.GetCanvas: TCanvas;
begin
Result := Self.PDFDocGDI.VCLCanvas;
end;
function TPDFPrinter.GetPageNum: integer;
begin
Result := Self.PDFDocGDI.RawPages.Count;
end;
procedure TPDFPrinter.NewPage;
var
NewCanvas : TCanvas;
begin
Self.PDFDocGDI.AddPage;
NewCanvas := Self.GetCanvas;
NewCanvas.Brush.Color := clWhite;
NewCanvas.Pen.Color := clWhite;
NewCanvas.Brush.Style := bsSolid;
NewCanvas.Rectangle(0, 0, Self.PDFDocGDI.DefaultPageWidth, Self.PDFDocGDI.DefaultPageHeight);
NewCanvas.Font.PixelsPerInch := Screen.PixelsPerInch;
NewCanvas.Font.Name := 'Arial';
NewCanvas.Font.Size := 10;
NewCanvas.Brush.Style := bsClear;
end;
Then all you do is call HTMLPrinter.Print(PDFPrinter, 1, HTMLPrinter.NumPrinterPages, ppPreview); to generate your PDF
EMF processing is all new to me so please update my code as you see fit.
Take care,
Ryan
Hi Cohiba,
I don't know it anybody has been able to help you out. I've just made a post about connect the DevExpress printing system and SynPDF together. You can see the details here http://synopse.info/forum/viewtopic.php?id=1036. I hope it helps you out.
Take care,
Ryan
Hi All,
I have had the need to combine several jobs to produce a document pack from our application. This application takes data from both HTMLView and the DevExpress ExpressQuantumGrid Suite / DevExpress Printing system. I've been looking for a long time to find the best way to do this and I have now found it. Html2Pdf covers getting the data from HTML View. I hope to demonstrate one way of getting the data from the DevExpress printing system.
First the print code for DevExpress. The key here is to use the function EnumPagesAsImages. The first parameter is an array of integers indicating the pages you want to convert to an image. To keep things simple I do one page at a time. The second parameter determines the image type to use. As the SynPDF system works with well metafiles that is the graphic type I have used. The third determines if the background should be painted. The forth parameter is a call back function to receive the rendered image. Parameters 5, 6 & 7 are data pointers which I don't use.
var
PageCount: Integer;
begin
// Other printing preparation code goes here
// Force the printout to regenerate
Self.dxGridLink.RebuildReport;
// Copy each page to the PDF document
for PageCount := 0 To dxComponentPrinter1.GetPageCount - 1 do begin
dxComponentPrinter1.EnumPagesAsImages([PageCount], TMetaFile, False, PDFPrinter.AppendDevExpressGrid, nil, nil, nil);
end;
Now the PDF document class. This just simple code so far. I hope to improve it to scale the metafile so I can add my own borders and additional headers and footers.
type
TDevExpress2SynPDF = class(TPdfDocumentGDI)
private
public
constructor Create(AUseOutlines: Boolean=false; ACodePage: integer=0; APDFA1: boolean=false);
destructor Destroy; override;
procedure AppendDevExpressGrid(AComponentPrinter: TCustomdxComponentPrinter; AReportLink: TBasedxReportLink;
AIndex, APageIndex: Integer; const AGraphic: TGraphic;
AData: Pointer; var AContinue: Boolean);
end;
implementation
uses SynGdiPlus;
procedure TDevExpress2SynPDF.AppendDevExpressGrid(
AComponentPrinter: TCustomdxComponentPrinter; AReportLink: TBasedxReportLink;
AIndex, APageIndex: Integer; const AGraphic: TGraphic; AData: Pointer;
var AContinue: Boolean);
var
Emf : TMetafile;
R : TRect;
PdfPage : TPdfpage;
begin
if AGraphic is TMetaFile then begin
Emf := AGraphic as TMetaFile;
// Make this page landscape
PdfPage := Self.AddPage;
PdfPage.PageWidth := Self.DefaultPageHeight;
PdfPage.PageHeight := Self.DefaultPageWidth;
R.Left := 0;
R.Top := 0;
R.Bottom := Emf.Height;
R.Width := Emf.Width;
Gdip.DrawAntiAliased(Emf, Self.VCLCanvas.Handle, R, smAntiAlias, trhClearTypeGridFit);
end;
end;
constructor TDevExpress2SynPDF.Create(AUseOutlines: Boolean; ACodePage: integer;
APDFA1: boolean);
begin
inherited Create(AUseOutlines, ACodePage, APDFA1);
Gdip := TGDIPlusFull.Create;
end;
destructor TDevExpress2SynPDF.Destroy;
begin
FreeAndNil(Gdip);
inherited;
end;
I use the Gdip.DrawAntiAliased as the normal metafile SynPDF functions produce either a black and white image with some backgrounds and lines missing or the PDF file is blank.
I hope this code helps somebody out but please post comments, suggestions or improvements to my code.
Take care,
Ryan
Hi All,
Just following up on my post from yesterday. I've been able to render the metafile to PDF using the following code:
const
OutputFileName = 'schedule.pdf';
InputFileName = 'schedule.emf';
var
PdfDoc : TPDFDocumentGDI;
PdfPage : TPdfpage;
Emf : TMetafile;
R : TRect;
begin
Emf := TMetafile.Create();
Emf.LoadFromFile(InputFileName);
Gdip := TGDIPlusFull.Create;
PdfDoc := TPdfDocumentGDI.Create;
PdfDoc.CompressionMethod := cmFlateDecode;
PdfDoc.DefaultPaperSize := psA4;
PdfDoc.DefaultPageLandscape := True;
PdfPage := PdfDoc.AddPage();
R.Left := 0;
R.Top := 0;
R.Bottom := Emf.Height;
R.Width := Emf.Width;
Gdip.DrawAntiAliased(Emf, PdfDoc.VCLCanvas.Handle, R, smAntiAlias, trhClearTypeGridFit);
PdfDoc.SaveToFile(OutputFileName);
PdfDoc.Free();
FreeAndNil(Emf);
end.
This produces a complete colour copy of the PDF document. The metafile has come from the DevExpress printing system. I'll post some code in another thread showing how to get the output from this into your PDF document.
Take care,
Ryan
Apologies for that it looks like our server won't accept EMF files. I've zipped it up and it can be downloaded from:
Hi All,
I've come across this library and it is doing everything I'm asking of it so far. I've hit a small snag though. This metafile http://www.lmp.co.uk/files/schedule.emf is causing me some headaches. I create a small test program for this metafile and I'm confused with the output.
const
OutputFileName = 'schedule.pdf';
InputFileName = 'schedule.emf';
var
PdfDoc : TPDFDocumentGDI;
PdfPage : TPdfpage;
Emf : TMetafile;
begin
Emf := TMetafile.Create();
Emf.LoadFromFile(InputFileName);
PdfDoc := TPdfDocumentGDI.Create;
PdfDoc.CompressionMethod := cmFlateDecode;
PdfDoc.DefaultPaperSize := psA4;
PdfDoc.DefaultPageLandscape := True;
PdfPage := PdfDoc.AddPage();
//PdfDoc.VCLCanvas.Draw(0, 0, Emf); // This gives black and white output only
//PdfDoc.Canvas.RenderMetaFile(Emf, 1); // This gives a blank PDF
PdfDoc.SaveToFile(OutputFileName);
PdfDoc.Free();
FreeAndNil(Emf);
end.
When run with PdfDoc.VCLCanvas.Draw uncommented I get a black and white output. Some lines are also missing. When PdfDoc.Canvas.RenderMetaFile is uncommented I get a blank PDF. Am I missing something obvious?
Looking though the forum somebody recommended looking at EMFExplorer to see if the meta file had some odd coding in it. I've done this and as long as I switch off GDI+ rendering then is shows OK. Look through the EMF data it appears all calls are supported in the PDF engine except EMR_SETROP2. I'm just trying to work out what this does and implement it if necessary .... although this isn't my area of expertise and might take some time
Many thanks for any help.
Ryan
Pages: 1