Some time ago a customer alerted me that he needed to remove protection from PDF files incoming from his sales network.

He told me that to overcome the problem he had to remove the protection from the PDF manually using an open source tool called QPDF running on a Windows system but that this was not the best solution since his application was resident on an IBM i system. He then asked me if I could check the possibility of installing the tool on the IBM i system to automate the task by including it in the nightly batch jobs.

I immediately tried to solve the problem by writing a small Java program. However, over time, we discovered it could not unprotect all types of PDF. At this point, before getting my hands on the Java code again, I decided to check if it was possible to compile QPDF on the IBM i system.

Since I couldn’t find anything on the internet, I was forced to roll up my sleeves…

This article summarizes this experience by showing the steps necessary to install QPDF on an IBM i system.


QPDF is a free command-line program that is capable of performing a variety of transformations such as linearization (fast web viewing), encryption, and decryption of PDF files. It includes support for making structural changes to your PDFs, including reorganizing pages, creating watermarks, rotating, merging and splitting PDFs through the ability to copy objects from one PDF file into another and to manipulate the list of pages in a PDF file. QPDF also has many options for inspecting or checking PDF files (useful primarily to PDF developers).


QPDF versions tested: 10.3.1, 10.3.2.

Target operating system: IBM i V7R2 (SF99720 level 20296 and SF99717 level 9).

Prerequisite: Fully functional YUM/RPM-based system (for RPM packages installation, Step 3).


Step 1: Start a PASE Environment

Start a PASE for i interactive terminal session from a 5250 session:

CALL PGM(QP2TERM)

Note. Alternatively, if SSH server is configured on your IBM i system, you can use a SSH client to access your IBM i system.


Step 2: Add open source installation directory to PATH environment variable

If not already done, you will need to adjust your path adding the open source installation directory /QOpenSys/pkgs/bin to your PATH environment variable (remember that this setting is temporary for this session only):

# export PATH=/QOpenSys/pkgs/bin:$PATH

You should also add the open source shared libraries installation directory /QOpenSys/pkgs/lib to your LIBPATH environment variable:

# export LIBPATH=/QOpenSys/pkgs/lib:$LIBPATH

Note. Use LIBPATH to specify extra directories to search for shared libraries in addition to the application’s built in search path. These directories are searched before the directories in the application’s search path (PATH vs LIBPATH).


Step 3: Use yum to install required RPM packages

Install tools for a complete development environment:

# yum -y group install "Development tools"

Install header files and libraries for Zlib development:

# yum -y install zlib-devel.ppc64

Install header files and libraries for for handling JPEG image data format:

# yum -y install libjpeg*

Step 4: Download QPDF to your PC

To build from source for Linux or other UNIX/UNIX-like systems, it is generally sufficient to download just the source qpdf-<version>.tar.gz file: qpdf-10.3.2.tar.gz.


Step 5: Upload qpdf-<version>.tar.gz file to IBM i system (<qpdf_upload_dir>)

You can use Netserver network drive, FTP or IBM Access Client Solutions (General -> Integrated File System -> Upload…).


Step 6: Extract files from qpdf-<version>.tar.gz archive

Change current dir to QPDF upload directory:

# cd <qpdf_upload_dir>

Set qpdfver environment variable:

# export qpdfver=10.3.2

Extract files from tar archive:

# tar -xvf qpdf-$qpdfver.tar.gz --no-same-owner

Note. tar tries to take the ownership information from the archive when running as euid 0 (root). If you are using a superuser (like QSECOFR user profile) use --no-same-owner tar option to extract files as yourself (file ownership set to current user, default for ordinary users).

This setting prevents warning message like the following:

tar: qpdf-x.x.x: Cannot change ownership to uid x, gid x: Una chiamata di sistema ha ricevuto un parametro che non è valido.
tar: Exiting with failure status due to previous errors

Note. tar QShell command fails to extract files from archive.


Step 7: Configure QPDF package

Change current dir to QPDF directory:

# cd qpdf-$qpdfver

Set the instruction mode to PPC64:

# export OBJECT_MODE=64

Note. Under AIX, it is critical that you define an environment variable indicating whether you are building 64 or 32 bit libraries. So, if you are building 64-bit libraries and you use a bash derivative shell, you would issue export OBJECT_MODE=64 before starting the configure step.

This setting prevents error messages in make step (ar command) like the following:

ar: 0707-126 [...] is not valid with the current object file mode.
        Use the -X option to specify the desired object mode.
# ./configure --prefix=/QOpenSys/QIBM/UserData/qpdf

Note. By default, make install installs the package’s commands under /usr/local/bin, include files under /usr/local/include, local libraries under /usr/local/lib, and local architecture-independent files, like documentation and manuals, under /usr/local/share.  You can specify an installation prefix other than /usr/local by giving configure the option --prefix=PREFIX. In this article, we wilI use /QOpenSys/QIBM/UserData/qpdf.


Step 8: Modify libtool and fuzz/build.mk files for IBM i compatibility

# sed --in-place='_backup' 's/ECHO="print -r --"/ECHO="echo"/' libtool

Before:

...
70 # An echo program that protects backslashes.
71 ECHO="print -r --"
...

After:

...
70 # An echo program that protects backslashes.
71 ECHO="echo"
...
# sed --in-place='_backup' 's/tar xzf ..\/..\/original-corpus.tar.gz)/tar xzf ..\/..\/original-corpus.tar.gz --no-same-owner)/' fuzz/build.mk

