#1 Re: PDF Engine » Bug in the CreateHyperLink() function? » 2019-03-05 14:23:16

@ab, several days ago I sent a push notification on the SynPDF GIT, which contains the above mentioned modification about hyperlinks, and also adds a new function, named CreateFileLink(), allowing to add a link to a file on the local computer, using an absolute or relative path. Can you please check, validate and (eventually) accept these modifications? Or if you decide to reject them, can you please inform me the reason, in order to allow me to recheck and correct my copy?

I also have the following questions:

  • My hyperlink works well now. However every time I click on it in Adobe PDF Reader, I receive a security notification popup dialog, asking me to confirm if I really want to open my file. Is there a way to trust the PDF in a such manner to no longer receive this notification? And if yes, how to do that?

  • Every time I click on my hyperlink, its background blinks to black. Is there a way to change this color?

#2 PDF Engine » Bug in the CreateHyperLink() function? » 2019-01-25 21:24:57

jeanmilost
Replies: 4

I noticed that the links generated by the CreateHyperLink() function seems wrong. Using this function I get the following result in my PDF File:

...
6 0 obj
<</Type/Annot/Subtype/Link/F 4/BS<</W 0>>/Rect[10 10 310 60]>>
endobj
7 0 obj
<</S/URI/URI(http://www.google.com/)>>
endobj
...

However this doesn't work, and nothing happen while I click on the active link rect.

Now if I modify the CreateHyperLink() function this way:

function TPdfDocument.CreateHyperLink(const ARect: TPdfRect; const url : RawUTF8;
  BorderStyle: TPdfAnnotationBorder; BorderWidth: integer): TPdfDictionary;
var aURIObj: TPdfDictionary;
begin
  result := CreateAnnotation(asLink,ARect,BorderStyle,BorderWidth);
  aURIObj := TPdfDictionary.Create(FXref);
  aURIObj.FSaveAtTheEnd := true;
  aURIObj.AddItem('Type', 'Action');
  aURIObj.AddItem('S', 'URI');
  aURIObj.AddItemTextUTF8('URI', url);
  result.AddItem('A', aURIObj);
end;

I get the following result:

...
6 0 obj
<</Type/Annot/Subtype/Link/F 4/BS<</W 0>>/Rect[10 10 310 60]/A<</Type/Action/S/URI/URI(http://www.google.com/)>>>>
endobj
...

Which is working as expected: when I click on the link active rect, the Google page is opened.

I have several times asked questions about this function, and how to insert a hyperlink into my PDF page. No answer has ever been given. Am I the only developer who can not use this feature, or is this function containing a bug? And in this case, is my above modification respecting the PDF standards, about which I know nothing?

Regards

#3 Re: PDF Engine » Can SynPDF be used with Direct2D/DirectWrite? » 2018-11-21 16:46:34

Thank you for the answer. I was expecting something like this.

However is it expected to expand SynPDF to support Direct2D in the future?

#4 PDF Engine » Can SynPDF be used with Direct2D/DirectWrite? » 2018-11-21 15:54:24

jeanmilost
Replies: 3

I'm interested to use Direct2D/DirectWrite to draw a PDF content. One of the reason is that I want to use the provided colored font system, other is because its offers a improved non Latin languages support, and it resolves better the font fallback.

So I tried to get the GDI canvas from my PDF page, to create a Direct2D canvas above it, and to draw text on it. However I just get a black rectangle as result.

My question is: Is there a way to draw on a PDF page using Direct2D, and if yes, how to achieve that?

NOTE here is the code I actually use to try to draw a text with Direct2D. Of course I tried several configurations, but I always get the same result

void TMainForm::DrawWithD2D()
{
    const std::wstring text = L"This is an example text!!!";

    std::auto_ptr<TPdfDocumentGDI> pPdfDoc(new TPdfDocumentGDI(false, 0, false, NULL));

    // populate PDF header
    pPdfDoc->Info->Title                = L"D2D Demo";
    pPdfDoc->Info->Author               = L"Ursa Minor";
    pPdfDoc->Info->Creator              = L"PDF Demo";
    pPdfDoc->Info->CreationDate         = ::Now();
    pPdfDoc->DefaultPaperSize           = Synpdf::psA4;
    pPdfDoc->UseFontFallBack            = true;
    pPdfDoc->UseUniscribe               = true;
    pPdfDoc->FontFallBackNames          = "Arial";
    pPdfDoc->KerningHHorzScale          = 96.5f;
    pPdfDoc->PDFA1                      = false;
    pPdfDoc->GeneratePDF15File          = true;
    pPdfDoc->ScreenLogPixels            = 96;
    pPdfDoc->UseMetaFileTextPositioning = tpExactTextCharacterPositining;

    // create and add first page
    pPdfDoc->AddPage();

    TCanvas* pCanvas = pPdfDoc->VCLCanvas;

    const TRect rect(20,20, 320, 220);

    ::D2D1_RECT_F drawRect;
    drawRect.left   = rect.Left;
    drawRect.top    = rect.Top;
    drawRect.right  = rect.Right;
    drawRect.bottom = rect.Bottom;

    // get Direct2D canvas
    std::unique_ptr<TDirect2DCanvas> pD2DCanvas(new TDirect2DCanvas(pCanvas->Handle, rect));

    // set antialiasing mode
    pD2DCanvas->RenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);

    ::ID2D1SolidColorBrush* pBrush = NULL;

    // create solid color brush
    pD2DCanvas->RenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &pBrush);

    if (!pBrush)
        return;

    // get (or create) the DirectWrite factory
    _di_IDWriteFactory pDirectWrite = WDirect2DHelper::DWriteFactory(DWRITE_FACTORY_TYPE_SHARED);

    if (!pDirectWrite)
        return;

    // configure and apply new rendering parameters for DirectWrite
    _di_IDWriteRenderingParams renderParams;
    pDirectWrite->CreateCustomRenderingParams(1.0f, 0.0f, 0.0f, DWRITE_PIXEL_GEOMETRY_RGB,
            DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC, &renderParams);
    pD2DCanvas->RenderTarget->SetTextRenderingParams(renderParams);

    // get DirectWrite text format object
    _di_IDWriteTextFormat pFormat = pD2DCanvas->Font->Handle;

    // found it?
    if (!pFormat)
        return;

    // set horiz alignment
    pFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);

    // set vert alignment
    pFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);

    // set word wrapping mode
    pFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);

    IDWriteInlineObject* pInlineObject = NULL;

    ::DWRITE_TRIMMING trimming;
    trimming.delimiter      = 0;
    trimming.delimiterCount = 0;
    trimming.granularity    = DWRITE_TRIMMING_GRANULARITY_NONE;

    // set text trimming
    pFormat->SetTrimming(&trimming, pInlineObject);

    try
    {
        pD2DCanvas->BeginDraw();

        // draw the text
        pD2DCanvas->RenderTarget->DrawText(text.c_str(), text.length(), pFormat, drawRect, pBrush,
                D2D1_DRAW_TEXT_OPTIONS_NONE);
    }
    __finally
    {
        pD2DCanvas->EndDraw();
    }

    pPdfDoc->SaveToFile(L"test.pdf");

    ::ShellExecute(Handle, L"open", L"test.pdf", L"", L"", 0);
}

