#1 Re: PDF Engine » Clipping problem after image » Yesterday 20:30:40

rvk

Clipping works perfectly in synpdf with an image in TJvRichedit.
I use the TPdfDocumentGDI and it enumerates perfectly through the EMR_STRETCHDIBITS for the image.

After that the richedit/Windows does the clipping for transparency through EMR_EXTSELECTCLIPRGN. That also works perfectly for my hand signature as image in the richedit.

It's just that any text after it is also clipped.
Or seems like the TPdfEnum.ExtSelectClipRgn leaves the clipping in a wrong state.

But I'll investigate this further. I have a feeling it's just a minor bug which can be fixed really easy but I was hoping you would have some more expertise as to how that newpath worked and would spot the bug more easily. But I'll get into it and report back when I find something.

After my last pull request the clipping works very good, at least for the RGN_COPY part. It just that the clipping seems to be left in a wrong state (on?).

#2 Re: PDF Engine » Clipping problem after image » Yesterday 17:31:31

rvk

Argh. I do need the NewPath line there. Otherwise the clipping mechanisme doesn't work for my other part of the PDF (for my signature where the clipping needs to be on another background pdf with pdftk).

So the question remains... why does the the clipping continue after an image (as shown in Image 2)?

You can load the meta.emf in emfexplorer.exe and see what happens there.
Also... rendering it on screen works correctly.
It's just creating a PDF from it, it hides everything after the image.

#3 PDF Engine » Clipping problem after image » Yesterday 17:21:49

rvk
Replies: 3

I have a clipping problem in the resulting PDF after rendering an image (GDI).
I have a TJvRichEdit with an image. Everything after the image is clipped away. It is still there (I can select it in Adobe reader and copy it to a text-editor) but it seems to be clipped away.

I can create a meta file in which the problem isn't present.
Loading the emf in a TMetaFile and rendering it to TPdfDocumentGDI with RenderMetaFile() the problem is that everything after the image doesn't show.

The EMR_STRETCHDIBITS is done correctly. But directly after that is a small EMR_EXTSELECTCLIPRGN block, which doesn't seem to close the clipping path correctly (??)

I can trace the problem to TPdfEnum.ExtSelectClipRgn.

All the way at the bottom there, there is the following.

    Canvas.Closepath;
    Canvas.Clip;
    Canvas.NewPath; // <----
    Canvas.FNewPath := false;

If I comment out the Canvas.NewPath there, it works correctly (see first image).
I'm not familiar with the NewPath and FNewPath mechanisme but I'm guessing there goes something wrong at that point (second image).
So the problem is that I don't know if I can leave that NewPath out, without causing other problems.
(and why is it causing the problem)

Code:

function ExecAssociatedApp(const FileName: string; Action: string = ''): boolean;
var
  rs: Word;
  Shw: integer;
begin
  Shw := SW_SHOW;
  rs := Winapi.ShellAPI.ShellExecute(0, pchar(Action), pchar(FileName), nil, nil, Shw);
  Result := rs > 32;
end;

procedure TestClip;
var
  FileTemp: string;
  meta: TMetaFile;
  pdf: TPdfDocumentGDI;
begin
  FileTemp := 'C:\Temp\Test1.pdf';
  meta := TMetaFile.Create;
  pdf := TPdfDocumentGDI.Create;
  try
    pdf.UseOptionalContent := true;
    pdf.Info.Title := 'Title name';
    pdf.Info.Author := 'User name';
    pdf.Info.Creator := 'Software name';
    pdf.Info.subject := 'Auto generated document';

    { page := } pdf.AddPage;

    meta.LoadFromFile('c:\temp\meta.emf');
    RenderMetaFile(pdf.Canvas, meta);

    pdf.SaveToFile(FileTemp);
    ExecAssociatedApp(FileTemp);

  finally
    meta.Free;
  end;
end;

Metafile (temporarily)
https://www.graficalc.nl/meta.emf

Correct (with the NewPath commented out):
2024-04-23-19-02-58-Test1-pdf.png

Incorrect (original code, with NewPath intact):
2024-04-23-19-04-10-Test1-pdf.png

#4 Re: PDF Engine » TGDIPages.AppendRichEdit() doesn't import some PNG images » Yesterday 16:55:19

rvk

I'm not sure about Delphi 11 (I'm still on 10.2) but TJvRichEdit supports images. They have Ole callback stuff which is probably needed to render the images to the metastream.

