Tech Articles


Merging PDF files using PoDoFo


One PDF capability that still hasn't been introduced as a native feature in PowerBuilder is the ability to merge PDF files. We're going to look at how we can easily add that capability using the open source (LGPL) PoDoFo library.

If you look at the list of runtime files for PowerBuilder, you'll note that a podofo.dll file is included in the most recent versions as needed when the OEM TX Text Control is used. Initially just the 32 bit version in 2019 R2 and more recently both the 32 and 64 bit versions in 2019 R3. Given that (as far as I know) the version of the TX Text Control didn't change between the initial 2019 release and R2, I'm not sure why it wasn't listed in the initial 2019 release docs, but that might have been a documentation error.  That file is (as I write this) the 0.9.6.0 version of the PoDoFo library. That means we can create another DLL that does a dynamic import on that library that PowerBuilder is including as a runtime and add the merge capability.

The VS 2019 code and PowerBuilder 2019 R3 code sample are available here.

The C++ source code for the DLL is written in VS2019. There is a package manager utility for Visual Studio C++ projects called vcpkg. I used that to pull down the PoDoFo libraries and all the libraries it is dependent on. They are not included in the attached sample code, so you will need to do the same on your system if you want to modify the C++ source code. There is a walkthrough of how to use vcpkg which uses SQLite as an example. You just need to do steps 1 and 2 and do it for podofo instead. Since I'm supporting 32 and 64 bit I did the install of the library (and dependencies) for both platforms using the :<target> option.

The PowerBuilder 2019R3 sample code includes both 32 and 64 bit examples. Note that it appears that TX Text Control ships a debug version of the podofo.dll, so you need to use the debug version of the pdfutils.dll to work with it. Note also that you can't used one of the files you're reading as the output because the library reads the files in as it works via a stream. Therefore output of the merge needs to be a separate file than the source files.

PoDoFo has a lot more capability than just merge. There's some examples in the tools folder of the PoDoFo project.  The C++ code I've provided is based on the podofomerge tool in that project. The pdfutils library can certainly be expanded to include other capability (hence naming it utils rather than just merge). At the moment though merge is all I needed. If you have additional PDF functionality not included in native PowerBuilder that you'd like to see added let me know through a comment below.

[05/17/2021 Update]: Discovered that there is a bug in PoDoFo loading landscape PDFs. I've updated the sample to include a SnapDevelop project that is a C# wrapper around SharpPDF that is then imported into PowerBuilder. The SnapDevelop approach works fine with both landscape and portrait files. The PoDoFO based sample only works with portrait files.

[09/10/2021 Update]: It appears there isn't a bug in PoDoFo.  PoDoFo is dependent on a number of other DLLs, and I had assumed that those were included in the PowerBuilder runtime as PoDoFo was included.  They aren't.  As soon as I included the other DLLs that PoDoFo is dependent on it started working correctly.  Also, I've updated the sample to PowerBuilder 2021.

Comments (26)
Tuesday, May 18 2021

It would be very good if in addition to merging, the power to split and digitally sign the PDFs.

0

Tuesday, Jun 22 2021

It would be nice if could use C# for the DLL so that could make use of SnapDevelop + PB to complete the project.
Thank you.
JX

0
Tuesday, Jun 22 2021

well... saw the SnapDevelop C# version there:
https://community.appeon.com/index.php/codeexchange/powerbuilder/286-pdfutils

Thanks Brace.
JX

0
Tuesday, Jun 22 2021

Thanks for noting that. I updated the code sample back in May, but I forgot to update this blog article. I've done that now.

There are solutions in both C++ (using PoDoFo) and C# (using SharpPDF).

0

Friday, Sep 10 2021

Bruce

I just downloaded the files - I extracted the pdfutilsdemo.zip to c:\pdfutilsdemo - i opened the workspace and it says the following targets could not be opened c:\pdfutildemo\concatpdf.pbt - i have given full access to all users on the folder and its not read only

Any ideas on what to try please

as always thanks in advance

Andrew
andy@lmcomputers.co.uk

0
Friday, Sep 10 2021

update, not sure why but when i did the same thing on another pc with PB installed it worked.

regards

Andrew

0
Friday, Sep 10 2021

Not sure either.

I did update it yesterday to PB 2021. I also included more of the runtimes that podofo needs. That apparently was the issue with merging portrait and lanscape files. I had assumed that since the TX Text Control that is packaged with PowerBuilder also used podofo and podofo ships with it that all the podofo needed runtimes would be included. That is not the case. Once I supplied all the runtimes that podofo needed the merging worked fine regardless of the orientation of the files.