#5 PDF Engine » How to add a simple hyperlink in a PDF document? » 2017-12-06 15:39:25

jeanmilost
Replies: 1

Hello,

I'm trying to add a simple hyperlink in a PDF document. When clicking on it, I want to be redirected on a website, or open a local file, depending of the link content.

I noticed that the TPdfDocument class contains several functions that seems useful to do that, but I'm absolutely unable to use them. Just nothing appear on my document. And I cannot find any info that explain how to use them.

Here is the code I wrote

    std::auto_ptr<TPdfDocumentGDI> pPdfDoc(new TPdfDocumentGDI(false, 0, false, NULL));

    pPdfDoc->Info->Author       = L"Tester";
    pPdfDoc->Info->CreationDate = ::Now();
    pPdfDoc->Info->Creator      = L"Tester";
    pPdfDoc->DefaultPaperSize   = Synpdf::psA4;
    pPdfDoc->UseUniscribe       = true;
    TPdfPage* pPage             = pPdfDoc->AddPage();

    TPdfRect rect;
    rect.Left   = 0;
    rect.Top    = 0;
    rect.Right  = 50;
    rect.Bottom = 50;

    pPdfDoc->CreateHyperLink(rect, "http://www.google.com");

    pPdfDoc->SaveToFile(L"test.pdf");

I just get a perfectly blank page when I open the test.pdf file.

I also tried the following variants (based on the few info I found on the internet) :

    ...
    TPdfDictionary* pDictionary = pPdfDoc->CreateLink(rect, "http://www.google.com");
    pDictionary->AddItem("S", "URI");
    pDictionary->AddItem("F", "(http://www.google.com)");
    ...
    ...
    pPdfDoc->VCLCanvas->MoveTo(10, 10);
    ::GDICommentBookmark(pPdfDoc->VCLCanvas->Handle, "http://www.google.com");
    ::GDICommentOutline(pPdfDoc->VCLCanvas->Handle, "http://www.google.com", 0);
    ::GDICommentLink(pPdfDoc->VCLCanvas->Handle, "http://www.google.com", rect, false);
    ...

But no way. So if somebody could post a simple example that show how to add a hyperlink in a PDF document, I would be really grateful.

Regards

#6 Re: PDF Engine » Latest SynPdf version breaks the clipping » 2017-08-15 15:17:16

Hello

For info: tried the latest nightly build today (1.18.3764), the issue still exists. Do you think you will deal with it soon?

Regards

#7 PDF Engine » Drawing from a metafile generates a broken PDF » 2017-02-22 16:44:44

jeanmilost
Replies: 0

Hello

I use SynPDF to export the user chats of my application into a PDF file. For that I use the VCL canvas provided by the PDF document, and I draw on it with several GDI and GDI+ functions. The resulting PDF is well generated.

Now I use the exactly same GDI and GDI+ drawing code, but instead of drawing directly on the VCL canvas provided by the PDF document, I generate an intermediate metafile, that I use to draw my page, and later I use this metafile to draw the final page on the PDF document. However the resulting PDF is broken (can be opened at all by Firefox, several parts are missing when opened by Chrome, ...)

Here is the code I use to generate the intermediate metafile canvas:

    // create metafile
    HDC hMetaFileDC = ::CreateEnhMetaFile(NULL, NULL, NULL, NULL);

    if (!hMetaFileDC)
    {
        M_LogErrorT("Export messages to PDF - FAILED - create metafile");
        return false;
    }

    pCanvas->Handle = hMetaFileDC;

And here is the code I use to draw the intermediate metafile on the final document

    // terminate and close the metafile
    ::HENHMETAFILE hMetaFile = ::CloseEnhMetaFile(hMetaFileDC);

    std::auto_ptr<TMetafile> pMeta(new TMetafile());

    // use TMetafile object
    pMeta->Handle = (unsigned)hMetaFile;

    // render meta file
    pPdfDoc->Canvas->RenderMetaFile(pMeta.get());

NOTE I also draw a footer after the above code by calling some GDI/GDI+ functions on the pPdfDoc->VCLCanvas

I also joined a copy of a PDF generated from the same content, once without the problem, and once with the problem, here:
https://drive.google.com/open?id=0B7C-R … nZ6Q21hNUk for the well formatted document
https://drive.google.com/open?id=0B7C-R … UhBaG9vekU for the broken document

I cannot figure out why the issue happen. Somebody can explain to me what I'm doing wrong?

Regards

#8 Re: PDF Engine » Clipping problems » 2017-02-22 16:17:04

Hello,