Before:

...
89 	(cd $(CORPUS_DIR); tar xzf ../../original-corpus.tar.gz)
...

After:

...
89 	(cd $(CORPUS_DIR); tar xzf ../../original-corpus.tar.gz --no-same-owner)
...

Step 9: Build QPDF package

# make

This step can take several hours to complete.

Note. You can ignore following warnings:

...: warning: visibility attribute not supported in this configuration; ignored [-Wattributes]

Step 10: Install QPDF package

# make install

Note. make install creates directories that doesn’t exist.


Step 11: Execute qpdf command

# export PATH=/QOpenSys/QIBM/UserData/qpdf/bin:$PATH
# export LIBPATH=/QOpenSys/QIBM/UserData/qpdf/lib:$LIBPATH
# qpdf --version
qpdf version 10.3.2
Run qpdf --copyright to see copyright and license information.

Step 12: Distributing QPDF to other IBM i systems

a. Search for shared libraries required by QPDF:

The AIX/PASE dump command can tell you quite a bit about binaries/executables, shared libraries, and object files. Here we’re using the -X64 flag to tell it we’re looking at a 64-bit object and the -H flag to tell it to dump the object’s header (LIBPATH… How Does It Work?).

# dump -X64 -H /QOpenSys/QIBM/UserData/qpdf/bin/qpdf
/QOpenSys/QIBM/UserData/qpdf/bin/qpdf:

                        ***Sezione loader***
                      Info intestazione loader
VERSIONE#         #SYMtableENT     #RELOCent        LENidSTR
0x00000001       0x000000f0       0x0000102f       0x0000017c

#IMPfilID        OFFidSTR         LENstrTBL        OFFstrTBL
0x00000006       0x000119a8       0x00001c3a       0x00011b24

                        ***Importare stringa file***
INDICE PERCORSO                      BASE                MEMBRO
0      libqpdf/build:/QOpenSys/pkgs/lib:/QOpenSys/pkgs/bin/../lib/gcc/powerpc-ibm-aix6.1.0.0/6.3.0/ppc64:/QOpenSys/pkgs/bin/../lib/gcc/powerpc-ibm-aix6.1.0.0/6.3.0:/QOpenSys/pkgs/bin/../lib/gcc:/QOpenSys/pkgs/bin/../lib/gcc/powerpc-ibm-aix6.1.0.0/6.3.0/../../..:/usr/lib:/lib
1                                    libz.so.1           shr_64.o
2                                    libjpeg.so.8        shr_64.o
3                                    libstdc++.so.6      shr_64.o
4                                    libgcc_s.so.1       shr_64.o
5                                    libc.a              shr_64.o

Note. libc.a is the C runtime library located at /QOpenSys/QIBM/ProdData/OS400/PASE/lib/.

b. Save QPDF objects and required shared libraries to save file:

CRTSAVF FILE(<savflib>/QPDF)
SAV DEV('/QSYS.LIB/<savflib>.LIB/QPDF.FILE') OBJ(('/QOpenSys/QIBM/UserData/qpdf/*') ('/QOpenSys/pkgs/lib/libz.so.1') ('/QOpenSys/pkgs/lib/libjpeg.so.8') ('/QOpenSys/pkgs/lib/libstdc++.so.6') ('/QOpenSys/pkgs/lib/libgcc_s.so.1')) SUBTREE(*ALL)

c. Transfer save file to target IBM i system.

d. Restore objects from save file:

– all objects to original path (/QOpenSys/QIBM/UserData/qpdf):

QSH CMD('mkdir -m 0755 -p -- /QOpenSys/QIBM/UserData/qpdf')
RST DEV('/QSYS.LIB/<savflib>.LIB/QPDF.FILE') OBJ(('/QOpenSys/QIBM/UserData/qpdf') ('/QOpenSys/pkgs/lib')) SUBTREE(*ALL) ALWOBJDIF(*ALL)

– only the necessary objects (command and shared libraries) to new path (/<newpath>/qpdf):

QSH CMD('mkdir -m 0755 -p -- /<newpath>/qpdf')
RST DEV('/QSYS.LIB/<savflib>.LIB/QPDF.FILE') OBJ(('/QOpenSys/QIBM/UserData/qpdf/bin/qpdf' *INCLUDE '/<newpath>/qpdf/qpdf') ('/QOpenSys/pkgs/lib/libz.so.1' *INCLUDE '/<newpath>/qpdf/libz.so.1') ('/QOpenSys/pkgs/lib/libjpeg.so.8' *INCLUDE '/<newpath>/qpdf/libjpeg.so.8') ('/QOpenSys/pkgs/lib/libstdc++.so.6' *INCLUDE '/<newpath>/qpdf/libstdc++.so.6') ('/QOpenSys/pkgs/lib/libgcc_s.so.1' *INCLUDE '/<newpath>/qpdf/libgcc_s.so.1')) SUBTREE(*NONE) ALWOBJDIF(*ALL)

e. (Distributed) QPDF can also be run from QShell:

QSH CMD('export PATH=/<newpath>/qpdf:$PATH; export LIBPATH=/<newpath>/qpdf:$LIBPATH; qpdf --version')

About author

IBM i System Administrator

Leave a Reply

%d bloggers like this: