#1 2019-02-10 17:59:46

EvaF
Member
Registered: 2014-07-19
Posts: 40

PDF canvas

I - like many others - use  FastReport for my reports but the exported PDF files - when pictures are embedded into them - look really bad.
Therefore I have decided to rework all my reports to use SynPDF.

A short introduction:
I have two webservice applications (both based on mORMot) that communicate with each other. One of them generates the picture files (*.png) which are inserted into reports by the second application.

All this can be handled by SynPDF alone. Instead of png files I plan to  use the windows metafiles(*.emf) which the second application will render into resulting PDF pages.

I have tried to render my generated emf file and stumbled on some small issues in SynPDF that I would like to have fixed:

They are related to Canvas.SaveDC and Canvas.RestoreDC

 procedure TPdfEnum.RestoreDC;
begin
  Assert(nDC>0);
  dec(nDC);
  Canvas.GRestore;         // <---   imho this line has to be added
end;

procedure TPdfEnum.SaveDC;
begin
  Assert(nDC<high(DC));
  DC[nDC+1] := DC[nDC];
  inc(nDC);
  Canvas.GSave;               // <---  imho this line has  to be added
end;

  and to clipping - I have already read this topic from MtwStark https://synopse.info/forum/viewtopic.php?id=4276 , but I was not digging so deeply and not to such extent as he.
I was focused only on the  EMR_SELECTCLIPPATH that is a part of my generated pictures - therefore my suggested change affects only the EMR_SELECTCLIPPATH.

function EnumEMFFunc(DC: HDC; var Table: THandleTable; R: PEnhMetaRecord;
   NumObjects: DWord; E: TPdfEnum): LongBool; stdcall;
....

 EMR_SELECTCLIPPATH:begin                                  // <---   added  EMF_record EMR_SELECTCLIPPATH
    if PolyFillMode = ALTERNATE then
      E.Canvas.EOClip
    else
      E.Canvas.Clip;
    E.Canvas.NewPath;
  end;
  EMR_BEGINPATH: begin
    E.InPath := true;                                         // <--- added a path bracket flag  := true; 
    E.Canvas.NewPath;
    if not Moved then begin
      E.Canvas.MoveToI(Position.X,Position.Y);
      Moved := true;
    end;
  end;
  EMR_ENDPATH:
  begin
    E.InPath := false;                                        // <--- added a path bracket flag  := false
    E.Canvas.fNewPath := false;
  end;

When the InPath flag is true, the path defined in path brackets (EMR_BEGINPATH .. EMR_ENDPATH) should  not be Filled nor Stroked

procedure TPdfEnum.NeedBrushAndPen;
begin
  if InPath then                          // <--- added a condition  
  begin
    fStrokeColor := -1;
    fPenWidth := -1;
    fFillColor := -1;
    fPenStyle := -1;
  end
  else begin
    if fInlined then begin
      fInlined := false;
      Canvas.Stroke;
    end;
    NeedPen;
    with DC[nDC] do
    if not brush.null then
      FillColor := brush.color;
  end;
end;

procedure TPdfEnum.NeedPen;
begin
  with DC[nDC] do
  if not pen.null and not InPath then begin                  // <--- added a condition not InPath
    StrokeColor := pen.color;
    ...
  end else begin
    // pen.null need reset values
    fStrokeColor := -1;
    fPenWidth := -1;
    fPenStyle := -1;
  end;
end;

and in EnumEMFFunc procedure an PolyPoly procedure

function EnumEMFFunc(DC: HDC; var Table: THandleTable; R: PEnhMetaRecord;
   NumObjects: DWord; E: TPdfEnum): LongBool; stdcall;
   ... 
  EMR_POLYGON, EMR_POLYLINE, EMR_POLYGON16, EMR_POLYLINE16:
  if not brush.null or not pen.null and not E.InPath then begin           // <--- added a condition not InPath
  ...
end;

procedure TPdfEnum.PolyPoly(data: PEMRPolyPolygon; iType: Integer);
...
  if not InPath then                                          // <--- added a condition not InPath
  begin
     if iType in [EMR_POLYPOLYLINE, EMR_POLYPOLYLINE16] then begin // stroke
     ...
  end;
end;

With these small changes applied  my generated emf's are rendered correctly

Last edited by EvaF (2019-02-11 12:46:50)

Offline

#2 2019-02-11 22:47:32

EvaF
Member
Registered: 2014-07-19
Posts: 40

Re: PDF canvas

I am continuing in the study of synPDF and mORMotReport and I have found out, that in some cases (when the font of pageNrs is not diffferent from the font of header (or footer))
then Adobe Acrobat notifies the error in generated PDF.

It can be fixed by swapping order of page draw and PageNr text:

procedure TGDIPages.EndDoc;
...
 for i := 0 to n-1 do begin
      Page := CreateMetaFile(fPages[i].SizePx.X,fPages[i].SizePx.Y);
      try
        fCanvas := CreateMetafileCanvas(Page);
//-     fCanvas.Draw(0,0,GetMetaFileForPage(i)); // re-draw the original page
        s := format(fPagesToFooterText,[i+1,n]); // add 'Page #/#' caption
        aX := fPagesToFooterAt.X;
        if aX<0 then
          aX := fPages[i].SizePx.X-fPages[i].MarginPx.Right;
        SavedState := fPagesToFooterState;
        if TextAlign=taRight then
          dec(aX,fCanvas.TextWidth(s));
        with fPages[i] do
          fCanvas.TextOut(aX,SizePx.Y-MarginPx.bottom-fFooterHeight+   
            fFooterGap+fPagesToFooterAt.Y,s);
        fCanvas.Draw(0,0,GetMetaFileForPage(i));                                     //+ <-- swapping an order of commands 
        FreeAndNil(fCanvas);
        SetMetaFileForPage(i,Page); // replace page content
      finally
        Page.Free;
      end;
   ...

Offline

Board footer

Powered by FluxBB