I have the exactly same issue using the latest nightly build. If I just revert the following function:

procedure TPdfEnum.ExtSelectClipRgn(data: PRgnDataHeader; iMode: DWord);
var ExtClip: TRect;
begin
  try
    ExtClip := data^.rcBound;
    with DC[nDC] do
    case iMode of
      RGN_COPY: begin
        ClipRgn := MetaRgn;
        ClipRgnNull := False;
      end;
    end;
  except
    on E: Exception do ; // ignore any error (continue EMF enumeration)
  end;
end;

To his previous version:

procedure TPdfEnum.ExtSelectClipRgn(data: PEMRExtSelectClipRgn);
var RGNs: PRgnData;
    i: Integer;
    RCT: TRect;
    ClipRect: TPdfBox;
begin // see http://www.codeproject.com/Articles/1944/Guide-to-WIN-Regions
  if not DC[nDC].ClipRgnNull then begin
    Canvas.GRestore;
    Canvas.NewPath;
    Canvas.fNewPath := False;
    DC[nDC].ClipRgnNull := True;
    fFillColor := -1;
  end;
  if Data^.cbRgnData>0 then begin
    Canvas.GSave;
    Canvas.NewPath;
    DC[nDC].ClipRgnNull := False;
    RGNs := @Data^.RgnData;
    for i := 0 to RGNs^.rdh.nCount-1 do begin
      Move(Rgns^.Buffer[i*SizeOf(TRect)], RCT, SizeOf(RCT));
      Inc(RCT.Bottom);
      ClipRect := Canvas.BoxI(RCT, False);
      Canvas.Rectangle(ClipRect.Left,ClipRect.Top,ClipRect.Width,ClipRect.Height);
    end;
    Canvas.Closepath;
    Canvas.Clip;
    Canvas.NewPath;
    Canvas.FNewPath := False;
  end;
end;

All become fine again and I can use the latest nightly build without problems.

Regards

#9 Re: PDF Engine » Questions and suggestions » 2017-02-22 13:54:50

Hello,

I posted a pull request just now, thanks for the info.

For 3, I use Embarcadero RAD Studio XE7.

Regards

#10 PDF Engine » Questions and suggestions » 2017-02-21 23:06:55

jeanmilost
Replies: 2

Hello,

I have some suggestions and questions about the latest version (nightly build) of the library:

- In SynPdf, I found the following code:

function CenterPoint(const Rect: TRect): TPoint;
begin
  result.X := (Rect.Right-Rect.Left) div 2+Rect.Left;
  result.Y := (Rect.Bottom-Rect.Top) div 2+Rect.Top;
end;

As it is a kind of function that may be called intensively, I suggest the following modification:

function CenterPoint(const Rect: TRect): TPoint;
begin
  result.X := (Rect.Left+Rect.Right) div 2;
  result.Y := (Rect.Top+Rect.Bottom) div 2;
end;

- Several month ago I posted a proposition to link Uniscribe dynamically inside SynPDF, and not only statically, as it's the case on the latest nightly build. Apparently this code has never been accepted. As it is an important functionality required by my company to work with SynPDF, and because for now I have no other alternative than synchronize the code manually, I want to know why this change was rejected, and what are the modalities to incorporate it inside the next library version?

- The latest version of the library generates the following warning while compiled on my computer:

[dcc32 Warning] SynPdfLib.dpk(46): W1029 Duplicate constructor 'ESynException.CreateLastOSError' with identical parameters will be inacessible from C++

Regards

#11 PDF Engine » Some additional features » 2016-10-05 16:31:26

jeanmilost
Replies: 3

Hello

I recently had to make changes in the SynPDF library to add some features that my project required but that were not available. Here they are:
- Support for multiple font linking, thus a valid font may be found in case the text contains many languages and both main font and first linked font did not support the language (e.g. FontFallBackNames := 'Arial,Verdana')
- Added a KerningHHorzScale property in TPdfDocumentGDI to be able to change the global Kerning horizontal scale value (in my case was useful to catch up some conversion errors between the GDI canvas and the PDF meta file)
- Added a UNISCRIBE_DYNAMIC_LINK define in case Uniscribe should be linked dynamically to the target application

I think (and hope) that these changes can serve to others, so I published them here: https://drive.google.com/file/d/0B7C-Rw … sp=sharing

Regards

#13 Re: PDF Engine » Compiling and using the library in c++ » 2016-04-14 13:29:44

Hi ab,

Thanks for your reply.

These issues were in relation with the latest stable version ('1.18.1417'), available here: http://synopse.info/files/pdf/synpdf.zip. However I built the latest nighty build version ('1.18.2556'), and you're right, many of the above mentioned issues no longer exist. I apologize for the mistake.

However there remains these issues:
-> In SynPdf.pas, the TScriptControlAttr_enum enumerator is conflicting with the VCL, and I propose the following change:

TScriptControlAttr_enum = (
    fContextDigits,
    fInvertPreBoundDir,
    fInvertPostBoundDir,
    fLinkStringBefore,
    fLinkStringAfter,
    fNeutralOverride,
    fNumericOverride,
    fLegacyBidiClass,
    pdfScr0, pdfScr1, pdfScr2, pdfScr3, pdfScr4, pdfScr5, pdfScr6, pdfScr7);

-> I still cannot compile the project with the NO_USE_UNISCRIBE define activated. I propose the following change:

function TPdfWrite.AddGlyphs(Glyphs: PWord; GlyphsCount: integer;
  Canvas: TPdfCanvas; AVisAttrsPtr: Pointer): TPdfWrite;
var TTF: TPdfFontTrueType;
    first: boolean;
    glyph: integer;
    {$ifdef USE_UNISCRIBE}
      AVisAttrs: PScriptVisAttr;
    {$endif}