Haven't updated the description for the sample yet as I'm still testing it out.

Comment was last edited 3 years ago by Bruce Armstrong
0

Thursday, Sep 16 2021

Hello Bruce,
I'd like to try this interesting funcionality but I found a problem in Tool -->'.NET DLL Importer' from PB IDE.

I opened your 'concatpdf' demo app and I tried to re-import PDF2Util2.dll file but returns an error (see attachment): "It's impossible to load file or assembly 'netstandar,verion=2.1.0.0,Culture=neutral,PublicKetToken=ccb13.....' or relative dependency".

maybe some dll files are missing or what else?

Attachments

Your account does not have privileges to view attachments in the comment

0
Thursday, Sep 16 2021

Select the .Net Core option in the importer rather than .Net Standard.

That error message is Microsoft's rather obtuse way of telling you that you're attempting to access a DLL that is targeted for a different framework than you're working in.

0

Thursday, Sep 16 2021

fine!
regards

0

Thursday, Sep 16 2021

Hello Bruce,

I get this error when I try to run the pdfsharp version of this. Do you why I get this error?

Thanks

Attachments

Your account does not have privileges to view attachments in the comment

0
Friday, Sep 17 2021

The sample includes a number of assemblies that PDFSharp is dependent on. One of those is System.Text.Encoding.Codepages.dll. Did you include that where you are attempting to use it?

0
Monday, Sep 20 2021

I extracted the PDFUtilsDemo as it was provided and try running it. I see the System.Text.Encoding.Codepages.dll include in the folder.

0
Monday, Sep 20 2021

Sorry, forgot to mention. I am still getting that error message.

0

Monday, Sep 27 2021

Hi Bruce,
So excited you made this utility as we are using a RTE control to merge a bunch of data-records into some RTF templates and want to save them as PDFs. I'm new to using RTE controls, and as far as I can tell, The RTE SaveDocument() method only saves a single "instance" of the document, so I'd need to loop through the data rows and save each document as a separate PDF file. We want to deliver the results as a single PDF file, so I'd need to merge them all back together.

I'm trying out your PODOFO sample app, and it works, but only if the PDF files I choose are in the same folder as the PDFUtils.dll (or maybe the executable folder). If they are elsewhere, the app crashes. I tried putting the DLL (and all the others you included in the zip file) in a folder in my computer's PATH (although pretty far down the list), and I also tried using the full path of where I put the DLL in the external function declaration...

FUNCTION int merge ( string input1, string input2, string output ) LIBRARY "c:\code\test\PDFUtils.dll"

But those didn't work. Is there something else I need to do?

I'll try the SharpPDF approach next, but it had some problems with it loading the system.text.encoding.dll on initial test.

0
Tuesday, Sep 28 2021

Making sure that the DLL and any DLLs it references are in path should be enough.

I didn't look at the sample code before posting this response, but normally with any sample code where I use the GetFileOpenName and/or GetFileSaveName functions and am also calling a DLL I do a GetCurrentDirectory during the application load and then do a ChangeDirectory back to the application directory before calling the DLL. That's because the DLL is usually in the application working directory, and the GetFileOpenName and GetFileSaveName have a habit of changing the working directory to the location of the file(s) that were selected. Using ChangeDirectory back to the application working directory prior to calling the DLL helps PB find it.

0

Tuesday, Sep 28 2021

Hi Bruce,
we have a bunch of pdf file to merge.
Is it possible to pass to 'merge function' all files into string array to make merge all at once?
I tried to modify the pdfutil2 c# source code and reimport the dll but doesn't work, the call at 'merge function' returns code error = 3.

