Last Updated on 13 June 2021 by Roberto De Pedrini
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).
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:
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
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
Step 5: Upload
qpdf-<version>.tar.gz file to IBM i system (
You can use Netserver network drive, FTP or IBM Access Client Solutions (General -> Integrated File System -> Upload…).
Step 6: Extract files from
Change current dir to QPDF upload directory:
# cd <qpdf_upload_dir>
qpdfver environment variable:
# export qpdfver=10.3.2
Extract files from tar archive:
# tar -xvf qpdf-$qpdfver.tar.gz --no-same-owner
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
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
Step 8: Modify libtool and fuzz/build.mk files for IBM i compatibility
# sed --in-place='_backup' 's/ECHO="print -r --"/ECHO="echo"/' libtool
... 70 # An echo program that protects backslashes. 71 ECHO="print -r --" ...
... 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
... 89 (cd $(CORPUS_DIR); tar xzf ../../original-corpus.tar.gz) ...
... 89 (cd $(CORPUS_DIR); tar xzf ../../original-corpus.tar.gz --no-same-owner) ...
Step 9: Build QPDF package
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
make install creates directories that doesn’t exist.
Step 11: Execute
# 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:
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-aix184.108.40.206/6.3.0/ppc64:/QOpenSys/pkgs/bin/../lib/gcc/powerpc-ibm-aix220.127.116.11/6.3.0:/QOpenSys/pkgs/bin/../lib/gcc:/QOpenSys/pkgs/bin/../lib/gcc/powerpc-ibm-aix18.104.22.168/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
libc.a is the C runtime library located at
b. Save QPDF objects and required shared libraries to save file:
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 (
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 (
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')