begin
  if (Glyphs<>nil) and (GlyphsCount>0) then begin
    with Canvas.FPage do
      if FFont.FTrueTypeFontsIndex=0 then
        TTF := nil else // mark we don't have an Unicode font, i.e. a TTF
        TTF := TPdfFontTrueType(FFont);
    if TTF<>nil then begin // we need a TTF font
      if (Canvas.FPage.Font<>TTF.UnicodeFont) and (TTF.UnicodeFont=nil) then
        TTF.CreateAssociatedUnicodeFont;
      Canvas.SetPDFFont(TTF.UnicodeFont,Canvas.FPage.FontSize);
      first := true;
      {$ifdef USE_UNISCRIBE}
        AVisAttrs := AVisAttrsPtr;
      {$endif}
      while GlyphsCount>0 do begin
        {$ifdef USE_UNISCRIBE}
          if (AVisAttrs=nil) or
             not(AVisAttrs^.fFlags*[fDiacritic,fZeroWidth]=[fZeroWidth]) then begin
        {$endif}
          glyph := TTF.WinAnsiFont.GetAndMarkGlyphAsUsed(Glyphs^);
          // this font shall by definition contain all needed glyphs
          // -> no Font Fallback is to be implemented here
          if first then begin
            first := false;
            Add('<');
          end;
          AddHex4(glyph);
        {$ifdef USE_UNISCRIBE}
          end;
        {$endif}
        inc(Glyphs);
        dec(GlyphsCount);
        {$ifdef USE_UNISCRIBE}
          if AVisAttrs<>nil then
            inc(AVisAttrs);
        {$endif}
      end;
      if not first then
        Add('> Tj'#10);
    end;
  end;
  result := self;
end;

Regards

#14 Re: PDF Engine » Latest SynPdf version breaks the clipping » 2016-04-14 12:54:15

Hi ab,

Thanks for your reply.

The library version is written inside the SynopseCommit.inc file, right? In this case, the latest stable version is the '1.18.1417', and this version contains the above mentioned issue.

I verified again my working version and is the '1.18.2217', so it's a more recent version, and effectively the clipping is working in it. In conclusion, if the code existing in the '1.18.2217' version is destined to replace the code in the latest stable '1.18.1417' version, then then above mentioned issue is effectively resolved.

Regards

#15 PDF Engine » Compiling and using the library in c++ » 2016-04-13 23:02:47

jeanmilost
Replies: 4

Hello,

I'm using the SynPdf in a c++ project. Every time I update to a new version I need to resolve some minor issues inside the generated .hpp files. As these issues are often very simple to resolve, I listed them below, and I added the way I resolve them. It would be possible to correct them once for all in the next library version?

Issue:    The library cannot be compiled if the define USE_UNISCRIBE is not declared
File:       SynPdf.pas
Solution: Change the following function as follow:

function TPdfWrite.AddGlyphs(Glyphs: PWord; GlyphsCount: integer;
  Canvas: TPdfCanvas; AVisAttrsPtr: Pointer): TPdfWrite;
var TTF: TPdfFontTrueType;
    first: boolean;
    glyph: integer;
    {$ifdef USE_UNISCRIBE}
      AVisAttrs: PScriptVisAttr;
    {$endif}
begin
  if (Glyphs<>nil) and (GlyphsCount>0) then begin
    with Canvas.FPage do
      if FFont.FTrueTypeFontsIndex=0 then
        TTF := nil else // mark we don't have an Unicode font, i.e. a TTF
        TTF := TPdfFontTrueType(FFont);
    if TTF<>nil then begin // we need a TTF font
      if (Canvas.FPage.Font<>TTF.UnicodeFont) and (TTF.UnicodeFont=nil) then
        TTF.CreateAssociatedUnicodeFont;
      Canvas.SetPDFFont(TTF.UnicodeFont,Canvas.FPage.FontSize);
      first := true;
      {$ifdef USE_UNISCRIBE}
        AVisAttrs := AVisAttrsPtr;
      {$endif}
      while GlyphsCount>0 do begin
        {$ifdef USE_UNISCRIBE}
          if (AVisAttrs=nil) or
             not(AVisAttrs^.fFlags*[fDiacritic,fZeroWidth]=[fZeroWidth]) then begin
        {$endif}
          glyph := TTF.WinAnsiFont.GetAndMarkGlyphAsUsed(Glyphs^);
          // this font shall by definition contain all needed glyphs
          // -> no Font Fallback is to be implemented here
          if first then begin
            first := false;
            Add('<');
          end;
          AddHex4(glyph);
        {$ifdef USE_UNISCRIBE}
          end;
        {$endif}
        inc(Glyphs);
        dec(GlyphsCount);
        {$ifdef USE_UNISCRIBE}
          if AVisAttrs<>nil then
            inc(AVisAttrs);
        {$endif}
      end;
      if not first then
        Add('> Tj'#10);
    end;
  end;
  result := self;
end;

Issue: The TScriptControlAttr_enum enumerator is conflicting with the VCL
File:       SynPdf
Solution: Declare the enum as follow:

  TScriptControlAttr_enum = (
    fContextDigits,
    fInvertPreBoundDir,
    fInvertPostBoundDir,
    fLinkStringBefore,
    fLinkStringAfter,
    fNeutralOverride,
    fNumericOverride,
    fLegacyBidiClass,
    pdfScr0, pdfScr1, pdfScr2, pdfScr3, pdfScr4, pdfScr5, pdfScr6, pdfScr7);

Issue:     All the properties exposed inside the TSynValidateText class result in the following error: [C++ Error] SynCommons.hpp(2091, 74): E2062 Invalid indirection, and [C++ Error] SynCommons.hpp(2091, 2): E2141 Declaration syntax error
File:        SynCommon.pas
Solution: In case the matching classes are not needed to be exposed, the following defines can be added:

{$NODEFINE TSynValidateTextProps}
{$NODEFINE TSynValidateText}
{$NODEFINE TSynValidatePassWord}

If not, all the properties of type

property MinLength: cardinal read fProps[0] write fProps[0];

should be corrected (NOTE that *$HppEmit may be used in this context)

Issue:    The classes TScriptState, TScriptAnalysis, TScriptItem and TScriptVisAttr result in the following error: [C++ Error] SynPdf.hpp(1799, 4): E2019 'TScriptState:: :: :: ()' cannot be declared in an anonymous union
File:       SynPdf.pas
Solution: In case the matching classes are not needed to be exposed, the following defines can be added:

  {$NODEFINE PScriptState}
  {$NODEFINE TScriptState}
  {$NODEFINE PScriptAnalysis}
  {$NODEFINE TScriptAnalysis}
  {$NODEFINE PScriptItem}
  {$NODEFINE TScriptItem}
  {$NODEFINE PScriptVisAttr}
  {$NODEFINE TScriptVisAttr}

otherwise need to find a way to not declare the above mentioned record members inside an anonymous union (NOTE that *$HppEmit may be used in this context)

Issue: Some declared constants are in conflict with the VCL, e.g. MWT_IDENTITY
File:   SynPdf.pas
Solution: Do not expose these constants in the hpp file using either the  {$NODEFINE MWT_IDENTITY}, or {$EXTERNALSYM MWT_IDENTITY}

Regards

#16 Re: PDF Engine » Latest SynPdf version breaks the clipping » 2016-04-13 20:30:22

I found that the issue is in relation with this function in SynPdf.pas:

procedure TPdfEnum.ExtSelectClipRgn(data: PRgnDataHeader; iMode: DWord);
var ExtClip: TRect;
begin
  try
    ExtClip := data^.rcBound;
    with DC[nDC] do
    case iMode of
      RGN_COPY: begin
        ClipRgn := MetaRgn;
        ClipRgnNull := False;
      end;
    end;
  except
    on E: Exception do ; // ignore any error (continue EMF enumeration)
  end;
end;

when I revert to:

procedure TPdfEnum.ExtSelectClipRgn(data: PEMRExtSelectClipRgn);
var RGNs: PRgnData;
    i: Integer;
    RCT: TRect;
    ClipRect: TPdfBox;
begin // see http://www.codeproject.com/Articles/1944/Guide-to-WIN-Regions
  if not DC[nDC].ClipRgnNull then begin
    Canvas.GRestore;
    Canvas.NewPath;
    Canvas.fNewPath := False;
    DC[nDC].ClipRgnNull := True;
    fFillColor := -1;
  end;
  if Data^.cbRgnData>0 then begin
    Canvas.GSave;
    Canvas.NewPath;
    DC[nDC].ClipRgnNull := False;
    RGNs := @Data^.RgnData;
    for i := 0 to RGNs^.rdh.nCount-1 do begin
      Move(Rgns^.Buffer[i*SizeOf(TRect)], RCT, SizeOf(RCT));
      Inc(RCT.Bottom);
      ClipRect := Canvas.BoxI(RCT, False);
      Canvas.Rectangle(ClipRect.Left,ClipRect.Top,ClipRect.Width,ClipRect.Height);
    end;
    Canvas.Closepath;
    Canvas.Clip;
    Canvas.NewPath;
    Canvas.FNewPath := False;
  end;
end;

all my clipping works fine again. Would it be possible to fix the bug, or revert to the previous version of this function in the library?

Regards

#17 Re: PDF Engine » About images compression in PDF files » 2016-03-15 12:23:46

Hi ab,

Thank you for your reply, this confirms what I thought.

By the way, it allows me to point a small typo in the comments:

    // - 80/90 is a good ration if you want to have a nice PDF to see on screen

should be

    // - 80/90 is a good ratio if you want to have a nice PDF to see on screen

It's right?

Regards

#18 PDF Engine » About images compression in PDF files » 2016-03-14 22:59:58

jeanmilost
Replies: 3

Hello,

I'm creating a PDF file containing many images. The source for these images are usually a TGraphic or TBitmap, that I draw on the VCLCanvas directly. I have the following questions:
- Are all my images converted internally in JPEG by SynPdf? If not, what is the encoding rule?
- I found a property named ForceJPEGCompression. By setting it to 90, my resulting PDF is 2Kb heavier than an uncompressed PDF (of 3263Kb). Setting to 60, I gain only 31Kb. How this property should be used?
- Are the image streams compressed internally by the SynPdf, e.g. using the ZIP algorithm? If yes, is there a way to change the compression configuration?

Regards

#19 PDF Engine » Latest SynPdf version breaks the clipping » 2016-03-01 13:17:48

jeanmilost
Replies: 9

Hello,

I use the following code to apply a clipping region on my PDF page

    HRGN pClipRegion = NULL;

    try
    {
        ::SelectClipRgn(pCanvas->Handle, NULL);

        pClipRegion = ::CreateRectRgn(rect.Left, rect.Top, rect.Right, rect.Bottom);

        if (::SelectClipRgn(pCanvas->Handle, pClipRegion) == ERROR)
        {
            DWORD error = ::GetLastError();
            ...
        }
        else
            ::SetMetaRgn(pCanvas->Handle);
    }
    __finally
    {
        if (pClipRegion)
            ::DeleteObject(pClipRegion);
    }

Where pCanvas is the canvas provided ny the VCLCanvas property. This code works well using the SynPdf version 1.18. But I recently tried the latest SynPdf version, and I noticed that the above code no longer works, as my page is drawn without clipping at all. What is the reason?

Regards

#20 Re: PDF Engine » Bitmaps are randomly not painted on the VCL canvas » 2016-03-01 13:00:10

Hi ab,

Thanks for your reply

In fact, it seems that the issue comes because the TBitmap canvas needs to be locked before be painted when used inside a threaded context. Here is a post about that: http://qc.embarcadero.com/wc/qcmain.aspx?d=43018

By locking my bitmap canvas the bug no more appeared

Regards

#21 Re: PDF Engine » Bitmaps are randomly not painted on the VCL canvas » 2016-02-26 15:04:02

Hi ab,

Thanks for your reply.

Which version of SynPdf are you using?
=> I downloaded and tried the last version, same issue, unfortunately

Try to disable bitmap hashing - by setting TPdfDocument.ForceNoBitmapReuse property to TRUE ?
=> Yes I tried, unfortunately this changes nothing

From which source are you creating the bitmaps? jpeg? png?
=> All of these, but in the PDF containing the issue, all sources were created from JPEG. However I could reproduce the exactly same issue with PNG files

Also note that the library does not support bitmap transparency yet.
=> I know smile for this reason I "flattened" the image before draw it, using the following code

    // get source bitmap width and height
    const int width  = pSource->Width;
    const int height = pSource->Height;

    // create and configure overlay
    std::auto_ptr<TBitmap> pOverlay(new TBitmap());
    pOverlay->PixelFormat = pSource->PixelFormat;
    pOverlay->SetSize(width, height);

    // fill overlay background
    pOverlay->Canvas->Brush->Color = bgColor.GetColor();
    pOverlay->Canvas->Brush->Style = bsSolid;
    pOverlay->Canvas->FillRect(TRect(0, 0, width, height));

    // configure alpha blending function
    ::BLENDFUNCTION bf;
    bf.BlendOp             = AC_SRC_OVER;
    bf.BlendFlags          = 0;
    bf.SourceConstantAlpha = opacity;
    bf.AlphaFormat         = AC_SRC_ALPHA;

    // alpha blend source bitmap above overlay
    ::AlphaBlend(pOverlay->Canvas->Handle, 0, 0, width, height, pSource->Canvas->Handle, 0, 0, width,
            height, bf);

    // copy back overlay to destination canvas
    pDestination->Draw(pos.X, pos.Y, pOverlay.get());

Where pSource is a TBitmap and pDestination the VCL canvas provided from the PDF document. Of course, the fill overlay background part should be replaced by a BitBlt() from a source background image in case the background is an image and not just a solid color.

An important info is that my PDF document is created inside a thread. However, the PDF document object is entirely created and executed on the thread side, and it has no direct interaction with another thread. But is it possible that some global static variable, existing inside the PdfSyn library, may enter in conflict with my threaded job, and thus cause a such issue?

NOTE I can provide some sample PDF if needed

Regards

#22 PDF Engine » Bitmaps are randomly not painted on the VCL canvas » 2016-02-24 23:06:53

jeanmilost
Replies: 5

Hello,

In a PDF document, I paint about 100 bitmap images. These images are spread over several pages, and I use the Draw() function of the VCL canvas provided by the document to draw these images. However, I noticed that randomly, some images are not painted (the location where the image should appear is simply white). The concerned images contain nothing special regarding to other images that are drawn correctly on the document. Sometimes, the images appear entirely black, or the image object can be selected on the document when opening in Adobe Reader, but the content is entirely white, sometimes nothing appear but a blank area. If I try to recreate the document, the previously missing images appear again, but others are missing, randomly.

Here is the code I use to draw the images:
pDoc->VCLCanvas->Draw(pos.X, pos.Y, pSource);

Where pSource is a 24 bit TBitmap containing the source image. I verified the content of pSource by saving all content on the disk, using pSource->SaveToFile() function. All of the saved files contained the correct image to paint.

Someone already encountered this problem? Is there a problem with the image conversion in the PDF metafile?

Regards

#23 Re: PDF Engine » How to create a web link? » 2016-02-12 12:57:50

Thanks for the answer.

Indeed, Acrobat recognizes automatically the URLs inside a text, on the explicit condition that the URL not exceeds the page and is cut on max 2 lines. Unfortunately, for this reason, the auto-detection cannot be applied in my case, because I need to show a URL in a small rectangle on my page, and the available space is not enough for format the URL in a such manner that it can be recognized by Acrobat or any PDF reader.

What I need is really a kind of alias, that may work as a button. On my rectangle, I want to show the cropped URL or a plain text, that will act as a recognized URL when the user clicks on it. I know that Acrobat is able to create a such "button", so I'm interested to know if a such solution is available in SynPdf.

Regards

#24 PDF Engine » How to create a web link? » 2016-02-11 22:32:48

jeanmilost
Replies: 2

Hello,

I want to create a web link in my PDF document, in a such manner that, when an user click on it in Adobe Acrobat Reader, his web browser opens directly on the clicked link. As it's possible to create a such link in Adobe Acrobat, is there a way to do that in SymPdf, and if yes, how?

Regards

#25 PDF Engine » How to select and draw again on a previously painted page? » 2016-02-05 14:09:48

jeanmilost
Replies: 0

Hello,

I encounter the following situation:
I created a PDF document from a source provided by an user. I don't know in advance nor the content of each page, which can contain many graphic elements, nor the number of pages that my PDF document will contain after the export. Now I want to add a page footer, that shows for each page the current page / total pages numbers. Because the export method is complex, I can not count the total of pages before exporting each page.

To paint the footer, I need to access again the VCL canvas of an already painted page, after all my pages were exported for the first time, because it's the only moment where I know the total page count. I seen that the page catalog is accessible from my PDF document, but I not found a way to select back a previously painted page in the document, and paint again some new text on it. What is the correct way to do that?

Regards

#26 Re: PDF Engine » Cutting a long text on many pages with margins » 2016-02-05 13:48:22

Thank you for your response.

As I know, I use the latest version, however it's difficult to be sure, because the source code shows only 1.18 as version number. For security I will download again from the site and retry.

For the margins, I resolved the question by creating my own margins system. I use the clipping to apply them, and I modified my functions to be able to cut the text itself for each pages.

Regards

#27 PDF Engine » Cutting a long text on many pages with margins » 2016-02-02 15:44:03

jeanmilost
Replies: 2

Hello,

I need to cut a long text into many A4 pages. Each of my page needs a margin of 0.375 inch (36 pixels) on the top and bottom. I use the GDI ::DrawText() function to draw my text on the VCLCanvas provided by the TPdfDocument class.

I tried to calculate the ideal page height respecting the above mentioned constraints, and to apply a clipping rectangle on my VCL canvas by using the GDI clipping functions. I noticed 2 issues:
1. The text located beyond the clipping rect is effectively not drawn on the PDF page but still seems to exist, e.g. I can select and edit it in Adobe Acrobat. (NOTE I also applied the clipping on the metafile by calling the ::SetMetaRgn() function)
2. There is a big difference between the clipping applied in pixels and the real clipping zone in the PDF file. For example I calculated the bottom clipping by searching how many plain text lines can be fitted inside my page after applying margins (see code below). In theory, the text should not be cropped on the middle of a line, because the clipping height can exactly be divided by the number of lines. However, my PDF file shows the last line of my text cropped in the middle on each page

So I have some questions:
- How can I declare margins correctly for each page on my PDF document, respecting the above mentioned constraints? (NOTE I read the pinned post about margins on this forum, but I could not found a solution for my case)
- Why my clipping rect does not exclude the text completely in my PDF document (i.e. why I find a "ghosted" text outside of my clipping rect)? What is the correct way to apply a clipping region on the PDF VCLCanvas?

Regards

PS: For convenience, here is the code I use to apply my clipping region:
pPdfDoc->DefaultPaperSize = Synpdf::psA4;
pPdfDoc->UseUniscribe = true;
pPdfDoc->AddPage();

...

pPdfDoc->VCLCanvas->Font->Name = L"Arial Unicode MS";
pPdfDoc->VCLCanvas->Font->Size = 12;

...

::TEXTMETRIC textMetric;
::GetTextMetrics(pPdfDoc->VCLCanvas->Handle, &textMetric);

const int pageHeight = pPdfDoc->VCLCanvasSize.Height - (36 * 2); // 36 is calculated as follow: 0.375 inch * 96 dpi
const int lineCount = pageHeight / textMetric.tmHeight;
const int clipHeight = 36 + (lineCount * textMetric.tmHeight);
const TRect clipRect(0, 36, pPdfDoc->VCLCanvasSize.Width, clipHeight);

int savedDC = 0;

try
{
    savedDC = ::SaveDC(pPdfDoc->VCLCanvas);

    HRGN pClipRegion = NULL;

    try
    {
        ::SelectClipRgn(pPdfDoc->VCLCanvas->Handle, NULL);

        pClipRegion = ::CreateRectRgn(clipRect.Left, clipRect.Top, clipRect.Right, clipRect.Bottom);

        if (::SelectClipRgn(pPdfDoc->VCLCanvas, pClipRegion) != ERROR)
            ::SetMetaRgn(pPdfDoc->VCLCanvas);
    }
    __finally
    {
        if (pClipRegion)
            ::DeleteObject(pClipRegion);
    }

    ... my page is drawn here ...

}
__finally
{
    if (savedDC)
        ::RestoreDC(pPdfDoc->VCLCanvas, savedDC);
}

#28 Re: PDF Engine » How to change the TPdfCanvas origin? » 2016-01-22 00:16:28

Hi ab,

In fact, this is dependent to the response to my other posts :-). If I can use the Windows DrawText() functions without problems, including for Hebrew and Arabic texts, then yes, using the VCLCanvas alone will be the best solution. Unfortunately, I tried to draw some texts in different languages using VCLCanvas in association with the Windows text functions, and the result was incorrect. I know that is not a problem with my drawing functions because the exactly same code generates a correct result on my screen but not in the PDF document. I tried to play with some properties as e.g. UseUniscribe, without success. The only solution I found until now to generate the correct output texts in Hebrew or Arabic was to use the TPdfCanvas TextOut() function.

In conclusion, if I can find a way to draw all my texts correctly using a VCLCanvas, then yes, I will use it to draw all my document, that's what I want in fact. But in case I cannot, my only alternative is either to draw all my document using TPdfCanvas, in this case I must revise all my drawing engine, or mixing the both canvas, in this case I must manage the different origins, in addition to the problems of text positioning, cropping and word wrapping.

Because I work for a company that requires the support of those languages, I need to have the absolute certainty that my text will be drawn correctly regardless of the language. However I continue to search why my text isn't generated correctly using a TCanvas. I really pray to find an acceptable solution, because this library provides many facilities that I really like.

Regards

#29 Re: PDF Engine » How to change the TPdfCanvas origin? » 2016-01-21 18:34:36

Hi ab,

Thank you for the response.

Ok that what I thought. So I have no alternative but to convert manually all the coordinates from a system to other if I use the both canvas together.

But is that possible to add a such property in the PDF engine in the future?

Regards

#30 PDF Engine » Cropping text with ellipsis » 2016-01-21 18:24:20

jeanmilost
Replies: 1

Hello,

It exists a flag inside the Windows TextOut() function to add ellipsis at the end (or on the middle of) a cropped text. E.g. if I have the text: "This is a demo text", and if there is only enough space to draw the text until "demo", the text will be shown in the following manner: "This is a d...", or "This ...o text" if the ellipsis are configured to be on the middle of the text. I wish to know if there is a similar property when text is drawn using the TextOut() function of the TPdfCanvas.

Regards

#31 PDF Engine » Z ordering question » 2016-01-21 18:10:25

jeanmilost
Replies: 1

Hello,

I need to draw a text above a red rect inside a PDF document. I created a TPdfDocument, then I use the TCanvas provided by the VCLCanvas property to draw the rect and the TPdfCanvas provided by the Canvas property to draw the text. In my code, I paint first the rect, then the text, so normally the output PDF should show a text above a red rect. However the opposite is happening: The text is drawn below the red rect.

Using the above described configuration, is there a way to notify the PDF document that the red rect should be drawn first, and the text above it? (Or in other words, to notify that the VCLCanvas content should be painted first, then the PDF canvas over it?)

Regards

#32 PDF Engine » How to change the TPdfCanvas origin? » 2016-01-21 16:21:12

jeanmilost
Replies: 4

Hello,

In a TPdfDocument, the TPdfCanvas origin is located on the left and bottom of the page, whereas the TCanvas is located on the left and top. I need that the both origins are the same, on the left and top of the document. So is there a way to move the TPdfCanvas origin to the left and top of the document?

Regards

#33 PDF Engine » Non Latin texts are not written correctly in PDF file using VCLCanvas » 2016-01-21 15:03:53

jeanmilost
Replies: 0

Hello,

I try to write some texts written in English, Japanese, Hebrew and Arabic inside a PDF file, using the VCLCanvas property of the TPdfDocument class, and the Window TextOut() function. For that I written a small function, called DrawTextTo(), that takes a TCanvas as parameter. I tried to draw my texts on the screen and inside the PDF document using the exactly same function for the both.

I noticed that the texts inside the generated PDF file contained errors regarding to the non Latin texts. I also tried to enable the UseUniscribe property, but the two ways result in errors:
- Enabling the UseUniscribe property, all my non Latin texts are broken (i.e. the Japanese font size is incorrect, the Hebrew is partially drawn and some chars are randomly drawn, and the Arabic only shows square)
- Disabling the UseUniscribe property, the chars, sometimes the whole sentence, are crossed for Arabic and Hebrew texts

Of course I tried to change the used font family, especially to a configuration that writes the text correctly using the TPdfCanvas functions.

So, what I do wrong here?

Here are the texts I use:
std::wstring WAppSettings::m_EnglishText  = L"Any text... What happen?";
std::wstring WAppSettings::m_JapaneseText = L"任意のテキストには、何が起こりますか?";
std::wstring WAppSettings::m_HebrewText   = L"כל טקסט, מה קורה?";
std::wstring WAppSettings::m_ArabicText   = L"أي نص، ماذا يحدث؟";

Here is the code used to draw text (pCanvas can either be a pointer to a screen canvas or to the VCLCanvas provided in TPdfDocument):
DWORD drawFlags = DT_WORDBREAK | DT_NOPREFIX | DT_VCENTER;

if (rtlLayout)
    drawFlags |= DT_RTLREADING;

if (doCenter)
    drawFlags |= DT_CENTER;

if (singleLine)
    drawFlags |= DT_SINGLELINE;

// configure text brush
pCanvas->Brush->Color = textColor; // a TColor value e.g. clRed, clBlue, ...
pCanvas->Brush->Style = bsClear;

// draw text
::DrawTextW(pCanvas->Handle, text.c_str(), text.length(), &rect, drawFlags);

Regards

#34 PDF Engine » TPdfCanvas TextRect() and MultilineTextRect() not supports Unicode? » 2016-01-21 14:27:09

jeanmilost
Replies: 0

Hello,

I tried to write some texts in Hebrew, Arabic and Japanese inside a PDF file. Using the function TextOut() available in the TPdfCanvas, the texts are written correctly. Now I tried to write the same texts using the TextRect() and MultilineTextRect() available in the same canvas. The result shows only ??? symbols for all non Latin texts. I tried some workarounds, as e.g. converting text to UTF8, but no way.

The texts to write are declared as follow:
std::wstring WAppSettings::m_EnglishText  = L"Any text... What happen?";
std::wstring WAppSettings::m_JapaneseText = L"任意のテキストには、何が起こりますか?";
std::wstring WAppSettings::m_HebrewText   = L"כל טקסט, מה קורה?";
std::wstring WAppSettings::m_ArabicText   = L"أي نص، ماذا يحدث؟";

And here is the code I use to draw the text:
This code works
    pPdfDoc->Canvas->BeginText();
    pPdfDoc->Canvas->TextOut(300, 400, (WideChar*)(WAppSettings::m_EnglishText.c_str()));
    pPdfDoc->Canvas->TextOut(300, 500, (WideChar*)(WAppSettings::m_JapaneseText.c_str()));
    pPdfDoc->Canvas->TextOut(300, 600, (WideChar*)(WAppSettings::m_HebrewText.c_str()));
    pPdfDoc->Canvas->TextOut(300, 700, (WideChar*)(WAppSettings::m_ArabicText.c_str()));
    pPdfDoc->Canvas->EndText();

This code not works
    TPdfRect pdfRect;
    pdfRect.Left   = 300;
    pdfRect.Top    = 400;
    pdfRect.Right  = pdfRect.Left + 100;
    pdfRect.Bottom = pdfRect.Top + 20;

    pPdfDoc->Canvas->BeginText();
    pPdfDoc->Canvas->TextRect(pdfRect, (WideChar*)(WAppSettings::m_EnglishText.c_str()), Synpdf::paLeftJustify, false);
    pdfRect.Top    = 500;
    pdfRect.Bottom = pdfRect.Top + 20;
    pPdfDoc->Canvas->TextRect(pdfRect, (WideChar*)(WAppSettings::m_JapaneseText.c_str()), Synpdf::paLeftJustify, false);
    pdfRect.Top    = 600;
    pdfRect.Bottom = pdfRect.Top + 20;
    pPdfDoc->Canvas->TextRect(pdfRect, (WideChar*)(WAppSettings::m_HebrewText.c_str()), Synpdf::paLeftJustify, false);
    pdfRect.Top    = 700;
    pdfRect.Bottom = pdfRect.Top + 20;
    pPdfDoc->Canvas->TextRect(pdfRect, (WideChar*)(WAppSettings::m_ArabicText.c_str()), Synpdf::paLeftJustify, false);
    pPdfDoc->Canvas->EndText();


So what is the correct way to write the above texts using the TextRect() and MultilineTextRect() functions?

NOTE saying that I only can use TextOut() is not an acceptable solution for me. I need to be able to place the text on multiple lines, crop the text, word wrap it, and so on, whatever the used alphabet.

Regards

Board footer

Powered by FluxBB