You are not logged in.
Pages: 1
Thanks, that's exactly what I was hoping for. I can now remove my workaround.
Thanks for you help. I've not got my N pages per sheet PDF generation complete.
I found an oddity in code where I accessed VCLCanvasSize before VCLCanvas. Since the VCLCanvas is lazily created on demand, this scenario leads to GetVCLCanvasSize accessing fVCLCanvasSize before it has been initialised. I worked around this by making sure that I access VCLCanvas before VCLCanvasSize, but I think that this protection should be implemented inside SynPdf.
I'm actually mixing metafiles and raster images. For the former, I can, as you say, use TPdfDocumentGDI and render the metafile to the VCL canvas. That seems to have no problems with multiple metafiles. For my raster images, I think it is better to use AddXObject and DrawXObject because that embeds the image in the document, which can be stretched appropriately by the PDF render process. A bit of a fankle, but I think I can make it work!
Isn't this a bug though. Why would it be possible to use RenderMetafile multiple times, apart from when there is text? It feels like the metafile enumeration code is failing to handle text properly.
OK, here's a simple reproduction:
program RenderMetafile;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Classes,
Winapi.Windows,
Vcl.Graphics,
SynCommons in 'SynCommons.pas',
SynLZ in 'SynLZ.pas',
SynPdf in 'SynPdf.pas';
var
Doc: TPdfDocument;
Page: TPdfPage;
Metafile: TMetafile;
Canvas: TMetafileCanvas;
i: Integer;
begin
Metafile := TMetafile.Create;
Metafile.SetSize(200, 200);
Canvas := TMetafileCanvas.Create(Metafile, GetDC(0));
Canvas.TextOut(10, 10, 'SynPDF is the best!');
Canvas.Free;
Doc := TPdfDocument.Create;
Doc.DefaultPaperSize := psA4;
Page := Doc.AddPage;
Page.PageLandscape := False;
for i := 1 to 20 do begin
Doc.Canvas.RenderMetaFile(Metafile, 1.0, 0.0, 20.0, i*20.0);
end;
Doc.SaveToFile('test.pdf');
Doc.Free;
end.
There should be 20 instances of the text of the page, but there is just the first one. If instead of the TextOut, I make a call to MoveTo/LineTo, then multiple instances of the line appear.
I'm not sure what you mean by "the main metafile". Anyway, I'm working on a repro. No point doing any more until I have something concrete.
Hmm, I think I'm going to have to make a small reproduction. The issue seems perhaps to be related to the inclusion of text in the metafile. Without that, behaviour is fine. Let me put together a reproduction.
I'm trying to implement a "multiple pages per sheet" feature for my PDF output. I'm currently rendering a single metafile to each page of the PDF document, calling TPdfCanvas.RenderMetaFile to achieve that. It works a treat.
However, I'm not trying to call this function multiple times on the same page with no luck. I'm seeing nothing at all in the generated PDF, or sometimes just a couple of parts of one of the metafiles. But certainly not multiple metafile graphics around the page.
Before I carry on, is this something that I can expect to do, or do I need to find another approach? I know that I can create my own metafile canvas, render my metafiles to that canvas, and finally render the composite metafile to the PDF document with a single call to TPdfCanvas.RenderMetaFile. I was hoping to avoid having to use that extra metafile to prepare my pages.
If I should be able to call TPdfCanvas.RenderMetaFile multiple times per page, and you'd like to dig into this, I'll prepare a reproduction of the issue. I've not done that yet because it would take a little while to extract it from my complex application code. And I didn't want to spend that time if the answer was a simple, "this can never work, do it a different way".
When compiling for x64 I see this warning:
SynCommons.pas(28604): warning W1029: Duplicate constructor 'TFileBufferWriter.CreateInRawByteStringStream' with identical parameters will be inacessible from C++
I don't see the warning under x86.
I'm personally not concerned by this warning, but I expect that you'll find to find a way to keep the compiler happy so that code can be compiled warning free.
Thanks Arnaud, that sorts the problem out and I can now remove my workaround.
Thanks! I'll check this all out tomorrow.
Consider the following program:
program PdfTest;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Classes,
Winapi.Windows,
Vcl.Graphics,
SynCommons in 'Synopse\SynCommons.pas',
SynLZ in 'Synopse\SynLZ.pas',
SynPdf in 'Synopse\SynPdf.pas';
var
Doc: TPdfDocument;
Page: TPdfPage;
Metafile: TMetafile;
Canvas: TMetafileCanvas;
begin
// create test metafile
Metafile := TMetafile.Create;
Metafile.SetSize(700, 700);
Canvas := TMetafileCanvas.Create(Metafile, GetDC(0));
Canvas.MoveTo(0, 0);
Canvas.LineTo(700, 700);
Canvas.MoveTo(0, 700);
Canvas.LineTo(700, 0);
Canvas.Free;
// create page in portrait orientation, and render metafile to it
Doc := TPdfDocument.Create;
Doc.DefaultPaperSize := psA4;
Page := Doc.AddPage;
Page.PageLandscape := False;
Doc.Canvas.RenderMetaFile(Metafile);
Doc.SaveToFile('_portrait.pdf');
// same again, but with landscape orientation
Doc := TPdfDocument.Create;
Doc.DefaultPaperSize := psA4;
Page := Doc.AddPage;
Page.PageLandscape := True;
Doc.Canvas.RenderMetaFile(Metafile);
Doc.SaveToFile('_landscape.pdf');
end.
This emits two PDF files that are meant to contain a simple cross. The portrait orientation version comes out correctly. The landscape orientation does not.
I tracked this down to the fact that TPdfCanvas.SetPage is called from AddPage. And this caches the height of the the page:
procedure TPdfCanvas.SetPage(APage: TPdfPage);
begin
FPage := APage;
FPageFontList := FPage.GetResources('Font');
FContents := TPdfStream(FPage.ValueByName('Contents'));
FHeight := FPage.GetPageHeight; <----- UH OH!
FFactor := 72/FDoc.FScreenLogPixels; // PDF expect 72 pixels per inch
end;
Then when we subsequently change orientation, the page height is changed, but FHeight is not updated.
I guess I can solve my problem by throwing in a call to TPdfCanvas.SetPage immediately before I call RenderMetafile. Is that what I am supposed to do? Is there a better way to solve my problem? Is the library behaving as designed? Do you mean to cache FPage.GetPageHeight and then not update it later?
Thanks!
Yes, it is very weird. I've not yet isolated the fault into an SSCCE so that a QC report can be logged. Or perhaps there already is one.
Anyway, thanks for listening and understanding. As for the naming of a conditional, I won't make a suggestion. You know your users better than anyone and I'm sure you'll come up with a sensible name that follows your existing conventions and naming patterns.
Yes, I know I can comment this out. But if Synopse.inc breaks RTTI in my project, for sure it will affect others. Anyway, I can work around it, but you may wish to dig a bit deeper for the benefit of your other users.
I recently discovered that Synopse.inc has this code:
{$WEAKLINKRTTI ON}
{$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
I don't understand why, but for 64 bit targets only, this means that any DUnit projects that include a unit that includes Synopse.inc, fail to enumerate test methods. I'm building with XE3.
It seems to me that these options should not be specified in the Synopse.inc file. The appropriate place for them is in the .dpr file. Since they have an impact on the entire project, it feels wrong that they appear in Synopse.inc.
Thanks again. I'm sure it will work out!!
Thank you very much for that. Much appreciated.
My next step is to try drawing a metafile onto a PDF doc that is using SaveToStreamDirectBegin. I'm hoping that will work. I'll get back to you if I have problems, but since I'm moving house at the moment, it may be a little while.
OK, that does a lovely job solving my problem. Thank you kindly.
But the SynPdf unit does not compile at the moment when USE_PDFSECURITY is not defined.
I'm having problems trying to use SaveToStreamDirectBegin etc. with VCLCanvas and metafiles. For example, here's a simple demo program that produces an empty PDF file. You'll recognise the code since I took it from here: http://blog.synopse.info/post/2010/05/0 … PDF-engine
If I switch from SaveToStreamDirectBegin to SaveToFile then the generated PDF file is as expected.
I'm using your code from this revision: http://synopse.info/fossil/vinfo?name=d … 0ec68b709b
Can I expect SaveToStreamDirectBegin to work in this setting, or should I give up on it?
program SaveToStreamDirectBeginQuery;
{$APPTYPE CONSOLE}
uses
SysUtils, Classes, Graphics, SynPdf;
procedure MakePDF(const FileName: string);
var
i: Integer;
Stream: TStream;
begin
with TPdfDocumentGDI.Create do
try
Stream := TFileStream.Create(FileName, fmCreate);
try
SaveToStreamDirectBegin(Stream);
for i := 1 to 9 do
begin
AddPage;
with VCLCanvas do
begin
Font.Name := 'Times new roman';
Font.Size := 150;
Font.Style := [fsBold, fsItalic];
Font.Color := clNavy;
TextOut(100, 100, 'Page ' + IntToStr(i));
end;
SaveToStreamDirectPageFlush;
end;
SaveToStreamDirectEnd;
finally
Stream.Free;
end;
finally
Free;
end;
end;
begin
MakePDF('C:\desktop\test.pdf');
end.
Thanks for that. I fully understand.
If your policy is to enable assertions in all builds, then shouldn't that be enforced in your Synopse.inc file? That would then mean you could avoid the messy $ifopt.
I'm trying out SynPdf which on the face of it is truly wonderful. Very well done indeed!
The compiler spits out a couple of hints when I compile SynPdf. These hints only appear when compiling with assertions disabled, i.e. $C- mode.
[dcc32 Hint] SynLZ.pas(1087): H2077 Value assigned to 'dst_beg' never used
[dcc32 Hint] SynCommons.pas(8491): H2077 Value assigned to 'PW' never used
The hints are produced in each case because a value assigned to these variables is only referred to in an assertion. I checked out revision 59eb20700d.
Pages: 1