these are my changes at the source (unfortunately I still don't know c# language):
namespace PDFUtil2
{
public class PDFUtils2
{
public int merge(string[] inpFileName, string outFileName)
{

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

PdfDocument outputDocument = new PdfDocument();

foreach (string fileName in inpFileName)
{
PdfDocument inputDocument = PdfReader.Open(fileName, PdfDocumentOpenMode.Import);
for (int idx = 0; idx < inputDocument.PageCount; idx++)
{
PdfPage page = inputDocument.Pages[idx];
outputDocument.AddPage(page);
}
}
outputDocument.Save(outFileName);
return 1;
}
}
}
Thanks

0
Friday, Oct 22 2021

My current project has the need to combine multiple pdf files into 1 combined file also. This project has a Client/Server version and a PowerServer 2021 version. The solution for the C/S version works great. Registering the dll we use in the C/S version to the PowerServer environment is causing issues on some external client's machines.

0
Saturday, Oct 23 2021

Could you be a bit more specific about what you mean by "causing some issues"?

0

Monday, Oct 25 2021

There are users who have standard user privileges on their workstation and the pdfsplitmerge.dll we use fails to register when the PowerServer app starts. We have had their admin manually register the dll at the cmd prompt. Each time the user runs the Web Client app, a Registration Error message appears saying this dll failed to register even when it was manually registered. The big problem is not being able to use this dll to merge the multiple documents. The users are insurance agents and they have the ability to select multiple documents to generate. We use this library to combine the various pdf files into 1 pdf file for convenience.

0
Monday, Oct 25 2021

I'm a bit lost. It sounds like the issue you're having isn't related to the code sample the article refers to. If the issue you're having isn't related to the code sample being discussed perhaps the best place to post about the issue would be the Q&A section.

0
Thursday, Oct 28 2021

Sorry for the confusion. Really what my company is looking for is an example/ability to merge multiple documents. The code you provided works great with 2 pdf files. The application I work on needs the ability to merge more than 2 documents into 1 final pdf file. Any suggestions or examples you have, is greatly appreciated. Thanks!

0
Thursday, Oct 28 2021

I'd just suggest calling this code in a loop. Write a wrapper function in PowerBuilder that takes the array of documents and then loop through them calling this.

0

Tuesday, Dec 21 2021

Thank you Bruce for your code! My company has used it in PB 2021 Build 1288 successfully. We upgraded our application to PB 2021 Build 1311 and the PDF merge works when we run the app within the PB IDE. When the application is deployed to a client/server environment, the same code crashes the application. Do you know if there are new libraries to deploy using PB 2021 1311? We deploy this app to PowerServer 2021 using the PB 2021 1311 and the PDF merging functionality works fine. That's why I wonder if there is a new or different library to include with the client/server version of the app that was missed in our client/server test. Thanks again for this solution!

0
Friday, Dec 24 2021

I just updated to 1311 and recompiled the sample in both 32 and 64 bit and it worked fine for me. Make sure you're including all of the runtime files. Appeon doesn't provide all of the runtime files with their podofo deploy, perhaps because they don't use features that require them. Apparently concatenating the PDF ( particularly in landscape) requires additional runtimes. That threw me for a bit when I first created the sample. The list of runtime files you need to include are listed below. I also updated the sample with both 32 and 64 bit compiled versions of the sample done in 2021 Builder 1311.

brotlicommon.dll
brotlidec.dll
bz2.dll
freetype.dll
jpeg62.dll
libpng16.dll
lzma.dll
PDFUtils.dll
podofo.dll
tiff.dll
zlib1.dll

0
Monday, Jan 03 2022

Thank you Bruce!

0

Find Articles by Tag

Oracle DLL Authorization OrcaScript RibbonBar IDE JSONGenerator ActiveX SQL BLOB Debugging NativePDF Automated Testing Encoding Migration PowerBuilder TLS/SSL Messagging HTTPClient GhostScript InfoMaker OAuth 2.0 Database Connection Performance C# PowerScript (PS) Menu PostgreSQL Import JSON Linux OS JSON Source Control PowerBuilder (Appeon) Platform DataWindow JSON iOS Database Table Schema Database Table OAuth 64-bit DragDrop SQL Server Repository Branch & Merge CrypterObject Trial XML SVN Debugger DevOps Bug JSONParser SOAP Application Expression OLE Script Source Code Web Service Proxy Error CI/CD Model TreeView Event Variable Resize Interface Debug External Functions Array Service COM Database PowerServer Mobile Jenkins .NET DataStore SnapObjects Excel PDFlib Syntax Installation Validation Outlook ODBC SDK TFS PBDOM Windows OS Authentication RichTextEdit Control Elevate Conference PostgreSQL ODBC driver Event Handling UI Git Database Table Data RibbonBar Builder Deployment SnapDevelop SqlExecutor Android Filter DataWindow Graph File Database Profile SqlModelMapper Open Source Testing Export .NET Assembly Azure Windows 10 Icons Mobile License Configuration .NET Std Framework Import Text Design Database Object Class MessageBox UI Themes Export JSON Window RESTClient PowerServer Web Sort REST TortoiseGit Encryption API Data PDF PFC Stored Procedure Transaction Database Painter 32-bit PowerBuilder Compiler Web API Icon WebBrowser Event Handler UI Modernization CoderObject WinAPI DataType Charts