(I currently do have a small problem with images, not showing text after an image, but I'll open a new topic for that)

#5 Re: PDF Engine » PDF Info (title etc) not saved in PDF version >1.3 » 2023-12-28 16:08:11

rvk
padule wrote:

Is this normal?

Works fine for me (with latest mORMot 2 sources).

What version of code are you using?
(otherwise a small sample would be helpful)

This works for me (generates a 1.5 PDF):

procedure MakePdfSynPdf;
var
  FileTemp: string;
  pdf: TPdfDocumentGDI;
begin
  FileTemp := 'C:\Temp\Test1.pdf';
  pdf := TPdfDocumentGDI.Create;
  try
    pdf.UseOptionalContent := true;
    pdf.Info.Title := 'Title name';
    pdf.Info.Author := 'User name';
    pdf.Info.Creator := 'Software name';
    pdf.Info.subject := 'Auto generated document';

    // page 1
    { page := } pdf.AddPage;
    pdf.VCLCanvas.Font.Name := 'Times New Roman';
    pdf.VCLCanvas.Font.size := 24;
    pdf.VCLCanvas.TextOut(100, 100, 'some text on page 1');

    // page 2
    { page := } pdf.AddPage;
    pdf.VCLCanvas.Font.Name := 'Times New Roman';
    pdf.VCLCanvas.Font.size := 24;
    pdf.VCLCanvas.TextOut(100, 100, 'some text on page 2');

    // back to page 1
    pdf.Canvas.SetPage(pdf.RawPages[0]);
    pdf.VCLCanvas.Font.Name := 'Times New Roman';
    pdf.VCLCanvas.Font.size := 24;
    pdf.VCLCanvas.TextOut(200, 200, 'some extra text on page 1');

    pdf.SaveToFile(FileTemp);

    ExecAssociatedApp(FileTemp);

  finally
      pdf.Free;
  end;

end;

#6 Re: PDF Engine » Compile SynPdf error » 2023-11-22 20:55:46

rvk
ab wrote:

You are right, mormot2ui does not install any component - there is no component installed with Lazarus packages anway IIRC.

No, components are installed via packages in Lazarus. Same as with Delphi. Only when you click Install the whole IDE (lazarus) gets re-compiled.

Now the mormot2ui package is made as an install (design-time) package which recompiles the IDE while this shouldn't be necessary because there is no components installed. So it can just be a non-design package (same as the mormot2).

It does work as it is now but "Install" wouldn't be needed I think.
Unless components are added.

BTW. In Delphi only the path is added to the project after which everything gets compiled automatically. This could also be done the same way with Lazarus or FPC. But a package could add that path automatically which is easier. But I think the package could also just contain all the needed paths without adding all the files specifically (but somehow that doesn't work correctly now). I will look into that.

ab wrote:

Note that mormot.ui.pdf would work on Windows only, as documented in the unit.

Ah, yeah, I didn't look at that. I think FPC has its own fcl-pdf package anyway.

#7 Re: PDF Engine » Compile SynPdf error » 2023-11-22 19:34:04

rvk
ab wrote:

At least the mORMot 2 unit (should) compile with FPC and Windows.

You probably just want the complete mORMot 2 package. Compiling just the mormot.ui.pdf.pas will pull in everything else anyway.

Compiling mORMot 2 on Lazarus/FPC trunk (from 2 months ago) works fine.

I only had to include mormot.ui.pdf.pas and mormot.lib.uniscribe as files in my console project because they are not in the mormot2ui.lpk.
(mormot.ui.report.pas is missing there too)

I did get this message during install of mormot2ui.lpk.

Suspicious include path
The package mormot2ui 2.0.1 adds the path "$(PkgIncPath)" to the include path of the IDE.
This is probably a misconfiguration of the package.

It probably doesn't add the path to the include path.
The path is in the package but somehow it doesn't get included automatically to the project when adding mormot2ui as requirement.

Manually setting "mORMot2\src\ui;mORMot2\src\lib" as "Other unit files" in projects options works too.

After that, creating a PDF with TPdfDocumentGDI isn't a problem.

Can't speak about the rest of mORMot 2 wink

But overall, using mormot.ui.pdf shouldn't be too hard on Lazarus (Windows, I didn't test Linux).

BTW. mormot2ui should install designtime components, shouldn't it?
I don't think it does. It does have the "Install" button but I don't think it really installs anything.
(or I'm just looking in the wrong place)

Edit: Ah, this probably answers it wink

// do-nothing-unit on non Delphi + Windows system
// = not yet compatible with FPC/LCL due to a lot of Windowsims and VCLisms :(
procedure Register;
begin
end;

#8 Re: PDF Engine » Compile SynPdf error » 2023-11-22 13:58:38

rvk

@loadymf. In addition... you might want to try mORMot 2 which includes src/ui/mormot.ui.pdf.pas with the latest changes to the PDF engine. It might give you more luck.

#9 Re: PDF Engine » PDF:AddXObject is slow and out of memory! » 2023-10-15 13:38:12

rvk
Flashcqxg wrote:

I test with the TPdfDocumentGdi , and set the i from 1 to 70000,it out of memory!

With the latest version of mormot2.ui.pdf your code works fine for me (in Delphi 10.2 and 32 bit app) and results in a pdf of 25MB with 70.000 pages.
So you might want to use the latest version (the mormot2 one, not the synpdf one).

#10 Re: PDF Engine » Access violation in Drawbitmap in PDF engine with small bitmaps » 2023-09-09 21:01:15

rvk

Wait??? Doesn't this line read bytes BEFORE the data structure if len is 12????

movups  xmm1, dqword ptr [data + len - 16] // no read after end of page

data is the first pointer, len is 12 so data + len - 16 is going to do data - 4 ?

So using a array of less than 128 bits (16 bytes) is really dangerous with this function wink

(that would be any bitmap with width < 6 pixels for 24bit pixels which seems to be consistent with my experience)

~~
I tried to test with this but I can't fool it.

var
  a: array[1..1] of byte;
  hash: THash128Rec;
begin
  DefaultHasher128(@hash, @a, 12);

You think that should give an AV.

~~
There would not be an immediate need to fix this, but we should be aware of it smile

(or a check could be added in drawbitmap and any other place where this hash is used)

And if someone needs a quick "fix".... this works too (just using another hasher128) wink

  DefaultHasher128 := @crc32c128;

#11 Re: PDF Engine » Access violation in Drawbitmap in PDF engine with small bitmaps » 2023-09-09 20:43:35

rvk

Mmm, Weird.
According to this it should indeed be 12.

// Alignment must be a power of 2.  Color BMPs require DWORD alignment (32).
function BytesPerScanline(PixelsPerScanline, BitsPerPixel, Alignment: Longint): Longint;
begin
  Dec(Alignment);
  Result := ((PixelsPerScanline * BitsPerPixel) + Alignment) and not Alignment;
  Result := Result div 8;
end;

((3 * 24) + 31) and not 31 = 96 div 8 = 12

Then it must be further on in _AesNiHashXmm0 .

#12 Re: PDF Engine » Access violation in Drawbitmap in PDF engine with small bitmaps » 2023-09-09 20:28:31

rvk
ab wrote:

DefaultHasher128 = AesNiHash should not read more than the number of bytes specified.

What is the value of "row" integer in your case?

What is the exact GPF line in the AesNiHash asm?

Ok, then there is something going on with the row.

It's a 24bit bitmap (default)

row := PERROW[B.PixelFormat];    = 24 bits = 3 bytes

w = 3    ---> that should be 3 * 3 = 9 bytes per row ???

row := (((w * row) + 31) and (not 31)) shr 3; // inlined BytesPerScanLine

row = 12

for y := 0 to h - 1 do
  DefaultHasher128(@hash, B.{%H-}ScanLine[y], row);

So the row should be 9 bytes for a 3 pixel bitmap, wouldn't it?

The row value is 12. Seems 3 bytes too much smile


Crashed line is in _AesNiHashXmm0 for 64 bit.
But if row (len) is indeed 12 for a 3 pixel bitmap then that seems logical that it crashes there:

        movups  xmm1, dqword ptr [data + len - 16] // no read after end of page

I don't know if the scanline is supposed to be aligned at 32 bit but seeing that access violation it seems not.
(the access violation is always on the last scanline)

#13 PDF Engine » Access violation in Drawbitmap in PDF engine with small bitmaps » 2023-09-09 19:42:57

rvk
Replies: 4

I'm trying to find out why sometimes the DrawBitmap crashes with an exception.
BTW. The exceptions are handled by the SaveToFile code but it still leaks a TPDFWriter, but that's not the topic here/

The code crashes on this line in TPdfDocument.CreateOrGetImage for bitmaps smaller than 5 pixels:

for y := 0 to h - 1 do
   DefaultHasher128(@hash, B.{%H-}ScanLine[y], row);

I was originally doing this in Win32 and there it was really unpredictable. One run ok, other run crash.
When switching to Win64 I could let it crash almost consistently.

My concrete question is... is for that DefaultHasher128 function a bitmap needed with a width bigger or multiple of 4 bytes or 128 bits or something??
If yes, the code should check for that.

Below the code to reproduce (with just a blank bitmap).

Crashes almost consistently with Win64 in mormot.crypt.core.asmx86.inc (_AesNiHashXmm0) and in Win32 sometimes/unpredictable in mormot.crypt.core.asmx86.inc (_AesNiHash128).
(The access violation is handled by exception but results in a PDF with size 0.)

program test_pdf_small;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, System.Classes, mormot.ui.pdf, Vcl.Graphics, WinApi.Windows, ShellApi, System.IOUtils;

function GetFileSize(const FileName : string) : Int64;
var
  Reader: TFileStream;
begin
  Reader := TFile.OpenRead(FileName);
  try
    result := Reader.Size;
  finally
    Reader.Free;
  end;
end;

var
  pdf: TPdfDocumentGDI;
  MemBmp: Vcl.Graphics.TBitmap;
  I: Integer;
begin
  for I := 1 to 100 do
  begin

    pdf := TPdfDocumentGDI.Create;
    try
      pdf.DefaultPageWidth := 30;
      pdf.DefaultPageHeight := 30;
      pdf.AddPage;

      // create a very small bitmap and copy to canvas
      MemBmp := Vcl.Graphics.TBitmap.Create;
      try
        MemBmp.Width := 4;
        MemBmp.Height := 4;
        BitBlt(pdf.VclCanvas.Handle, 0, 0, MemBmp.Width, MemBmp.Height, MemBmp.Canvas.Handle, 0, 0, SRCCOPY);
      finally
          MemBmp.Free;
      end;

      // crashes here, also note the leak when savetofile has an silent exception
      pdf.SaveToFile('test.pdf');

    finally
        pdf.Free;
    end;

    if GetFileSize('test.pdf') = 0 then
      writeln('error on ' + I.ToString)
    else
      writeln('correct  ' + I.ToString);

  end;

  // ShellExecute(0, 'open', 'test.pdf', nil, nil, SW_NORMAL);

  writeln('done');
  readln;

end.

#14 Re: PDF Engine » CreateRegion and SelectClipRgn support in PDF (EMR_EXTSELECTCLIPRGN) » 2023-09-09 10:36:11

rvk

Thanks for the merge.

When looking for the word "clipping" here on the forum I came across this topic from MtwStark.
https://synopse.info/forum/viewtopic.php?id=4276

It seems (s)he made a lot (and I mean A LOT) of changes to SynPDF regarding the clipping-code.

I couldn't find a pull request (or even fixes on github).

I understand that those changes were a LOT to just merge with the existing code (and it wasn't even as fix for mORMot2) and was left behind because of this.

If I have any other fixes I'll try to keep them small and clearly understandable smile

#15 Re: PDF Engine » CreateRegion and SelectClipRgn support in PDF (EMR_EXTSELECTCLIPRGN) » 2023-09-08 18:46:04

rvk

I searched back to where these changes were reverted.
Here are some references (for documentation and futures sake).

Original commit in SynPDF and mORMot was on 2015-11-09
https://github.com/synopse/SynPDF/commi … 55924c4d9c
https://github.com/synopse/mORMot/commi … b4077dc725

The revert in mORMot was done here on 2017-01-13 (and SynPdf on 2017-01-18):
https://github.com/synopse/mORMot/commi … 215790822e
https://github.com/synopse/SynPDF/commi … 8388dc689a

It points to this topic: https://synopse.info/forum/viewtopic.php?pid=22968

The strange thing is that this is discussed earlier (in 2016) but then this change wouldn't have happened yet.
https://synopse.info/forum/viewtopic.ph … 071#p20071

On the commit on SynPDF there are some comments in 2022 where it's mentioned the code was good (but was never followed upon):
https://github.com/synopse/SynPDF/commi … a#comments
https://github.com/synopse/SynPDF/commi … #r84370305

I've created a pull request (my first wink ) with the changes (including the check on iMode = 5).
I also fixed the DrawBitmap() to add 1 for Bottom and Right (the bitmap was actually drawn too small).

"Reintroduce SelectClipRgn support for GDI+ and fix DrawBitmap()"

Hopefully nobody has problems with it (otherwise I'm willing to help, with a problem .emf, to find the problem).

#16 Re: PDF Engine » CreateRegion and SelectClipRgn support in PDF (EMR_EXTSELECTCLIPRGN) » 2023-09-08 16:09:13

rvk

Ok, here is another proof that the bitmap was drawn too small.

I added a FillRectangle of 1x1 at coordinates 0,0  1,1 and 4,6.

You see clearly that in the original drawing of bitmap with 2x4 pixels (drawn at 2,2), the lower right is not connected to the FillRectangle.

So, or all the BoxI() for FillRect etc. is not correct (but they seems to be places correctly) or the code for DrawBitmap is incorrect (too small).

After some further testing I will prepare a pull request for the ExtSelectClipRgn with the addition of corrected bottom/right in DrawBitmap.
(After that, when I get the time I'll see if I can tackle the EMR_SELECTCLIPPATH, which is completely missing now).

FdtqgnK.png

When corrected:
D8aEvBq.png

#17 Re: PDF Engine » CreateRegion and SelectClipRgn support in PDF (EMR_EXTSELECTCLIPRGN) » 2023-09-08 15:25:58

rvk

YES, PERFECT. I think I found a problem in the drawing of the DrawBitmap() in the PDF Engine.

For the old ExtSelectClipRgn code there was a correction of bottom (and I added right) of +1.
This was because in BoxI() there is I2Y(Rect.Bottom - 1) and I2X(Rect.Right - 1) in the correction to coordinates and size.

For the correct handling you would also need to do the Bottom + 1 and Right + 1 in the DrawBitmap() (same as in ExtSelectClipRgn).
So the bitmap is now drawn always 1 bitmap pixel too small (???).

Can you comment on why the BoxI() has the - 1 for Bottom and Right ??
There might be other places where this is a problem and because this is outside of the clipping problem I thought you might have some comments.

I can correct those before calling BoxI() in DrawBitmap() and ExtSelectClipRgn() and the outcome is perfect now:

      R := TRect(Rect(xd, yd, wd + xd, hd + yd));
      NormalizeRect(R);
      Inc(R.Bottom); // <----- these 2 lines
      Inc(R.Right);   // <----- these 2 lines
      box := BoxI(R, true);
      clp := GetClipRect;
      if (clp.Width > 0) and
         (clp.Height > 0) then
        Doc.CreateOrGetImage(bmp, @box, @clp) // use cliping
      else
        Doc.CreateOrGetImage(bmp, @box, nil);
      // Doc.CreateOrGetImage() will reuse any matching TPdfImage
      // don't send bmi and bits parameters here, because of StretchDIBits above

8MlFHSJ.png


47FiQoA.png

KhdSXMd.png

And this now seems pixel perfect with the EMF Explorer (which is this one):
UIg7UZO.png

#18 Re: PDF Engine » CreateRegion and SelectClipRgn support in PDF (EMR_EXTSELECTCLIPRGN) » 2023-09-08 14:22:15

rvk

Grrr. translating these coordinates is really hard.

Still not perfect.
It's also not just adding or substracting a value because the ratio/proportions are also off when using BoxI().
I can't figure out what other translation then BoxI() should be done.

The larger the image, the more deviation.

Hryy7y6.png

Vj3tsw8.png

(There is also a big bug in PDF Engine when dealing with bitmaps of size 7x7 (very small). It crashes, even without the clipping stuff.)

#19 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-08 10:54:31

rvk
padule wrote:

Sure, here is a simple example function (you can plug it into the example project I shared some time ago to test it with the various buttons for pdf, canvas, printer).

Thanks.

I see from the resulting EMF that this one uses EMR_SELECTCLIPPATH.
The other topic is about fixing EMR_EXTSELECTCLIPRGN (for regions).

If I look at the EnumEMFFunc (where all the GDI+ messages are handled), I don't see any EMR_SELECTCLIPPATH.
So, that clipping technique is not supported in the PDF Engine.

Possible clipping records in EMF:
https://learn.microsoft.com/en-us/opens … e1a82e3e03

As far as I can see there are some constants from EMF mention which do nothing.
But the below is a list of the constants which are not even mentioned in the case statement.
Unfortunately EMR_SELECTCLIPPATH is one of them wink

EMR_SETMAPPERFLAGS = $10;
EMR_SETCOLORADJUSTMENT = 23;
EMR_OFFSETCLIPRGN = 26;
EMR_EXCLUDECLIPRECT = 29;
EMR_SCALEVIEWPORTEXTEX = 31;
EMR_SCALEWINDOWEXTEX = 32;
EMR_ANGLEARC = 41;
EMR_EXTFLOODFILL = 53;
EMR_FLATTENPATH = 65;
EMR_WIDENPATH = 66;
EMR_SELECTCLIPPATH = 67;
EMR_FRAMERGN = 72;
EMR_INVERTRGN = 73;
EMR_PAINTRGN = 74;
EMR_MASKBLT = 78;
EMR_PLGBLT = 79;
EMR_SETDIBITSTODEVICE = 80;
EMR_CREATEMONOBRUSH = 93;
EMR_CREATEDIBPATTERNBRUSHPT = 94;
EMR_POLYTEXTOUTA = 96;
EMR_POLYTEXTOUTW = 97;
EMR_CREATECOLORSPACE = 99;
EMR_SETCOLORSPACE = 100;
EMR_DELETECOLORSPACE = 101;
EMR_GLSRECORD = 102;
EMR_GLSBOUNDEDRECORD = 103;
EMR_PIXELFORMAT = 104;
EMR_DRAWESCAPE = 105;
EMR_EXTESCAPE = 106;
EMR_STARTDOC = 107;
EMR_FORCEUFIMAPPING = 109;
EMR_NAMEDESCAPE = 110;
EMR_COLORCORRECTPALETTE = 111;
EMR_SETICMPROFILEA = 112;
EMR_SETICMPROFILEW = 113;
EMR_ALPHABLEND = 114;
EMR_TRANSPARENTDIB = 117;
EMR_SETLINKEDUFIS = 119;
EMR_SETTEXTJUSTIFICATION = 120;

#20 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-08 09:40:59

rvk
padule wrote:

We observed that the dashed lines are "simulated" by drawing many ellipses.

Yes, I saw that too when tinkering with the code for drawing.
But it is GDI+ that's passing on that info.

padule wrote:

I think the problem is that the clipping doesn't work correctly. The stroke should be clipped to the boundaries of the ellipses, so that the resulting line is the correct width. As the clipping doesn't work the result is a fatter line.

For clipping problem I opened a new topic:
https://synopse.info/forum/viewtopic.php?id=6694

I am about to create a pull request with a fix (but still need to learn how to do that).

But even with the fix for clipping, the dashed line is too big. There is no clipping involved in the dashed line from GDI+.
You can create an EMF file and examine it with emfexplorer (and then View>EMF as Text).
There is no clipping records there.

BTW. Can you share your code to create your clipping example. I would like to throw it though the clipping fix to see if that works correctly (especially at the edges).

#21 Re: PDF Engine » CreateRegion and SelectClipRgn support in PDF (EMR_EXTSELECTCLIPRGN) » 2023-09-07 16:26:32

rvk
ab wrote:

You can send a pull request with your patched code, I could make a look at it, and probably merge it.

BTW. Do you have some reference (link, documentation or description) for the problems many of those users had with the old clipping code?

(So I could test this and see if there is an alternative fix is this still exists in current code.)

#22 Re: PDF Engine » CreateRegion and SelectClipRgn support in PDF (EMR_EXTSELECTCLIPRGN) » 2023-09-07 15:32:19

rvk

The code is almost identical with the old code (with the check for iMode added).

But I will test this some more (we wouldn't want a bad patch wink ) and create a pull request with patch (once I know how to do that smile )

#23 Re: PDF Engine » CreateRegion and SelectClipRgn support in PDF (EMR_EXTSELECTCLIPRGN) » 2023-09-07 13:28:46

rvk

I couldn't find that many problems with the old code on the forum.

No problem... I added the old code back with the addition of checking for data^.iMode is RGN_COPY in the ExtSelectClipRgn (which was mentioned in that topic).

// we are handling RGN_COPY (5) only..
if data^.iMode <> RGN_COPY then exit;

For me that works for now. If there are any problems I will mention them here.

#24 Re: PDF Engine » CreateRegion and SelectClipRgn support in PDF (EMR_EXTSELECTCLIPRGN) » 2023-09-07 11:37:45

rvk

Hmmmm, Yeah, definitely not supported.

Size of region data from GDI is 208 bytes.

      EMR_EXTSELECTCLIPRGN:
      begin
        dum1 := PEMRExtSelectClipRgn(R)^.cbRgnData;        // <---- has a block of 208 bytes
        E.ExtSelectClipRgn(@PEMRExtSelectClipRgn(R)^.RgnData[0],
          PEMRExtSelectClipRgn(R)^.iMode);
      end;

But the function ExtSelectClipRgn() doesn't seem to do much with the data parameter??

procedure TPdfEnum.ExtSelectClipRgn(data: PRgnDataHeader; iMode: DWord);
begin
  try
    with DC[nDC] do
      case iMode of
        RGN_COPY:
          begin
            ClipRgn := MetaRgn; // where is the data parameter used??
            ClipRgnNull := false;
          end;
      end;
  except
    on e: Exception do
      ; // ignore any error (continue EMF enumeration)
  end;
end;

Any ideas?

Was this meant to be functional or is it still in development?

Did this work in the past ???
Seeing this post: https://synopse.info/forum/viewtopic.ph … 071#p20071

Edit:
The code from that old topic (which was once in SynPDF) seems to be working much better. (no anti-alias but that might be fixed.)
Why was that code removed?

q6cDhkt.png

#25 PDF Engine » CreateRegion and SelectClipRgn support in PDF (EMR_EXTSELECTCLIPRGN) » 2023-09-07 10:28:56

rvk
Replies: 16

I'm trying my hand again at the clipping problem when printing a bitmap in GDI+ with clipping.

Here are two images. One is from the the generated EMF and the other from the PDF generated with the latest mormot2.

It seems that clipping in PDF is only restricted to a box, and not to a complete non-square region.
Is this true?

Or is clipping even supported for EnumEMFFunc() for GDI+?

Seeing that ClipRgn and MetaRgn are TPdfBox it seems that it couldn't even do a region but only a box.

If it should be supported then I can investigate further why it doesn't work correctly.
(otherwise I would be wasting a lot of time wink )

EMF
FDiRYHN.png

PDF
vrxzbqU.png

#26 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-07 08:40:09

rvk

I did see some downsides of my hack above.
When using larger width for pen for dahses it doesn't work anymore for flat caps (probably because EMR_POLYBEZIERTO16 isn't used in that case).
I have a more 'stable' hack for you now. But you would need to have a special unique color for the dashes:

      EMR_EXTCREATEPEN: // approx. - fast solution
        with PEMRExtCreatePen(R)^ do
          if ihPen - 1 < cardinal(length(E.Obj)) then
            with E.obj[ihPen - 1] do
            begin
              kind := OBJ_PEN;
              PenColor := elp.elpColor;
              PenWidth := elp.elpWidth;
              PenStyle := elp.elpPenStyle and (PS_STYLE_MASK or PS_ENDCAP_MASK);

              // this doesn't work because we don't have dashes
              // if pen.style and PS_STYLE_MASK = PS_DASH then PenWidth := 0;

              if elp.elpColor = 0 { r } + 255 { g } shl 8 + 0 { b } shl 16 then PenWidth := 0; // your color for dashes

            end;

This will only set the pen width to 0 for when that color is used.
It seems a lot more stable (but is still an ugly hack).
It will also work for flat caps

(If anyone else has any other ideas about how to detect if we are in dashed lines ?)

Result:
(You'll notice the blue line creeps to the left but that's because of the round caps, and is according to specification, using the round cap before start of line).
rcbBiEd.png

BTW. This is the original where you see the problem of GDI+ clearly.
naYnUpV.png

And this was with flat caps:
CDXVYmK.png

You also notice the corners are slightly different from the EMF version
So that does seem to be a minor 'bug' in synpdf but when using pen=0 those corners are cut anyway.
But maybe that's the reason the 'bug' is there in GDI+ to provide those corners for flat caps smile
BOzEgz8.png

#27 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-06 21:00:38

rvk

BTW. I have a REALLY REALLY BAD hack for you.

      EMR_POLYBEZIERTO16:
        begin
          if not pen.null then
            E.NeedPen;
          E.Canvas.SetLineWidth(0); // <--- add this line
          if not E.Canvas.fNewPath then
            if not Moved then

It will generate this for the PDF.
tAjPTyK.png

But I'm sure this has many undesired side-effects smile

It will fix the bug in GDI+ with drawing the dashes with a pen width of 0 because GDI+ didn't calculatie the pen width when doing the dashes.

But there might be other drawings which need EMR_POLYBEZIERTO16 to be drawn with a pen with of > 0.
(I'm also sure this 'fix' can be improved upon, for example by setting the pen correctly so other drawings will reset the pen if needed.)

BTW. This still doesn't the dashes in native postscript but with simulated dashes, but then with a pen width of 0.

#28 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-06 20:27:30

rvk

Yes. I found more references stating that GDI+ and the enhanced meta canvas is inaccurate below a width of 1.5 or 2.
I'm still investigating the problem a bit but it is hard.

As I understand it now... the Microsoft Print to PDF uses a complete different method of using the GDI+ for printing.
You have GDI-based printing and XPS-based printing. Microsoft Print to PDF does the latter.

That's probably also the reason the dashed line is a path instead of actual dashed line.
I think XPS-based printing always uses the ellipse version which is badly rounded.
But they did fixed the bug that the TEnhancedMetaFile has (i.e. the bigger line width which the ellipses are drawn).
So we can forget about that one.

You also have several flavors of GDI-printing. You can print via the print spooler (which uses EMF format) or directly to the printer.
I'm not sure how I can force the EMF method to see if my printer then also has the same problem (setting the processor didn't hold).
I think the printer version prints directly (via the dll's) but I'm not sure about that and how.

This is for printing Microsoft Print to PDF and the XPS writer in Windows:

procedure TForm1.btnPrintClick(Sender: TObject);
begin
  // Printer.PrinterIndex := Printer.Printers.IndexOf('Microsoft Print to PDF');
  // shows as technology = http://schemas.microsoft.com/xps/2005/06

  Printer.PrinterIndex := Printer.Printers.IndexOf('Postscript');
  // shows as technology = Postscript

  Printer.PrinterIndex := Printer.Printers.IndexOf('Microsoft XPS Document Writer');
  // shows as technology = http://schemas.microsoft.com/xps/2005/06

  Printer.BeginDoc;
  // Showmessage( GetTechnologyString( Printer.Canvas.Handle ) );
  Draw(Printer.Canvas.Handle);
  Printer.EndDoc;

  showmessage('done');

end;

This is the XPS Writer (and is the same as the Microsoft Print to PDF because it both uses XPS-based printing).
uWff2E3.png


BTW. The used GetTechnologyString() looks like this. (the SetPostscriptIdentity doesn't seem to do much.)

const
  PSIDENT_GDICENTRIC = 0;
  PSIDENT_PSCENTRIC = 1;
  POSTSCRIPT_IDENTIFY = 4117;
  GETTECHNOLOGY = 20;

function GetTechnologyString(Handle: HDC): string;
var
  indata: DWord;
  outdata: array [0 .. 255] of Byte;
  S: string;
begin
  indata := GETTECHNOLOGY;
  if ExtEscape(Handle, QUERYESCSUPPORT, SizeOf(indata), @indata, 0, nil) > 0 then
  begin
    if ExtEscape(Handle, GETTECHNOLOGY, 0, nil, SizeOf(outdata), @outdata) > 0 then
    begin
      SetString(S, PAnsiChar(@outdata[0]), StrLen(PAnsiChar(@outdata[0])));
      Result := 'ok = ' + S;
    end
    else
        Result := 'gettechnology error';
  end
  else
      Result := 'query error';
end;

// doesn't do much ??
function SetPostscriptIdentity(Handle: HDC): string;
var
  indata: DWord;
  outdata: DWord;
begin
  indata := POSTSCRIPT_IDENTIFY;
  if ExtEscape(Handle, QUERYESCSUPPORT, SizeOf(indata), @indata, 0, nil) > 0 then
  begin
    indata := PSIDENT_GDICENTRIC;
    indata := PSIDENT_PSCENTRIC;
    if ExtEscape(Handle, POSTSCRIPT_IDENTIFY, SizeOf(indata), @indata, 0, nil) > 0 then
    begin
      Result := 'ok set POSTSCRIPT_IDENTIFY';
    end
    else
        Result := 'POSTSCRIPT_IDENTIFY error';
  end
  else
      Result := 'query error';
end;

#29 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-06 15:42:31

rvk
igors233 wrote:

This is very interesting, so you kind of have your own printToPDF printer installed as part of your app.
Can you explain a bit more how you achieved it?

The original reason for this was that I needed to print from my program to a pdf and after that merge it with a stationery background.pdf.

I do it the same like all other PDF creators, like PDFCreator, BullZip PDF printer etc. etc. (PDFCreator also even has the ability to merge afterwards with pdftk)

In my setup I installed Ghostscript and then I create a 'Postscript' printer with the ghostscript.ppd as main driver-description.
ghostscript.ppd uses the standard PScript5.dll from Windows.
For Windows 7+ I created my own version of the PDD-package because it needed to be signed, and the version from Ghostscript back then wasn't signed (I think now it is).

In my application I set the output for the print-job to a file (instead of the installed port LPT1: or NULL:) and print to the 'Postscript' printer.
That creates a .ps file which a can throw through ghostscript.exe (gswinc32.exe or gswin64c.exe). You could also use the gsdll64.dll.
That will result in a pdf (without background).
Then you could use pdftk to merge a background.pdf with your just created pdf.

I recently switched to SynPdf for creating a pdf. For this I also use TGdiPages (essentially the same as TPdfDocumentGDI).
(That way you would only have one code-base for the actual printing, PDF or real printer doesn't matter)

Using SynPdf has the advantage of bypassing the print-spooler of Windows and process of using ghostscript.exe (although the pdftk step is still needed for background).

There is only one downside in SynPdf, I recently discovered. In the Postscript driver from Windows I could print a transparent bitmap (hand signature on stationery).
When merging with a background (with background colors and images) the bitmap kept it's transparency.
In SynPdf it's only transparent for the current PDF/job (so over your own text in the same job).
But when merging the resulting PDF with a background.pdf (full lightgreen.pdf for example, the background of the bitmap is white).

(Transparency and GDI+ -> PDF is not an easy thing. I think this is a known problem.)

#30 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-06 13:16:04

rvk
padule wrote:

I still have one question though.

You confirmed that in the "print as pdf" file that I posted, the dashed line is still created as many segments (ellipses) and not as a real dashed line.
How does it display correctly then? What does it do differently?

Yes, that's from the Microsoft Print to PDF. That driver is really bad. It also creates very large PDF's and isn't optimized.
I usually avoid that printer.
(But I don't know why those segments are printed/interpreted correctly)

I have my own printer for my application. It creates a Postscript printer from which a catch the output to a .ps file and I throw that through ghostscript.
Somehow the .ps file from that Postscript printer is very small and the dashed line doesn't contain segments.

(you can do this yourself by creating a postscript printer and let it print to a port FILE:)

Attached is a Temp.zip with an example of a .ps file printed with the Print to PDF driver and one with Ghostscript.
https://file.io/sH9jya1SyMEj

BTW. Both drivers use the same base driver (PScript5.dll) so it must be something that the ghostscript.ppd is setting (because that's the only difference).
(Edit: actually, the print to pdf from Microsoft now uses some other driver I see)

Here is the resulting PDF from my ghostscript printer and the one from Microsoft Print to PDF.
https://file.io/4FeFlH109Psb

You can clearly see the difference when you zoom in. And the ghostscript one really has the dash (as you can see in the .ps versions).

From ghostscript.ppd.

*% == Basic Capabilities
*LanguageLevel: "3"
*ColorDevice: True
*DefaultColorSpace: RGB
*FileSystem: True
*Extensions: CMYK FileSystem Composite
*TTRasterizer: Type42
*FreeVM: "10000000"
*PrintPSErrors: True
*ContoneOnly: True
*% AccurateScreensSupport: True

Maybe the Postscript level but I haven't looked into that.

#31 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-06 10:33:23

rvk

Yes, I was already trying that:

  mode := PSIDENT_GDICENTRIC;
  mode := PSIDENT_PSCENTRIC;
  if ExtEscape(MyMetaFileCanvas.Handle, POSTSCRIPT_IDENTIFY, sizeof(DWORD), @mode, 0, nil) = 0 then
    Showmessage('error');

But it doesn't seem to work (so no dice, no error but still the ellipses) smile

There are also some calls from GDI+ to determine the capabilities of the 'driver' / canvas.

https://learn.microsoft.com/en-us/previ … 1(v=vs.85)

  if ExtEscape(MyMetaFileCanvas.Handle, GETTECHNOLOGY, 0, nil, SizeOf(DWord), @mode) > 0 then
    Showmessage(mode.ToString);

(above just raw draft, doesn't work.)
Later on I'll try to find if this could be something.

#32 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-06 10:03:17

rvk
padule wrote:

The MetaFile (emf) is just a list of GDI commands.
After a bit of googling, apparently, there is a different format (emf+) for GDI+, so here is where probably the incompatibility arises.

I think actually that the Enhanced Mata Canvas for sympdf is already EMF+.

Ths is from Winapi.GDIPOBJ in Delphi:

// The following methods are for playing an EMF+ to a graphics
// via the enumeration interface.  Each record of the EMF+ is
// sent to the callback (along with the callbackData).  Then
// the callback can invoke the Metafile::PlayRecord method
// to play the particular record.
function EnumerateMetafile(metafile: TGPMetafile; const destPoint: TGPPointF;
  callback: EnumerateMetafileProc; callbackData: Pointer = nil;
  imageAttributes: TGPImageAttributes = nil): TStatus; overload;

And mormot.ui.pdf.pas receives that in:

/// EMF enumeration callback function, called from GDI
// - draw most content on PDF canvas (do not render 100% GDI content yet)
function EnumEMFFunc(DC: HDC; var Table: THandleTable; R: PEnhMetaRecord;
  NumObjects: DWord; E: TPdfEnum): LongBool; stdcall;

I think the problem is that GDI+ thinks it has to do with a device which can't interpret postscript.

There is an option with POSTSCRIPT_IDENTIFY to set PSIDENT_GDICENTRIC or PSIDENT_PSCENTRIC for GDI centric or Postscript centric but that's for the device driver I think. (I don't even know if that would help)

I don't know how to influence GDI+ into thinking we have the dash-line capability (so it sends DASH instead of those ellipses).

ab wrote:

So in our case, when we draw the GDI+ into a GDI handle, there is a conversion into GDI API calls.
So there is no EMF+ format involved here.

Yes, but when GDI+ is going to pass on the records it stored, it does so via the EnumerateMetafile which is EMF+.
Synpdf itself works with Enhanced Meta Canvas with EMF+ structures doesn't it?

GDI+ does have the dash stored as real dash-structure (like passed to it). It gives it correctly to the printer driver (via the canvas) on Draw() command.
But for the TEnhancedMetaCanvas this is translated to ellipsed (which contain a bug that it draws the line length which is too big).

I whish I knew how the printerdriver did this. It also receives the GDI+ records on Draw() but seems to translate them much more efficient (postscript dash codes instead of ellipses).

#33 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-05 22:48:39

rvk

Last post for tonight. The problem is indeed the GDI+ to EnhancedMetaCanvas interface.
For example... if you change the FormButtom method to create a TMetaFileCanvas and use that and save it you see that the dashed line is also bigger.
(emf file can be opened in Paint)

procedure TForm1.btnFormClick(Sender: TObject);
var
  MyMetafile: TMetafile;
  MyMetaFileCanvas: TMetaFileCanvas;
begin
  MyMetafile := TMetafile.Create;
  MyMetaFileCanvas := TMetafileCanvas.Create(MyMetafile, CreateCompatibleDC(0));
  Draw(MyMetaFileCanvas.Handle);
  MyMetaFileCanvas.Free; // freeing puts it in MetaFile

  MyMetafile.SaveToFile('C:\Users\Rik\Downloads\test_pdf\test.emf');

  // Draw(Form1.Canvas.Handle);  // old line

end;

5NmWdQp.png
(still need to bump up the resolution here)

I'm not sure how the printer driver takes it's data from the printer-canvas but via a TMetaFileCanvas this might be a 'bug' in GDI+ to MetaCanvas.

But it's clear that the printerdriver uses a different method to receive it's data over the MetaFileCanvas method.

(In the past I used the ghostscript printerdriver for PDF in my program which would have solved that.)

#34 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-05 21:16:12

rvk

BTW If I don't use Print To PDF from Microsoft but my own PDF writer (based on Ghostscript driver) I get a better result.

NEGHHOF.png

Now the COMPLETE PDF looks like this (only relevant print part).
This is including the solid line AND the dashed line.

%%Page: 1 1
%%BeginPageSetup
%%PageOrientation: Portrait
pdfStartPage
0 0 612 792 re W
%%EndPageSetup
[] 0 d
1 i
0 j
0 J
10 M
1 w
/DeviceGray {} cs
[0] sc
/DeviceGray {} CS
[0] SC
false op
false OP
{} settransfer
0 0 612 792 re
W
q
q
[0.12 0 0 0.12 0 0] cm
6 w
1 J
/DeviceRGB {} CS
[0 0 1] SC
180 3600 m
240 6360 l
1800 6300 l
S
[24 30] 0 d
/DeviceRGB {} CS
[0 1 0] SC
300 3600 m
360 6240 l
1800 6180 l
S
Q
Q
showpage
%%PageTrailer
pdfEndPage
%%Trailer
end

This is how it should be.
The [24 30] 0 d is the actual dashed line setup for pdf (as you could see in TPdfCanvas.SetDash()).
And the corresponding x l #10 after that prints the dashed line.

But I have no idea how to setup GDI+ to do exactly the same for synpdf.

I think it has to do with the fact synpdf uses a EMR/enhanced meta canvas and gdi will pass the drawing different from passing it to the printerdriver via canvas. But I'm not sure (as I said, I'm no expert).

#35 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-05 19:42:27

rvk

I did check and you can see the the print to pdf has a much lower resolution.
You can see that clearly when you zoom in on the end-points. They are not as smooth as the synpdf ones.

The line code in print to pdf is

/DeviceRGB {} CS
[0 0 1] SC
1 J
0 j
10 M
0.96 w
28.799999 480 m
38.400002 38.400002 l
288 48 l
S
Q
q
[1 0 0 1 47.52 480] cm

Line code in synpdf is

/DeviceRGB {} CS
[0 0 1] SC
[] 0 d
1 J
0.75 w
22.5 467 m
30 812 l
225 804.5 l
S

Next the dashed line in print to pdf is

/DeviceRGB {} cs
[0 1 0] sc
0 0 m
0.16 -3.84 l
0.32 -4.16 l
0.64 -4.32 l
0.96 -4.16 l
1.12 -3.68 l
0.96 0.16 l
0.96 0.48 l
0.48 0.48 l
0.16 0.48 l
0 0 l
h
0.32 -8.64 m
0.32 -12.48 l
0.48 -12.8 l
0.8 -12.8 l
1.12 -12.8 l
1.28 -12.32 l
1.28 -8.48 l
1.12 -8.16 l
0.8 -8 l
0.32 -8.16 l
0.32 -8.64 l
h
<last repeated many times>

In synpdf

/DeviceRGB {} cs
[0 1 0] sc
37.13 467 m
37.22 470 l
37.22 470.19 37.41 470.38 37.59 470.38 c
37.83 470.33 37.97 470.19 37.97 469.95 c
37.88 466.95 l
37.88 466.77 37.73 466.58 37.5 466.63 c
37.31 466.63 37.13 466.77 37.13 467 c
h
37.31 473.75 m
37.36 476.75 l
37.36 476.94 37.55 477.13 37.73 477.08 c
37.97 477.08 38.11 476.94 38.11 476.7 c
38.06 473.7 l
38.06 473.52 37.88 473.33 37.69 473.33 c
37.45 473.38 37.31 473.52 37.31 473.75 c
h
37.45 480.5 m
37.5 483.5 l
37.55 483.69 37.69 483.88 37.92 483.83 c
38.11 483.83 38.3 483.69 38.25 483.45 c
38.2 480.45 l
38.2 480.27 38.02 480.08 37.83 480.08 c
37.59 480.13 37.45 480.27 37.45 480.5 c
h

In code you can also notice that (when debugging) the EMR_POLYBEZIERTO16 is called for every EMR_LINETO.
That's what results in those x c#10 lines.

/// EMF enumeration callback function, called from GDI
// - draw most content on PDF canvas (do not render 100% GDI content yet)
function EnumEMFFunc(DC: HDC; var Table: THandleTable; R: PEnhMetaRecord;
  NumObjects: DWord; E: TPdfEnum): LongBool; stdcall;

I'm not sure why those curve calls are done in the synpdf version and not in the print to pdf one.
BUT, as I already mentioned... the print to pdf doesn't have smooth end-points. I think that's why there are so many "x l #10" lines for one section.
In synpdf those are smooth curves.

CqlXdLL.png
versus
gQOcGlX.png

Do note... this could also be due to the print to pdf driver just simulating a round end-point.

#36 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-05 15:51:53

rvk
padule wrote:

Here is the PDF created with the Microsoft PDF printer (correct with both solid and dashed line with width=1): https://file.io/jwK2sU2v0k2J  (3Kb)

rvk wrote:

So it seems that GDI itself translate the dashed line itself to many multiple fragments (and calls to LineTo).

Actually, I don't really care as long as the result is correct.

Well, you should care if you want to fix this.

I'm not sure why GDI+ is drawing the dashes as ellipses but it's definitely doing that at a higher resolution.

1) You could draw this to another drawable canvas and after that copy that canvas to VCLCanvas and see if that helps (probably with loosing some resolution).

2) Second option is to find out why GDI+ is drawing ellipses and seeing if you can alter that behavior (maybe with setting the stroke color to transparent?).

3) Third option is to hack mormot.ui.pdf and adjust the line width when there is a dashed line. But I looked at the TPdfEnum.NeedPen and when it's called, it's not called with any dash style because GDI+ takes the drawing of dash completely for itself. So it's difficult to know if there is a dash being drawn. You could put a global boolean switch and hack the TPdfEnum.NeedPen, to pass 0.1 if that boolean is set (and set the boolean yourself when you want to draw a dash).

But you'll see it's not something the mormot.ui.pdf itself can do because from mormot's point of view, there is no dashed line being drawn, but multiple smaller ellipses, by GDI+.

#37 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-05 14:21:05

rvk

Actually. If you change all those 0.75 w into 0.075 w you get this:

8wLgHcn.png

Maybe GDI uses fill but shouldn't be drawn with a line length when using dash (because it already circling the dashes and only the inner should be filled, not the line itself) ??

O, and the multiple "xx w" was because I commented out the check on width.

#38 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-05 14:13:37

rvk
padule wrote:

In my small example program, you can use the print button to print on PDF using a PDF printer (eg. change 'My Printer' with 'Microsoft Print on PDF', included in Windows).
The resulting PDF of the print will be perfect (both lines with width=1), so it is not a limitation of the PDF format, it must be something in the mormot pdf library.

No, actually it's something in GDI.

I had another quick look at the decompressed PDF it seems that there isn't really ONE dashed line (in terms of PDF line).

The line code looks like this and is correct:

/DeviceRGB {} CS
[0 0 1] SC
[] 0 d
1 J
0.75 w
22.5 467 m
30 812 l
225 804.5 l
S

And the dashed line looks like this with many repeats:

/DeviceRGB {} CS
[0 1 0] SC
[] 0 d
0 J
0.75 w
0.75 w
0.75 w
0.75 w
0.75 w
<many times>
/DeviceRGB {} cs
[0 1 0] sc
37.31 473.75 m
37.36 476.75 l
37.36 476.94 37.55 477.13 37.73 477.08 c
37.97 477.08 38.11 476.94 38.11 476.7 c
38.06 473.7 l
38.06 473.52 37.88 473.33 37.69 473.33 c
37.45 473.38 37.31 473.52 37.31 473.75 c
h
37.45 480.5 m
37.5 483.5 l
37.55 483.69 37.69 483.88 37.92 483.83 c
38.11 483.83 38.3 483.69 38.25 483.45 c
38.2 480.45 l
38.2 480.27 38.02 480.08 37.83 480.08 c
37.59 480.13 37.45 480.27 37.45 480.5 c
h
<last part repeated many times>

So it seems that GDI itself translate the dashed line itself to many multiple fragments (and calls to LineTo).

Above that code are many 0.75 w parts. (which stands for line width.

The kicker now.... I changed all the 0.75 w parts into 0.175 w to make the lines thinner.
The result is this:
BLy0NEe.png

Here you see clearly that it is GDI that is drawing the line.

When that is drawn on a canvas with 1 pixel resolution, you get the screen and printer result.
But when it is drawn to a canvas with much higher resolution, it's going to draw those circel thingies.

PS. I extracted the pdf with pdf2ps (from xpdf-tools) and looked at the resulting .ps file.
You can read the ps file afterwards with a postscript interpreter or with gimp (and setting a resolution of 1200 during import).

I know you can hack the pdf writer to write smaller lines (like I showed you earlier).
But it's definitely GDI that's drawing the dashed line itself.

#39 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-05 11:32:14

rvk

O, when commenting out the check on the old pen.width so it ALWAYS sets the pen again, I was able to set a pen.width of 1 for the dashed line.

      // if pen.width * Canvas.fWorldFactorX * Canvas.fDevScaleX <> fPenWidth then

DBbnQID.png

Setting the solid line to 1 in code and the dashed line to 1 reveals that the dashed line is always a bit bigger.

bP2PafJ.png

I'm not sure if this is a PDF thing.
(if you create plain PDF with only vector line is it possible to create a 1pixel dashed line?)

I have two things I wonder about...

1) Is the resolution set to maximum? If the resolution is higher it could be that there is a minimum width for a line (with taking rounding errors in account).
       (for me fWorldFactorX and Canvas.fDevScaleX where not 1 so there could be some problems there. Canvas has a resolution.)

2) When using rounded caps, how would the PDF actually make a round cap for a 1 pixel line? You can't, so the line might always be slightly larger.

#40 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-09-05 11:25:34

rvk
padule wrote:
ab wrote:

...in a way incompatible with what the PDF library can handle.

If you think it can be fixed, it would be helpful if you could point me roughly to the code where this is handled and I'll see if I can fix it myself.

Hi, a non-expert here... wink

For the actual setting of the pen and dash you can look in mormot.ui.pdf.pas at the procedure TPdfEnum.NeedPen.

But this only gets called when you actually save the file to pdf, not before (so the translation from gdi to pdf).

When putting a breakpoint there and watch for DC[nDC].pen.width you see that for both lines the value is 16 (in your example program).

Increasing the width to 100 in second breakpoint-loop seems to increase the width of the dashed line.
But lowering it under 16 to 8 for example doesn't seem to be possible.
Minimum width dashed line seems to be that value.

This is the result of setting the DC[nDC].pen.width to 1 for both lines.

qd7jAjN.png

And this for setting it to 100 for both lines.

HYY4Z6V.png

#41 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-08-29 13:53:52

rvk
padule wrote:

Maybe I should have mentioned that I use GDI+ to draw on the VCLCanvas. It works really fine, except for this problem with dotted lines.
I have a function I use to set the pen property values:

Looks like you are programming the dash and pen yourself (and not by SynPDF).

I'm not familiar with the rest of the code so I can't say what's going wrong here.
And since there is no code to reproduce this I can't dive in the source myself.

You might want to break the code down to standalone parts, creating a reproducible project which just draws two lines.
With doing so you might find the problem yourself or it would at least be easier to track down the problem.

Also, to begin with, you might want to check if this line doesn't produce a GetLastStatus <> Ok error:
(and what error it does produce, since the problem is that you get a solid line)

Pen.SetDashPattern(@Dash,4);
if Pen.GetLastStatus<>Ok then Pen.SetDashStyle(DashStyleSolid);

#42 Re: PDF Engine » Drawing dashed lines on VCLCanvas » 2023-08-29 10:10:27

rvk

Can you show your code or snippet?
For me it works fine with the latest source of mormot.ui.pdf.

PDF.VCLCanvas.Pen.Width := 1;
PDF.VCLCanvas.Pen.Style := psDash;
PDF.VCLCanvas.MoveTo(100, 600);
PDF.VCLCanvas.LineTo(300, 600);
PDF.VCLCanvas.Pen.Width := 4;
PDF.VCLCanvas.MoveTo(300, 600);
PDF.VCLCanvas.LineTo(600, 600);

lwVWREm.png

#43 Re: PDF Engine » MultilineTextRect does not work correctly » 2023-06-28 08:14:40

rvk

It seems like you are misinterpreting some things.
First of all there are these lines in MultilineTextRect.

      MoveToNextLine;
      ARect.Top := ARect.Top - FPage.Leading;
      if ARect.Top < ARect.Bottom + FPage.FontSize then
        Break;

So the ARect.Top is decreasing with each line (by FPage.Leading which is default 0)
That means you have your Top and Bottom wrong.

Change them to Top := 800 and Bottom := 300; (the coordinates are from the bottom up)

Next... for the MoveToNextLine function to function you will need to set the Leading.

    /// Set the text leading, Tl, to the specified leading value
    // - leading which is a number expressed in unscaled text space units;
    // it specifies the vertical distance between the baselines of adjacent
    // lines of text
    // - Text leading is used only by the MoveToNextLine and ShowTextNextLine methods
    // - you can force the next line to be just below the current one by calling:
    // ! SetLeading(Attributes.FontSize);
    // - Default value is 0
    procedure SetLeading(leading: Single);                       {  TL  }

So if you change your code to this it should work:

      R.Left := 30;
      R.Top := 800; // this is almost at the top of the page
      R.Right := 100;
      R.Bottom := 300; // needs to be lower than the top
      PDF.Canvas.SetFont('Times New Roman', 12, []);
      PDF.Canvas.SetLeading(12); // font size, for newline
      PDF.Canvas.MultilineTextRect(R, 'Welcome to mORMot Open Source', True);

#44 Re: PDF Engine » SynPdf - Select active page / canvas » 2023-04-27 08:39:00

rvk

I don't think that's (directly) possible with TPdfDocumentGDI.
You would think that it should work with TPdfDocumentGDI.RawPages but in TPdfDocumentGDI.AddPage there is this line at the top:

if (FCanvas<>nil) and (FCanvas.FPage<>nil) then
  TPdfPageGdi(FCanvas.FPage).FlushVCLCanvas;

That line flushes and frees TPdfPage.fVCLCurrentCanvas of the previous page.
That's the pdf.VCLCanvas you want to use for the page.
When going back to page one with TPdfDocumentGDI.RawPages the needed TPdfPage.VCLCanvas is already freed and destroyed so when accessing it again, it will be created again, new but empty.
That's why the page is overwritten.

You could try to comment out the above line. Then you get the result you want.
The TPdfPage.fVCLCurrentCanvas is kept after TPdfDocumentGDI.AddPage.
The TPdfPage.VCLCanvas is flushed in that case at TPdfDocumentGDI.SaveToStream() and not at AddPage.

But I'm not sure if there are any problems with it down the road.

#45 Re: PDF Engine » Problem with resaving an encrypted PDF » 2022-12-14 12:19:27

rvk
ab wrote:

My guess is that it is because you specified PDF_PERMISSION_NOCOPY.

You deny to make any copy - so you deny any save?

Woops, yes. But in my problem code I have this:

Enc := TPdfEncryption.New(elRC4_40 { elRC4_128 same result } , '', 'testpassword', PDF_PERMISSION_NOMODIF + [epFillingForms, epAuthoringComment]);

(Try it yourself with these options)

With this it works

Enc := TPdfEncryption.New(elRC4_40 { elRC4_128 same result } , '', 'testpassword', PDF_PERMISSION_NOMODIF);

Why would adding epFillingForms and epAuthoringComment make Adobe not be able to save?

These are the permissions when doing that:
qUYUIiq.png

When merging a PDF with pdftk and including the same options it does work. Permissions:
l2XbFLi.png

Those permissions are the same but with the SynPDF in Adobe Reader I get the error when "saving as".

I found this out because normally the SynPDF goes through pdftk.exe to merge a background and there is no problem with saving.
But when creating a "blanc" PDF (without background), so clean from SynPDF, saving as in Adobe isn't possible.

Edit:
It's also a problem if you just use PDF_PERSMISSION_NOPRINT. Copying should be allowed then.
In PDF_PERSMISSION_NOPRINT the epContentCopy and epAuthoringComment are included.

Examining further, it seems to happen when epAuthoringComment is included, while this is no problem with another PDF generator.

Edit #2: BTW. Subsequently this also doesn't work because it includes epAuthoringComment.

Enc := TPdfEncryption.New(elRC4_40 { elRC4_128 same result } , '', 'testpassword', PDF_PERMISSION_ALL);

Then I thought this would work but it also doesn't:

Enc := TPdfEncryption.New(elRC4_40 { elRC4_128 same result } , '', 'testpassword', PDF_PERMISSION_ALL - [epAuthoringComment]);

(Also epContentCopy is only for Content copying. "Save as" should always work in Adobe reader. Even with the most strict PDF.)

#46 PDF Engine » Problem with resaving an encrypted PDF » 2022-12-14 11:24:01

rvk
Replies: 2

There still seems to be a problem with password protecting a PDF.
When I try to resave a password protected PDF (generated from SynPDF), Adobe Reader complains with the message

The document could not be saved. Bad parameter.

Code to duplicate this behavior:

uses SynPdf;

procedure MakePdfSynPdf;
var
  FileTemp: string;
  PDF: TPdfDocumentGDI;
  Enc: TPdfEncryption;
begin
  FileTemp := 'C:\Temp\Test1.pdf';
  Enc := TPdfEncryption.New(elRC4_40 { elRC4_128 same result } , '', 'testpassword', PDF_PERMISSION_NOCOPY);
  PDF := TPdfDocumentGDI.Create(false, 0, false, Enc);
  try
    PDF.Info.Author := 'User name';
    PDF.Info.Creator := 'Software name';
    PDF.Info.subject := 'Auto generated document';
    PDF.CompressionMethod := cmNone;
    PDF.EmbeddedTTF := true;
    PDF.EmbeddedTTFIgnore.Text := RawUTF8('Arial'#13#10'Times New Roman'#13#10'Courier New'#13#10'Symbol'#13#10'WingDings');;
    PDF.EmbeddedWholeTTF := true;
    PDF.Root.ViewerPreference := [vpFitWindow];
    PDF.DefaultPageLandscape := false;
    PDF.AddPage;
    PDF.VCLCanvas.Font.Name := 'Arial';
    PDF.VCLCanvas.Font.size := 20;
    PDF.VCLCanvas.TextOut(400, 400, 'Test');
    PDF.SaveToFile(FileTemp);
    // ExecAssociatedApp(FileTemp);
  finally
      PDF.Free;
  end;
end;

Open C:\Temp\Test1.pdf with Adobe Reader (Windows) and try to save it to the desktop.
Without password it works just fine.

#47 Re: PDF Engine » Duplex printing issue? » 2022-10-19 10:02:08

rvk

Works fine for me.

Does this also happen with TGDIPages.PrintPages()?
How do you set the duplex? Manually through the dialog or in code?

Some simple example code which reproduces this?
Something like this (which works for me):

procedure TForm1.Button1Click(Sender: TObject);
var
  PDF: mORMotReport.TGDIPages;
begin
  PDF := mORMotReport.TGDIPages.Create(Self);
  try
    PDF.BeginDoc;
    PDF.DrawTextAt('Test page 1', 10);
    PDF.NewPage;
    PDF.DrawTextAt('Test page 2', 20);
    PDF.EndDoc;
    PDF.ShowPreviewForm; // setting printer to duplex and printing works correctly
  finally
      PDF.Free;
  end;
end;

BTW, when printing though Adobe Reader, the chances are that the entire page is filled with whitespace and send to the printer, and thus overlapping your previous text. So this could still be a driver fault. You could test this by creating a small example with printdialog (to set the duplex option) which directly prints two pages to the printer.

#48 Re: PDF Engine » PDF rendering not correct » 2022-08-02 20:59:05

rvk

Yes, I can confirm the UseUniscribe := true works in this case.
The UseFontFallBack were not needed for me (and seeing that the PDF already contains Arial I think they don't do much).

I don't now why UseUniscribe is needed when a fairly simple Arial font is used (I'm not that familiar with the inner workings of SynPDF).

But maybe it has something to do with the way everything is rendered into the WMF and after that the font-info becomes complex in which case there is a need for UseUniscribe (just guessing here).

BTW The reason some lines work fine and others not is in the fact that the space between words is wrong. And text which is combined is stretched. But text snippets that were placed on a certain position is fine.
The amount (Betrag) on the productline is on a specific position (so that works fine).
The amount  for subtotal and MWST probably have multiple spaces in front of them and that's because they are shifted too much to the right (the multiple spaces are all expanded).

(hey, and that's where that format('%11.2n) and format('%7.2n) comes in. The 11.2 has more spaces and each space is expanded to multiple spaces. 7.2 has less space and that's why that one seemed to fix some of the issues smile )

Maybe @ab can elaborate on the reason for the need for UseUniscribe.

#49 Re: PDF Engine » PDF rendering not correct » 2022-08-02 12:20:02

rvk

Yes, download went fine. I see what the problem is.
Somehow the spaces in between words are too large.
And that throws off the entire layout.

(I haven't got time right now to look into it further but maybe someone else recognizes this problem.)
Maybe TPdfDocumentGDI.UseMetaFileTextPositioning can help? (have to test that if I got some time)


For clarity here the PDF screenshot:

bsF9Yr7.png

From the WMF loaded in LibreOffice Draw:

1p6Oips.png

#50 Re: PDF Engine » PDF rendering not correct » 2022-08-02 10:38:54

rvk

We still haven't seen what exactly the problem is. Alignment problems? Or something else? "Doesn't render properly" isn't really a clear description.

KlausV wrote:

I changed the length of the format command from format('%11.2n) to format('%7.2n) and now it seems correct, but why?

That would suggest some alignment problems. If you use spaces you would need to use (right aligned or decimal aligned) tabs to always correctly align numbers.

These also don't align in text:
    145.12
133241.12
(and the first has 4 spaces, so is 7.2, second has 1 space so is also 7.2)

But I'm not sure why this would be correct in a WMF format (IF that's the actual problem of course).
Maybe it has to do with font substitution (in WMF one font and when rendered to PDF another font is chosen).

KlausV wrote:

Now, I have created the meta WMF file and it looks correct, but the PDF file does not.
Additionally, I have created the WMF file out of QR and it looks perfect as well.

In that case it might be useful (especially for the developer) to have the sample meta.wmf to check where this goes wrong.

(It then can be loaded with aMeta.LoadFromFile('C:\temp\WMF\meta.wmf); and debugged properly.)

Board footer

Powered by FluxBB