diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2016-11-21 14:40:11 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2016-11-21 14:40:13 +0900 |
commit | 73663385d86042feae8622f5858c1564a8490a10 (patch) | |
tree | 0abdb1ecc4a3d326d52de9bc341c3257272db297 | |
parent | c22b7528b129321263e86fe810243e08766b62f0 (diff) | |
download | qpdf-73663385d86042feae8622f5858c1564a8490a10.tar.gz qpdf-73663385d86042feae8622f5858c1564a8490a10.tar.bz2 qpdf-73663385d86042feae8622f5858c1564a8490a10.zip |
Imported Upstream version 5.0.1
Change-Id: Idc5b8effa25ad1c63f76999f5fd76918e52ecc59
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
47 files changed, 1140 insertions, 365 deletions
@@ -1,3 +1,59 @@ +2013-10-18 Jay Berkenbilt <ejb@ql.org> + + * 5.0.1: release + + * Warn when -accessibility=n is specified with a modern encryption + format (R > 3). Also, accept this flag (and ignore with warning) + with 256-bit encryption. qpdf has always ignored the + accessibility setting with R > 3, but it previously did so + silently. + +2013-10-05 Jay Berkenbilt <ejb@ql.org> + + * Replace operator[] in std::string and std::vector with "at" in + order to get bounds checking. This reduces the chances that + incorrect code will result in data exposure or buffer overruns. + See README.hardening for additional notes. + + * Use cryptographically secure random number generation when + available. See additional notes in README. + + * Replace some assert() calls with std::logic_error exceptions. + Ideally there shouldn't be assert() calls outside of testing. + This change may make a few more potential code errors in handling + invalid data recoverable. + + * Security fix: In places where std::vector<T>(size_t) was used, + either validate that the size parameter is sane or refactor code + to avoid the need to pre-allocate the vector. This reduces the + likelihood of allocating a lot of memory in response to invalid + data in linearization hint streams. + + * Security fix: sanitize /W array in cross reference stream to + avoid a potential integer overflow in a multiplication. It is + unlikely that any exploits were possible from this bug as + additional checks were also performed. + + * Security fix: avoid buffer overrun that could be caused by bogus + data in linearization hint streams. The incorrect code could only + be triggered when checking linearization data, which must be + invoked explicitly. qpdf does not check linearization data when + reading or writing linearized files, but the qpdf --check command + does check linearization data. + + * Security fix: properly handle empty strings in + QPDF_Name::normalizeName. The empty string is not a valid name + and would never be parsed as a name, so there were no known + conditions where this method could be called with an empty string. + + * Security fix: perform additional argument sanity checks when + reading bit streams. + + * Security fix: in QUtil::toUTF8, change bounds checking to avoid + having a pointer point temporarily outside the bounds of an + array. Some compiler optimizations could have made the original + code unsafe. + 2013-07-10 Jay Berkenbilt <ejb@ql.org> * 5.0.0: release @@ -76,8 +76,32 @@ That code has the following license: SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Building on UNIX/Linux -====================== +Building from a pristine checkout +================================= + +When building qpdf from a pristine checkout from version control, +documentation and automatically generated files are not present. +Building on Windows from a pristine checkout is not guaranteed to work +because of issues running autoconf; see README-windows.txt for how to +handle this. For UNIX and UNIX-like systems, you must have some +addditional tools installed to build from the source repository. To +do this, you should run + +./autogen.sh +./configure --enable-doc-maintenance +make +make install + +If you don't have Apache fop and the docbook stylesheets installed, +you won't be able to build documentation. You can omit +--enable-doc-maintenance and produce working qpdf software that passes +its test suite, but make install will fail because the documentation +files won't exist. Depending on your purposes, you can either work +around this or grab the docs from a source distribution. + + +Building from source distribution on UNIX/Linux +=============================================== For UNIX and UNIX-like systems, you can usually get by with just @@ -167,3 +191,22 @@ the test suite fails, test failure detail will be included in the build output. Otherwise, you will have to have access to the qtest.log file from the build to view test failures. The debian packages for qpdf enable this option, for example. + + +Random Number Generation +======================== + +When the qpdf detects either the Windows cryptography API or the +existence of /dev/urandom, /dev/arandom, or /dev/random, it uses them +to generate cryptography secure random numbers. If none of these +conditions are true, the build will fail with an error. It is +possible to configure qpdf with the --enable-insecure-random option, +in which case it will generate random numbers with stdlib's random() +or rand() calls instead. These random numbers are not cryptography +secure, but the qpdf library is fully functional using them. Using +non-secure random numbers means that it's easier in some cases to +guess encryption keys. If you're not generating encrypted files, +there's no advantage to using secure random numbers. + +If you are building qpdf on a platform that qpdf doesn't know how to +generate secure random numbers on, a patch would be welcome. diff --git a/README.hardening b/README.hardening new file mode 100644 index 0000000..a2389af --- /dev/null +++ b/README.hardening @@ -0,0 +1,93 @@ +Avoiding operator[] +=================== + +During a security review by Red Hat security team (specifically +Florian Weimer), it was discovered that qpdf used std::string and +std::vector's operator[], which has no bounds checking by design. +Instead, using those objects' at() method is preferable since it does +bounds checking. Florian has a tool that can detect all uses of these +methods and report them. I have a short perl script that +automatically corrects any such uses. The perl script is not intended +to be general, but it could be reasonably general. The only known +shortcut is that it might not work very well with some cases of nested +[]'s like a[b[c]] or with cases where there are line breaks inside the +brackets. For qpdf's coding style, it worked on all cases reported. + +To use this, obtain htcondor-analyzer, run it, and respond to the +report. Here's what I did. + +sudo aptitude install libclang-dev llvm llvm-dev clang +cd /tmp +git clone https://github.com/fweimer/htcondor-analyzer +# HEAD = 5fa06fc68a9b0677e9de162279185d58ba1e8477 at this writing +cd htcondor-analyzer +make + +in qpdf + +./autogen.sh +/tmp/htcondor-analyzer/create-db +CC=/tmp/htcondor-analyzer/cc CXX=/tmp/htcondor-analyzer/cxx ./configure --disable-shared --disable-werror +# to remove conftest.c +\rm htcondor-analyzer.sqlite +/tmp/htcondor-analyzer/create-db + +Repeat until no more errors: + +/tmp/fix-at.pl is shown below. + +make +/tmp/htcondor-analyzer/report | grep std:: | grep qpdf >| /tmp/r +perl /tmp/fix-at.pl /tmp/r +# move all *.new over the original file. patmv is my script. Can +# also use a for loop. +patmv -f s/.new// **/*.new + +---------- /tmp/fix-at.pl ---------- +#!/usr/bin/env perl +require 5.008; +use warnings; +use strict; +use File::Basename; + +my $whoami = basename($0); + +my %to_fix = (); +while (<>) +{ + chomp; + die unless m/^([^:]+):(\d+):(\d+):\s*(.*)$/; + my ($file, $line, $col, $message) = ($1, $2, $3, $4); + if ($message !~ m/operator\[\]/) + { + warn "skipping $_\n"; + next; + } + push(@{$to_fix{$file}}, [$line, $col, $message]); +} +foreach my $file (sort keys %to_fix) +{ + open(F, "<$file") or die; + my @lines = (<F>); + close(F); + my $last = ""; + my @data = reverse sort { ($a->[0] <=> $b->[0]) || ($a->[1] <=> $b->[1]) } @{$to_fix{$file}}; + foreach my $d (@data) + { + my ($line, $col) = @$d; + next if $last eq "$line:$col"; + $last = "$line:$col"; + die if $line-- < 1; + die if $col-- < 1; + print $lines[$line]; + $lines[$line] =~ s/^(.{$col})([^\[]+)\[([^\]]+)\]/$1$2.at($3)/ or die "$file:$last\n"; + print $lines[$line]; + } + open(F, ">$file.new") or die; + foreach my $line (@lines) + { + print F $line; + } + close(F) or die; +} +-------------------- diff --git a/README.maintainer b/README.maintainer index a7f5a3b..646a9e6 100644 --- a/README.maintainer +++ b/README.maintainer @@ -43,6 +43,9 @@ Release Reminders casting policy in the manual, and ensure that integer types are properly handled. + * Remember to avoid using operator[] with std::string or + std::vector. See README.hardening for details. + * Increment shared library version information as needed (libqpdf/build.mk) @@ -64,6 +64,11 @@ General ======= + * When decrypting files with /R=6, hash_V5 is called more than once + with the same inputs. Caching the results or refactoring to reduce + the number of identical calls could improve performance for + workloads that involve processing large numbers of small files. + * Consider providing a Windows installer for qpdf using NSIS. * Consider adding a method to balance the pages tree. It would call @@ -71,12 +76,11 @@ General and replace the /Pages key of the root dictionary with the new tree. - * Improve the random number seed to make it more secure so that we - have stronger random numbers, particularly when multiple files are - generated in the same second. This code may need to be - OS-specific. Probably we should add a method in QUtil to seed with - a strong random number and call this automatically the first time - QUtil::random() is called. + * Secure random number generation could be made more efficient by + using a local static to ensure a single random device or crypt + provider as long as this can be done in a thread-safe fashion. In + the initial implementation, this is being skipped to avoid having + to add any dependencies on threading libraries. * Study what's required to support savable forms that can be saved by Adobe Reader. Does this require actually signing the document with @@ -1,8 +1,7 @@ -# generated automatically by aclocal 1.11.6 -*- Autoconf -*- +# generated automatically by aclocal 1.14 -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, -# 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, -# Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -12,6 +11,8 @@ # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_include([m4/ax_random_device.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) diff --git a/config-mingw32 b/config-mingw32 index 1591cdf..365344b 100755 --- a/config-mingw32 +++ b/config-mingw32 @@ -1,5 +1,5 @@ #!/bin/sh -./configure --disable-test-compare-images --enable-external-libs --enable-werror --with-windows-wordsize=32 --with-buildrules=mingw +./configure --disable-test-compare-images --enable-external-libs --enable-werror --with-windows-wordsize=32 --with-buildrules=mingw ${1+"$@"} # As of autoconf 2.69 and gcc 4.6, autoconf's configure fails to # recognize that defining _FILE_OFFSET_BITS works with mingw32. # Append to qpdf-config.h rather than passing CPPFLAGS on the diff --git a/config-mingw64 b/config-mingw64 index 68f304e..6438480 100644 --- a/config-mingw64 +++ b/config-mingw64 @@ -7,4 +7,5 @@ RANLIB=x86_64-w64-mingw32-ranlib \ DLLTOOL=x86_64-w64-mingw32-dlltool \ STRIP=x86_64-w64-mingw32-strip \ - OBJDUMP=x86_64-w64-mingw32-objdump + OBJDUMP=x86_64-w64-mingw32-objdump \ + ${1+"$@"} diff --git a/config-msvc b/config-msvc index db0d987..cb00cf6 100755 --- a/config-msvc +++ b/config-msvc @@ -4,8 +4,9 @@ if ! test "$wordsize" = "32" -o "$wordsize" = "64"; then echo "Usage: $0 {32,64}" exit 2 fi +shift objdump= if test "$wordsize" = "64"; then objdump=OBJDUMP=x86_64-w64-mingw32-objdump fi -CC=cl CXX="cl -TP -GR" ./configure --disable-test-compare-images --enable-external-libs --enable-werror --with-windows-wordsize=$wordsize --with-buildrules=msvc $objdump +CC=cl CXX="cl -TP -GR" ./configure --disable-test-compare-images --enable-external-libs --enable-werror --with-windows-wordsize=$wordsize --with-buildrules=msvc $objdump ${1+"$@"} @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for qpdf 5.0.0. +# Generated by GNU Autoconf 2.69 for qpdf 5.0.1. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='qpdf' PACKAGE_TARNAME='qpdf' -PACKAGE_VERSION='5.0.0' -PACKAGE_STRING='qpdf 5.0.0' +PACKAGE_VERSION='5.0.1' +PACKAGE_STRING='qpdf 5.0.1' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -648,6 +648,7 @@ GENDEPS HAVE_LD_VERSION_SCRIPT QPDF_LARGE_FILE_TEST_PATH WINDOWS_WORDSIZE +RANDOM_DEVICE CXXCPP OTOOL64 OTOOL @@ -740,6 +741,8 @@ enable_fast_install with_gnu_ld with_sysroot enable_libtool_lock +enable_insecure_random +with_random enable_external_libs with_windows_wordsize with_large_file_test_path @@ -1309,7 +1312,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures qpdf 5.0.0 to adapt to many kinds of systems. +\`configure' configures qpdf 5.0.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1374,7 +1377,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of qpdf 5.0.0:";; + short | recursive ) echo "Configuration of qpdf 5.0.1:";; esac cat <<\_ACEOF @@ -1387,6 +1390,9 @@ Optional Features: --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) + --enable-insecure-random + whether to use stdlib's random number generator + (default is no) --enable-external-libs whether to use external libraries distribution --disable-largefile omit support for large files --enable-ld-version-script @@ -1414,6 +1420,7 @@ Optional Packages: --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot=DIR Search for dependent libraries within DIR (or the compiler's sysroot if not specified). + --with-random=FILE Use FILE as random number seed [auto-detected] --with-windows-wordsize={32,64} Windows only: whether this is a 32-bit or 64-bit build; required if external-libs are enabled @@ -1508,7 +1515,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -qpdf configure 5.0.0 +qpdf configure 5.0.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2048,7 +2055,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by qpdf $as_me 5.0.0, which was +It was created by qpdf $as_me 5.0.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -14806,6 +14813,116 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu +# Check whether --enable-insecure-random was given. +if test "${enable_insecure_random+set}" = set; then : + enableval=$enable_insecure_random; if test "$enableval" = "yes"; then + qpdf_INSECURE_RANDOM=1; + else + qpdf_INSECURE_RANDOM=0; + fi +else + qpdf_INSECURE_RANDOM=0 +fi + +if test "$qpdf_INSECURE_RANDOM" = "1"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define USE_INSECURE_RANDOM 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +# Check whether --with-random was given. +if test "${with_random+set}" = set; then : + withval=$with_random; RANDOM_DEVICE="$withval" +else + as_ac_File=`$as_echo "ac_cv_file_"/dev/urandom"" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for \"/dev/urandom\"" >&5 +$as_echo_n "checking for \"/dev/urandom\"... " >&6; } +if eval \${$as_ac_File+:} false; then : + $as_echo_n "(cached) " >&6 +else + test "$cross_compiling" = yes && + as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 +if test -r ""/dev/urandom""; then + eval "$as_ac_File=yes" +else + eval "$as_ac_File=no" +fi +fi +eval ac_res=\$$as_ac_File + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_File"\" = x"yes"; then : + RANDOM_DEVICE="/dev/urandom"; +else + as_ac_File=`$as_echo "ac_cv_file_"/dev/arandom"" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for \"/dev/arandom\"" >&5 +$as_echo_n "checking for \"/dev/arandom\"... " >&6; } +if eval \${$as_ac_File+:} false; then : + $as_echo_n "(cached) " >&6 +else + test "$cross_compiling" = yes && + as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 +if test -r ""/dev/arandom""; then + eval "$as_ac_File=yes" +else + eval "$as_ac_File=no" +fi +fi +eval ac_res=\$$as_ac_File + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_File"\" = x"yes"; then : + RANDOM_DEVICE="/dev/arandom"; +else + as_ac_File=`$as_echo "ac_cv_file_"/dev/random"" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for \"/dev/random\"" >&5 +$as_echo_n "checking for \"/dev/random\"... " >&6; } +if eval \${$as_ac_File+:} false; then : + $as_echo_n "(cached) " >&6 +else + test "$cross_compiling" = yes && + as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 +if test -r ""/dev/random""; then + eval "$as_ac_File=yes" +else + eval "$as_ac_File=no" +fi +fi +eval ac_res=\$$as_ac_File + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_File"\" = x"yes"; then : + RANDOM_DEVICE="/dev/random"; +fi + + +fi + +fi + + +fi + + if test "x$RANDOM_DEVICE" != "x" ; then + +$as_echo "#define HAVE_RANDOM_DEVICE 1" >>confdefs.h + + + +cat >>confdefs.h <<_ACEOF +#define RANDOM_DEVICE "$RANDOM_DEVICE" +_ACEOF + + fi + + USE_EXTERNAL_LIBS=0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for whether to use external libraries distribution" >&5 $as_echo_n "checking for whether to use external libraries distribution... " >&6; } @@ -14983,6 +15100,39 @@ fi fi +if test "x$qpdf_INSECURE_RANDOM" != "x1"; then + OLIBS=$LIBS + LIBS="$LIBS Advapi32.lib" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Advapi32 library" >&5 +$as_echo_n "checking for Advapi32 library... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#pragma comment(lib, "crypt32.lib") + #include <windows.h> + #include <Wincrypt.h> + HCRYPTPROV cp; +int +main () +{ +CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_FULL, 0); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + LIBS="$OLIBS -lAdvapi32" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + LIBS=$OLIBS +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + QPDF_LARGE_FILE_TEST_PATH= @@ -16402,7 +16552,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by qpdf $as_me 5.0.0, which was +This file was extended by qpdf $as_me 5.0.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -16468,7 +16618,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -qpdf config.status 5.0.0 +qpdf config.status 5.0.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 22fffc2..af54b6c 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. dnl This config.in requires autoconf 2.5 or greater. AC_PREREQ([2.68]) -AC_INIT([qpdf],[5.0.0]) +AC_INIT([qpdf],[5.0.1]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_FILES([autoconf.mk]) @@ -16,6 +16,23 @@ AC_PROG_CXX AC_HEADER_STDC LT_INIT([win32-dll]) +AC_ARG_ENABLE(insecure-random, + AS_HELP_STRING([--enable-insecure-random], + [whether to use stdlib's random number generator (default is no)]), + [if test "$enableval" = "yes"; then + qpdf_INSECURE_RANDOM=1; + else + qpdf_INSECURE_RANDOM=0; + fi], [qpdf_INSECURE_RANDOM=0]) +if test "$qpdf_INSECURE_RANDOM" = "1"; then + AC_MSG_RESULT(yes) + AC_DEFINE([USE_INSECURE_RANDOM], [1], [Whether to use inscure random numbers]) +else + AC_MSG_RESULT(no) +fi + +AX_RANDOM_DEVICE + USE_EXTERNAL_LIBS=0 AC_MSG_CHECKING(for whether to use external libraries distribution) AC_ARG_ENABLE(external-libs, @@ -54,6 +71,23 @@ if test "$BUILD_INTERNAL_LIBS" = "0"; then AC_SEARCH_LIBS(pcre_compile,pcre,,[MISSING_PCRE=1; MISSING_ANY=1]) fi +if test "x$qpdf_INSECURE_RANDOM" != "x1"; then + OLIBS=$LIBS + LIBS="$LIBS Advapi32.lib" + AC_MSG_CHECKING(for Advapi32 library) + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[#pragma comment(lib, "crypt32.lib") + #include <windows.h> + #include <Wincrypt.h> + HCRYPTPROV cp;]], + [CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_FULL, 0);] + )], + [AC_MSG_RESULT(yes) + LIBS="$OLIBS -lAdvapi32"], + [AC_MSG_RESULT(no) + LIBS=$OLIBS]) +fi + QPDF_LARGE_FILE_TEST_PATH= AC_SUBST(QPDF_LARGE_FILE_TEST_PATH) AC_ARG_WITH(large-file-test-path, @@ -20,7 +20,7 @@ while (<O>) { my $dll = $1; $dll =~ tr/A-Z/a-z/; - next if $dll =~ m/^(kernel32|user32|msvcrt)\.dll$/; + next if $dll =~ m/^(kernel32|user32|msvcrt|advapi32)\.dll$/; $dlls{$dll} = 1; } elsif (m/^Magic.*\((PE.+?)\)/) diff --git a/doc/qpdf-manual.html b/doc/qpdf-manual.html index 956c52a..a22ee5f 100644 --- a/doc/qpdf-manual.html +++ b/doc/qpdf-manual.html @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>QPDF Manual</title><link rel="stylesheet" type="text/css" href="stylesheet.css" /><meta name="generator" content="DocBook XSL Stylesheets V1.76.1" /></head><body><div class="book" title="QPDF Manual"><div class="titlepage"><div><div><h1 class="title"><a id="idp19180656"></a>QPDF Manual</h1></div><div><h2 class="subtitle">For QPDF Version 5.0.0, July 10, 2013</h2></div><div><div class="author"><h3 class="author"><span class="firstname">Jay</span> <span class="surname">Berkenbilt</span></h3></div></div><div><p class="copyright">Copyright © 2005–2013 Jay Berkenbilt</p></div></div><hr /></div><div class="toc"><p><strong>Table of Contents</strong></p><dl><dt><span class="preface"><a href="#acknowledgments">General Information</a></span></dt><dt><span class="chapter"><a href="#ref.overview">1. What is QPDF?</a></span></dt><dt><span class="chapter"><a href="#ref.installing">2. Building and Installing QPDF</a></span></dt><dd><dl><dt><span class="sect1"><a href="#ref.prerequisites">2.1. System Requirements</a></span></dt><dt><span class="sect1"><a href="#ref.building">2.2. Build Instructions</a></span></dt></dl></dd><dt><span class="chapter"><a href="#ref.using">3. Running QPDF</a></span></dt><dd><dl><dt><span class="sect1"><a href="#ref.invocation">3.1. Basic Invocation</a></span></dt><dt><span class="sect1"><a href="#ref.basic-options">3.2. Basic Options</a></span></dt><dt><span class="sect1"><a href="#ref.encryption-options">3.3. Encryption Options</a></span></dt><dt><span class="sect1"><a href="#ref.page-selection">3.4. Page Selection Options</a></span></dt><dt><span class="sect1"><a href="#ref.advanced-transformation">3.5. Advanced Transformation Options</a></span></dt><dt><span class="sect1"><a href="#ref.testing-options">3.6. Testing, Inspection, and Debugging Options</a></span></dt></dl></dd><dt><span class="chapter"><a href="#ref.qdf">4. QDF Mode</a></span></dt><dt><span class="chapter"><a href="#ref.using-library">5. Using the QPDF Library</a></span></dt><dt><span class="chapter"><a href="#ref.design">6. Design and Library Notes</a></span></dt><dd><dl><dt><span class="sect1"><a href="#ref.design.intro">6.1. Introduction</a></span></dt><dt><span class="sect1"><a href="#ref.design-goals">6.2. Design Goals</a></span></dt><dt><span class="sect1"><a href="#ref.casting">6.3. Casting Policy</a></span></dt><dt><span class="sect1"><a href="#ref.encryption">6.4. Encryption</a></span></dt><dt><span class="sect1"><a href="#ref.adding-and-remove-pages">6.5. Adding and Removing Pages</a></span></dt><dt><span class="sect1"><a href="#ref.reserved-objects">6.6. Reserving Object Numbers</a></span></dt><dt><span class="sect1"><a href="#ref.foreign-objects">6.7. Copying Objects From Other PDF Files</a></span></dt><dt><span class="sect1"><a href="#ref.rewriting">6.8. Writing PDF Files</a></span></dt><dt><span class="sect1"><a href="#ref.filtered-streams">6.9. Filtered Streams</a></span></dt></dl></dd><dt><span class="chapter"><a href="#ref.linearization">7. Linearization</a></span></dt><dd><dl><dt><span class="sect1"><a href="#ref.linearization-strategy">7.1. Basic Strategy for Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.linearized.preparation">7.2. Preparing For Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.optimization">7.3. Optimization</a></span></dt><dt><span class="sect1"><a href="#ref.linearization.writing">7.4. Writing Linearized Files</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-data">7.5. Calculating Linearization Data</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-issues">7.6. Known Issues with Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-debugging">7.7. Debugging Note</a></span></dt></dl></dd><dt><span class="chapter"><a href="#ref.object-and-xref-streams">8. Object and Cross-Reference Streams</a></span></dt><dd><dl><dt><span class="sect1"><a href="#ref.object-streams">8.1. Object Streams</a></span></dt><dt><span class="sect1"><a href="#ref.xref-streams">8.2. Cross-Reference Streams</a></span></dt><dd><dl><dt><span class="sect2"><a href="#ref.xref-stream-data">8.2.1. Cross-Reference Stream Data</a></span></dt></dl></dd><dt><span class="sect1"><a href="#ref.object-streams-linearization">8.3. Implications for Linearized Files</a></span></dt><dt><span class="sect1"><a href="#ref.object-stream-implementation">8.4. Implementation Notes</a></span></dt></dl></dd><dt><span class="appendix"><a href="#ref.release-notes">A. Release Notes</a></span></dt><dt><span class="appendix"><a href="#ref.upgrading-to-2.1">B. Upgrading from 2.0 to 2.1</a></span></dt><dt><span class="appendix"><a href="#ref.upgrading-to-3.0">C. Upgrading to 3.0</a></span></dt><dt><span class="appendix"><a href="#ref.upgrading-to-4.0">D. Upgrading to 4.0</a></span></dt></dl></div><div class="preface" title="General Information"><div class="titlepage"><div><div><h2 class="title"><a id="acknowledgments"></a>General Information</h2></div></div></div><p> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>QPDF Manual</title><link rel="stylesheet" type="text/css" href="stylesheet.css" /><meta name="generator" content="DocBook XSL Stylesheets V1.78.1" /></head><body><div class="book"><div class="titlepage"><div><div><h1 class="title"><a id="idp42115008"></a>QPDF Manual</h1></div><div><h2 class="subtitle">For QPDF Version 5.0.1, October 18, 2013</h2></div><div><div class="author"><h3 class="author"><span class="firstname">Jay</span> <span class="surname">Berkenbilt</span></h3></div></div><div><p class="copyright">Copyright © 2005–2013 Jay Berkenbilt</p></div></div><hr /></div><div class="toc"><p><strong>Table of Contents</strong></p><dl class="toc"><dt><span class="preface"><a href="#acknowledgments">General Information</a></span></dt><dt><span class="chapter"><a href="#ref.overview">1. What is QPDF?</a></span></dt><dt><span class="chapter"><a href="#ref.installing">2. Building and Installing QPDF</a></span></dt><dd><dl><dt><span class="sect1"><a href="#ref.prerequisites">2.1. System Requirements</a></span></dt><dt><span class="sect1"><a href="#ref.building">2.2. Build Instructions</a></span></dt></dl></dd><dt><span class="chapter"><a href="#ref.using">3. Running QPDF</a></span></dt><dd><dl><dt><span class="sect1"><a href="#ref.invocation">3.1. Basic Invocation</a></span></dt><dt><span class="sect1"><a href="#ref.basic-options">3.2. Basic Options</a></span></dt><dt><span class="sect1"><a href="#ref.encryption-options">3.3. Encryption Options</a></span></dt><dt><span class="sect1"><a href="#ref.page-selection">3.4. Page Selection Options</a></span></dt><dt><span class="sect1"><a href="#ref.advanced-transformation">3.5. Advanced Transformation Options</a></span></dt><dt><span class="sect1"><a href="#ref.testing-options">3.6. Testing, Inspection, and Debugging Options</a></span></dt></dl></dd><dt><span class="chapter"><a href="#ref.qdf">4. QDF Mode</a></span></dt><dt><span class="chapter"><a href="#ref.using-library">5. Using the QPDF Library</a></span></dt><dt><span class="chapter"><a href="#ref.design">6. Design and Library Notes</a></span></dt><dd><dl><dt><span class="sect1"><a href="#ref.design.intro">6.1. Introduction</a></span></dt><dt><span class="sect1"><a href="#ref.design-goals">6.2. Design Goals</a></span></dt><dt><span class="sect1"><a href="#ref.casting">6.3. Casting Policy</a></span></dt><dt><span class="sect1"><a href="#ref.encryption">6.4. Encryption</a></span></dt><dt><span class="sect1"><a href="#ref.adding-and-remove-pages">6.5. Adding and Removing Pages</a></span></dt><dt><span class="sect1"><a href="#ref.reserved-objects">6.6. Reserving Object Numbers</a></span></dt><dt><span class="sect1"><a href="#ref.foreign-objects">6.7. Copying Objects From Other PDF Files</a></span></dt><dt><span class="sect1"><a href="#ref.rewriting">6.8. Writing PDF Files</a></span></dt><dt><span class="sect1"><a href="#ref.filtered-streams">6.9. Filtered Streams</a></span></dt></dl></dd><dt><span class="chapter"><a href="#ref.linearization">7. Linearization</a></span></dt><dd><dl><dt><span class="sect1"><a href="#ref.linearization-strategy">7.1. Basic Strategy for Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.linearized.preparation">7.2. Preparing For Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.optimization">7.3. Optimization</a></span></dt><dt><span class="sect1"><a href="#ref.linearization.writing">7.4. Writing Linearized Files</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-data">7.5. Calculating Linearization Data</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-issues">7.6. Known Issues with Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-debugging">7.7. Debugging Note</a></span></dt></dl></dd><dt><span class="chapter"><a href="#ref.object-and-xref-streams">8. Object and Cross-Reference Streams</a></span></dt><dd><dl><dt><span class="sect1"><a href="#ref.object-streams">8.1. Object Streams</a></span></dt><dt><span class="sect1"><a href="#ref.xref-streams">8.2. Cross-Reference Streams</a></span></dt><dd><dl><dt><span class="sect2"><a href="#ref.xref-stream-data">8.2.1. Cross-Reference Stream Data</a></span></dt></dl></dd><dt><span class="sect1"><a href="#ref.object-streams-linearization">8.3. Implications for Linearized Files</a></span></dt><dt><span class="sect1"><a href="#ref.object-stream-implementation">8.4. Implementation Notes</a></span></dt></dl></dd><dt><span class="appendix"><a href="#ref.release-notes">A. Release Notes</a></span></dt><dt><span class="appendix"><a href="#ref.upgrading-to-2.1">B. Upgrading from 2.0 to 2.1</a></span></dt><dt><span class="appendix"><a href="#ref.upgrading-to-3.0">C. Upgrading to 3.0</a></span></dt><dt><span class="appendix"><a href="#ref.upgrading-to-4.0">D. Upgrading to 4.0</a></span></dt></dl></div><div class="preface"><div class="titlepage"><div><div><h1 class="title"><a id="acknowledgments"></a>General Information</h1></div></div></div><p> QPDF is a program that does structural, content-preserving transformations on PDF files. QPDF's website is located at <a class="ulink" href="http://qpdf.sourceforge.net/" target="_top">http://qpdf.sourceforge.net/</a>. QPDF's source code is hosted on github at <a class="ulink" href="https://github.com/qpdf/qpdf" target="_top">https://github.com/qpdf/qpdf</a>. @@ -17,7 +16,7 @@ made considerable enhancements to it since that time. I feel fortunate to have worked for people who would make such a decision. This work would not have been possible without their support. - </p></div><div class="chapter" title="Chapter 1. What is QPDF?"><div class="titlepage"><div><div><h2 class="title"><a id="ref.overview"></a>Chapter 1. What is QPDF?</h2></div></div></div><p> + </p></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a id="ref.overview"></a>Chapter 1. What is QPDF?</h1></div></div></div><p> QPDF is a program that does structural, content-preserving transformations on PDF files. It could have been called something like <span class="emphasis"><em>pdf-to-pdf</em></span>. It also provides many useful @@ -47,14 +46,14 @@ your original PDF creation can't handle. For example, many programs generate simple PDF files but can't password-protect them, web-optimize them, or perform other transformations of that type. - </p></div><div class="chapter" title="Chapter 2. Building and Installing QPDF"><div class="titlepage"><div><div><h2 class="title"><a id="ref.installing"></a>Chapter 2. Building and Installing QPDF</h2></div></div></div><div class="toc"><p><strong>Table of Contents</strong></p><dl><dt><span class="sect1"><a href="#ref.prerequisites">2.1. System Requirements</a></span></dt><dt><span class="sect1"><a href="#ref.building">2.2. Build Instructions</a></span></dt></dl></div><p> + </p></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a id="ref.installing"></a>Chapter 2. Building and Installing QPDF</h1></div></div></div><div class="toc"><p><strong>Table of Contents</strong></p><dl class="toc"><dt><span class="sect1"><a href="#ref.prerequisites">2.1. System Requirements</a></span></dt><dt><span class="sect1"><a href="#ref.building">2.2. Build Instructions</a></span></dt></dl></div><p> This chapter describes how to build and install qpdf. Please see also the <code class="filename">README</code> and <code class="filename">INSTALL</code> files in the source distribution. - </p><div class="sect1" title="2.1. System Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.prerequisites"></a>2.1. System Requirements</h2></div></div></div><p> + </p><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.prerequisites"></a>2.1. System Requirements</h2></div></div></div><p> The qpdf package has relatively few external dependencies. In order to build qpdf, the following packages are required: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> zlib: <a class="ulink" href="http://www.zlib.net/" target="_top">http://www.zlib.net/</a> </p></li><li class="listitem"><p> pcre: <a class="ulink" href="http://www.pcre.org/" target="_top">http://www.pcre.org/</a> @@ -94,7 +93,7 @@ this, the following additional requirements are required by the test suite. Note that in no case are these items required to use qpdf. - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> libtiff: <a class="ulink" href="http://www.remotesensing.org/libtiff/" target="_top">http://www.remotesensing.org/libtiff/</a> </p></li><li class="listitem"><p> GhostScript version 8.60 or newer: <a class="ulink" href="http://www.ghostscript.com" target="_top">http://www.ghostscript.com</a> @@ -117,7 +116,7 @@ To build the PDF version of the documentation, you need Apache fop (<a class="ulink" href="http://xml.apache.org/fop/" target="_top">http://xml.apache.org/fop/</a>) version 0.94 or higher. - </p></div><div class="sect1" title="2.2. Build Instructions"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.building"></a>2.2. Build Instructions</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.building"></a>2.2. Build Instructions</h2></div></div></div><p> Building qpdf on UNIX is generally just a matter of running </p><pre class="programlisting">./configure @@ -146,10 +145,10 @@ make hand-crafted non-recursive Makefile that requires gnu make. If you're really interested, please read the comments in the top-level <code class="filename">Makefile</code>. - </p></div></div><div class="chapter" title="Chapter 3. Running QPDF"><div class="titlepage"><div><div><h2 class="title"><a id="ref.using"></a>Chapter 3. Running QPDF</h2></div></div></div><div class="toc"><p><strong>Table of Contents</strong></p><dl><dt><span class="sect1"><a href="#ref.invocation">3.1. Basic Invocation</a></span></dt><dt><span class="sect1"><a href="#ref.basic-options">3.2. Basic Options</a></span></dt><dt><span class="sect1"><a href="#ref.encryption-options">3.3. Encryption Options</a></span></dt><dt><span class="sect1"><a href="#ref.page-selection">3.4. Page Selection Options</a></span></dt><dt><span class="sect1"><a href="#ref.advanced-transformation">3.5. Advanced Transformation Options</a></span></dt><dt><span class="sect1"><a href="#ref.testing-options">3.6. Testing, Inspection, and Debugging Options</a></span></dt></dl></div><p> + </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a id="ref.using"></a>Chapter 3. Running QPDF</h1></div></div></div><div class="toc"><p><strong>Table of Contents</strong></p><dl class="toc"><dt><span class="sect1"><a href="#ref.invocation">3.1. Basic Invocation</a></span></dt><dt><span class="sect1"><a href="#ref.basic-options">3.2. Basic Options</a></span></dt><dt><span class="sect1"><a href="#ref.encryption-options">3.3. Encryption Options</a></span></dt><dt><span class="sect1"><a href="#ref.page-selection">3.4. Page Selection Options</a></span></dt><dt><span class="sect1"><a href="#ref.advanced-transformation">3.5. Advanced Transformation Options</a></span></dt><dt><span class="sect1"><a href="#ref.testing-options">3.6. Testing, Inspection, and Debugging Options</a></span></dt></dl></div><p> This chapter describes how to run the qpdf program from the command line. - </p><div class="sect1" title="3.1. Basic Invocation"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.invocation"></a>3.1. Basic Invocation</h2></div></div></div><p> + </p><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.invocation"></a>3.1. Basic Invocation</h2></div></div></div><p> When running qpdf, the basic invocation is as follows: </p><pre class="programlisting"><span class="command"><strong>qpdf</strong></span><code class="option"> [ <em class="replaceable"><code>options</code></em> ] <em class="replaceable"><code>infilename</code></em> [ <em class="replaceable"><code>outfilename</code></em> ]</code> @@ -174,10 +173,10 @@ make </p><p> Most options require an output file, but some testing or inspection commands do not. These are specifically noted. - </p></div><div class="sect1" title="3.2. Basic Options"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.basic-options"></a>3.2. Basic Options</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.basic-options"></a>3.2. Basic Options</h2></div></div></div><p> The following options are the most common ones and perform commonly needed transformations. - </p><div class="variablelist"><dl><dt><span class="term"><code class="option">--password=password</code></span></dt><dd><p> + </p><div class="variablelist"><dl class="variablelist"><dt><span class="term"><code class="option">--password=password</code></span></dt><dd><p> Specifies a password for accessing encrypted files. </p></dd><dt><span class="term"><code class="option">--linearize</code></span></dt><dd><p> Causes generation of a linearized (web-optimized) output file. @@ -239,7 +238,7 @@ make your terminal's locale. A detailed discussion of this is out of scope for this manual, but just be aware of this issue if you have trouble with a password that contains 8-bit characters. - </p></div><div class="sect1" title="3.3. Encryption Options"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.encryption-options"></a>3.3. Encryption Options</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.encryption-options"></a>3.3. Encryption Options</h2></div></div></div><p> To change the encryption parameters of a file, use the --encrypt flag. The syntax is @@ -260,7 +259,7 @@ make </p><p> If <code class="option"><em class="replaceable"><code>key-length</code></em></code> is 40, the following restriction options are available: - </p><div class="variablelist"><dl><dt><span class="term"><code class="option">--print=[yn]</code></span></dt><dd><p> + </p><div class="variablelist"><dl class="variablelist"><dt><span class="term"><code class="option">--print=[yn]</code></span></dt><dd><p> Determines whether or not to allow printing. </p></dd><dt><span class="term"><code class="option">--modify=[yn]</code></span></dt><dd><p> Determines whether or not to allow document modification. @@ -272,7 +271,7 @@ make </p></dd></dl></div><p> If <code class="option"><em class="replaceable"><code>key-length</code></em></code> is 128, the following restriction options are available: - </p><div class="variablelist"><dl><dt><span class="term"><code class="option">--accessibility=[yn]</code></span></dt><dd><p> + </p><div class="variablelist"><dl class="variablelist"><dt><span class="term"><code class="option">--accessibility=[yn]</code></span></dt><dd><p> Determines whether or not to allow accessibility to visually impaired. </p></dd><dt><span class="term"><code class="option">--extract=[yn]</code></span></dt><dd><p> @@ -281,7 +280,7 @@ make Controls printing access. <code class="option"><em class="replaceable"><code>print-opt</code></em></code> may be one of the following: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> <code class="option">full</code>: allow full printing </p></li><li class="listitem"><p> <code class="option">low</code>: allow low-resolution printing only @@ -293,7 +292,7 @@ make <code class="option"><em class="replaceable"><code>modify-opt</code></em></code> may be one of the following, each of which implies all the options that follow it: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> <code class="option">all</code>: allow full document modification </p></li><li class="listitem"><p> <code class="option">annotate</code>: allow comment authoring and form operations @@ -326,7 +325,7 @@ make AES-based encryption format used is the PDF 2.0 encryption method supported by Acrobat X. the same options are available as with 128 bits with the following exceptions: - </p><div class="variablelist"><dl><dt><span class="term"><code class="option">--use-aes</code></span></dt><dd><p> + </p><div class="variablelist"><dl class="variablelist"><dt><span class="term"><code class="option">--use-aes</code></span></dt><dd><p> This option is not available with 256-bit keys. AES is always used with 256-bit encryption keys. </p></dd><dt><span class="term"><code class="option">--force-V4</code></span></dt><dd><p> @@ -341,7 +340,7 @@ make encrypted in this way. </p></dd></dl></div><p> The default for each permission option is to be fully permissive. - </p></div><div class="sect1" title="3.4. Page Selection Options"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.page-selection"></a>3.4. Page Selection Options</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.page-selection"></a>3.4. Page Selection Options</h2></div></div></div><p> Starting with qpdf 3.0, it is possible to split and merge PDF files by selecting pages from one or more input files. Whatever file is given as the primary input file is used as the starting @@ -393,7 +392,7 @@ make twice. </p><p> Example page ranges: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> <code class="literal">1,3,5-9,15-12</code>: pages 1, 2, 3, 5, 6, 7, 8, 9, 15, 14, 13, and 12. </p></li><li class="listitem"><p> @@ -449,16 +448,16 @@ outfile.pdf</code> <code class="filename">./encrypted.pdf</code> are separated files. These are all corner cases that most users should hopefully never have to be bothered with. - </p></div><div class="sect1" title="3.5. Advanced Transformation Options"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.advanced-transformation"></a>3.5. Advanced Transformation Options</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.advanced-transformation"></a>3.5. Advanced Transformation Options</h2></div></div></div><p> These transformation options control fine points of how qpdf creates the output file. Mostly these are of use only to people who are very familiar with the PDF file format or who are PDF developers. The following options are available: - </p><div class="variablelist"><dl><dt><span class="term"><code class="option">--stream-data=<em class="replaceable"><code>option</code></em></code></span></dt><dd><p> + </p><div class="variablelist"><dl class="variablelist"><dt><span class="term"><code class="option">--stream-data=<em class="replaceable"><code>option</code></em></code></span></dt><dd><p> Controls transformation of stream data. The value of <code class="option"><em class="replaceable"><code>option</code></em></code> may be one of the following: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> <code class="option">compress</code>: recompress stream data when possible (default) </p></li><li class="listitem"><p> @@ -475,7 +474,7 @@ outfile.pdf</code> Controls handling of object streams. The value of <code class="option"><em class="replaceable"><code>mode</code></em></code> may be one of the following: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> <code class="option">preserve</code>: preserve original object streams (default) </p></li><li class="listitem"><p> @@ -616,12 +615,12 @@ outfile.pdf</code> QDF mode is intended for people, mostly developers, who wish to inspect or modify PDF files in a text editor. For details, please see <a class="xref" href="#ref.qdf" title="Chapter 4. QDF Mode">Chapter 4, <em>QDF Mode</em></a>. - </p></div><div class="sect1" title="3.6. Testing, Inspection, and Debugging Options"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.testing-options"></a>3.6. Testing, Inspection, and Debugging Options</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.testing-options"></a>3.6. Testing, Inspection, and Debugging Options</h2></div></div></div><p> These options can be useful for digging into PDF files or for use in automated test suites for software that uses the qpdf library. When any of the options in this section are specified, no output file should be given. The following options are available: - </p><div class="variablelist"><dl><dt><span class="term"><code class="option">--static-id</code></span></dt><dd><p> + </p><div class="variablelist"><dl class="variablelist"><dt><span class="term"><code class="option">--static-id</code></span></dt><dd><p> Causes generation of a fixed value for /ID. This is intended for testing only. Never use it for production files. </p></dd><dt><span class="term"><code class="option">--static-aes-iv</code></span></dt><dd><p> @@ -702,7 +701,7 @@ outfile.pdf</code> attempt to normalize the stream data as if it is a page content stream. This attempt will be made even if it is not a page content stream, in which case it will produce unusable results. - </p></div></div><div class="chapter" title="Chapter 4. QDF Mode"><div class="titlepage"><div><div><h2 class="title"><a id="ref.qdf"></a>Chapter 4. QDF Mode</h2></div></div></div><p> + </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a id="ref.qdf"></a>Chapter 4. QDF Mode</h1></div></div></div><p> In QDF mode, qpdf creates PDF files in what we call <em class="firstterm">QDF form</em>. A PDF file in QDF form, sometimes called a QDF file, is a completely valid PDF file that has @@ -726,7 +725,7 @@ outfile.pdf</code> and writes a repaired file to standard output. </p><p> The following attributes characterize a QDF file: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> All objects appear in numerical order in the PDF file, including when objects appear in object streams. </p></li><li class="listitem"><p> @@ -786,7 +785,7 @@ outfile.pdf</code> </p><p> When <span class="command"><strong>fix-qdf</strong></span> is run, it goes through the file and recomputes the following parts of the file: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> the <code class="literal">/N</code>, <code class="literal">/W</code>, and <code class="literal">/First</code> keys of all object stream dictionaries </p></li><li class="listitem"><p> @@ -800,7 +799,7 @@ outfile.pdf</code> the offset to the cross-reference table or cross-reference stream following the <code class="literal">startxref</code> token </p></li></ul></div><p> - </p></div><div class="chapter" title="Chapter 5. Using the QPDF Library"><div class="titlepage"><div><div><h2 class="title"><a id="ref.using-library"></a>Chapter 5. Using the QPDF Library</h2></div></div></div><p> + </p></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a id="ref.using-library"></a>Chapter 5. Using the QPDF Library</h1></div></div></div><p> The source tree for the qpdf package has an <code class="filename">examples</code> directory that contains a few example programs. The <code class="filename">qpdf/qpdf.cc</code> source @@ -827,7 +826,7 @@ outfile.pdf</code> <span class="type">QPDFWriter</span>) can be used in more than one thread at a time. Multiple threads may simultaneously work with different instances of these and all other QPDF objects. - </p></div><div class="chapter" title="Chapter 6. Design and Library Notes"><div class="titlepage"><div><div><h2 class="title"><a id="ref.design"></a>Chapter 6. Design and Library Notes</h2></div></div></div><div class="toc"><p><strong>Table of Contents</strong></p><dl><dt><span class="sect1"><a href="#ref.design.intro">6.1. Introduction</a></span></dt><dt><span class="sect1"><a href="#ref.design-goals">6.2. Design Goals</a></span></dt><dt><span class="sect1"><a href="#ref.casting">6.3. Casting Policy</a></span></dt><dt><span class="sect1"><a href="#ref.encryption">6.4. Encryption</a></span></dt><dt><span class="sect1"><a href="#ref.adding-and-remove-pages">6.5. Adding and Removing Pages</a></span></dt><dt><span class="sect1"><a href="#ref.reserved-objects">6.6. Reserving Object Numbers</a></span></dt><dt><span class="sect1"><a href="#ref.foreign-objects">6.7. Copying Objects From Other PDF Files</a></span></dt><dt><span class="sect1"><a href="#ref.rewriting">6.8. Writing PDF Files</a></span></dt><dt><span class="sect1"><a href="#ref.filtered-streams">6.9. Filtered Streams</a></span></dt></dl></div><div class="sect1" title="6.1. Introduction"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.design.intro"></a>6.1. Introduction</h2></div></div></div><p> + </p></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a id="ref.design"></a>Chapter 6. Design and Library Notes</h1></div></div></div><div class="toc"><p><strong>Table of Contents</strong></p><dl class="toc"><dt><span class="sect1"><a href="#ref.design.intro">6.1. Introduction</a></span></dt><dt><span class="sect1"><a href="#ref.design-goals">6.2. Design Goals</a></span></dt><dt><span class="sect1"><a href="#ref.casting">6.3. Casting Policy</a></span></dt><dt><span class="sect1"><a href="#ref.encryption">6.4. Encryption</a></span></dt><dt><span class="sect1"><a href="#ref.adding-and-remove-pages">6.5. Adding and Removing Pages</a></span></dt><dt><span class="sect1"><a href="#ref.reserved-objects">6.6. Reserving Object Numbers</a></span></dt><dt><span class="sect1"><a href="#ref.foreign-objects">6.7. Copying Objects From Other PDF Files</a></span></dt><dt><span class="sect1"><a href="#ref.rewriting">6.8. Writing PDF Files</a></span></dt><dt><span class="sect1"><a href="#ref.filtered-streams">6.9. Filtered Streams</a></span></dt></dl></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.design.intro"></a>6.1. Introduction</h2></div></div></div><p> This section was written prior to the implementation of the qpdf package and was subsequently modified to reflect the implementation. In some cases, for purposes of explanation, it @@ -856,7 +855,7 @@ outfile.pdf</code> as there seems to be a shortage of PDF validation tools out there. (This was, in fact, one of the major motivations behind the initial creation of qpdf.) - </p></div><div class="sect1" title="6.2. Design Goals"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.design-goals"></a>6.2. Design Goals</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.design-goals"></a>6.2. Design Goals</h2></div></div></div><p> The QPDF package includes support for reading and rewriting PDF files. It aims to hide from the user details involving object locations, modified (appended) PDF files, the @@ -973,7 +972,7 @@ outfile.pdf</code> password-protected files. QPDF does not enforce encryption parameters and will treat user and owner passwords equivalently. Either password may be used to access an encrypted file. - <sup>[<a id="idp25313984" href="#ftn.idp25313984" class="footnote">1</a>]</sup> + <a href="#ftn.idp48289504" class="footnote" id="idp48289504"><sup class="footnote">[1]</sup></a> <code class="classname">QPDF</code> will allow recovery of a user password given an owner password. The input PDF file must be seekable. (Output files written by <code class="classname">QPDFWriter</code> need @@ -1000,7 +999,7 @@ outfile.pdf</code> </p><p> The following example should clarify how <code class="classname">QPDF</code> processes a simple file. - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Client constructs <code class="classname">QPDF</code> <code class="varname">pdf</code> and calls <code class="function">pdf.processFile("a.pdf");</code>. @@ -1065,7 +1064,7 @@ outfile.pdf</code> As the client continues to request objects, the same process is followed for each new requested object. </p></li></ul></div><p> - </p></div><div class="sect1" title="6.3. Casting Policy"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.casting"></a>6.3. Casting Policy</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.casting"></a>6.3. Casting Policy</h2></div></div></div><p> This section describes the casting policy followed by qpdf's implementation. This is no concern to qpdf's end users and largely of no concern to people writing code that uses qpdf, but @@ -1097,7 +1096,7 @@ outfile.pdf</code> There are a few significant areas where casting is common in the qpdf sources or where casting would be required to quiet higher levels of compiler warnings but is omitted at present: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> <span class="type">char</span> vs. <span class="type">unsigned char</span>. For historical reasons, there are a lot of places in qpdf's internals that deal with <span class="type">unsigned char</span>, which @@ -1204,7 +1203,7 @@ outfile.pdf</code> with signed vs. unsigned integers from resulting in memory overwrites or other issues with potential security implications, though there are never any absolute guarantees. - </p></div><div class="sect1" title="6.4. Encryption"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.encryption"></a>6.4. Encryption</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.encryption"></a>6.4. Encryption</h2></div></div></div><p> Encryption is supported transparently by qpdf. When opening a PDF file, if an encryption dictionary exists, the <code class="classname">QPDF</code> object processes this dictionary using @@ -1245,7 +1244,7 @@ outfile.pdf</code> multiple encryption formats, qpdf will choose the newest format. The only exception to this is that clear-text metadata will be preserved as clear-text if it is that way in the original file. - </p></div><div class="sect1" title="6.5. Adding and Removing Pages"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.adding-and-remove-pages"></a>6.5. Adding and Removing Pages</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.adding-and-remove-pages"></a>6.5. Adding and Removing Pages</h2></div></div></div><p> While qpdf's API has supported adding and modifying objects for some time, version 3.0 introduces specific methods for adding and removing pages. These are largely convenience routines that @@ -1254,7 +1253,7 @@ outfile.pdf</code> manipulation of the <code class="literal">/Pages</code> tree itself. For details, see <code class="function">addPage</code> and surrounding methods in <code class="filename">QPDF.hh</code>. - </p></div><div class="sect1" title="6.6. Reserving Object Numbers"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.reserved-objects"></a>6.6. Reserving Object Numbers</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.reserved-objects"></a>6.6. Reserving Object Numbers</h2></div></div></div><p> Version 3.0 of qpdf introduced the concept of reserved objects. These are seldom needed for ordinary operations, but there are cases in which you may want to add a series of indirect objects @@ -1280,7 +1279,7 @@ outfile.pdf</code> when copying objects from other PDF files, as discussed in <a class="xref" href="#ref.foreign-objects" title="6.7. Copying Objects From Other PDF Files">Section 6.7, “Copying Objects From Other PDF Files”</a>. For an example of how to use reserved objects, search for <code class="function">newReserved</code> in <code class="filename">test_driver.cc</code> in qpdf's sources. - </p></div><div class="sect1" title="6.7. Copying Objects From Other PDF Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.foreign-objects"></a>6.7. Copying Objects From Other PDF Files</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.foreign-objects"></a>6.7. Copying Objects From Other PDF Files</h2></div></div></div><p> Version 3.0 of qpdf introduced the ability to copy objects into a <code class="classname">QPDF</code> object from a different <code class="classname">QPDF</code> object, which we refer to as @@ -1311,7 +1310,7 @@ outfile.pdf</code> <code class="function">QPDF::makeIndirectObject</code>, this method automatically distinguishes between indirect objects in the current file, foreign objects, and direct objects. - </p></div><div class="sect1" title="6.8. Writing PDF Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.rewriting"></a>6.8. Writing PDF Files</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.rewriting"></a>6.8. Writing PDF Files</h2></div></div></div><p> The qpdf library supports file writing of <code class="classname">QPDF</code> objects to PDF files through the <code class="classname">QPDFWriter</code> class. The @@ -1325,9 +1324,9 @@ outfile.pdf</code> exactly accurate, but it provides a correct “notional” idea of how writing works. Look at the code in <code class="classname">QPDFWriter</code> for exact details. - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Initialize state: - </p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><p> next object number = 1 </p></li><li class="listitem"><p> object queue = empty @@ -1349,7 +1348,7 @@ outfile.pdf</code> object onto queue. </p></li><li class="listitem"><p> While there are more objects on the queue: - </p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><p> Pop queue. </p></li><li class="listitem"><p> Look up object's new number <span class="emphasis"><em>n</em></span> in the @@ -1405,7 +1404,7 @@ outfile.pdf</code> xref table. Finally we can write out the trailer dictionary with appropriately computed /ID (see spec, 8.3, File Identifiers), the cross reference table offset, and <code class="literal">%%EOF</code>. - </p></div><div class="sect1" title="6.9. Filtered Streams"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.filtered-streams"></a>6.9. Filtered Streams</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.filtered-streams"></a>6.9. Filtered Streams</h2></div></div></div><p> Support for streams is implemented through the <code class="classname">Pipeline</code> interface which was designed for this package. @@ -1430,18 +1429,18 @@ outfile.pdf</code> filter should write to whatever type of output is required. The <code class="classname">QPDF</code> class has an interface to write raw or filtered stream contents to a given pipeline. - </p></div><div class="footnotes"><br /><hr width="100" align="left" /><div class="footnote"><p><sup>[<a id="ftn.idp25313984" href="#idp25313984" class="para">1</a>] </sup> + </p></div><div class="footnotes"><br /><hr style="width:100; text-align:left;margin-left: 0" /><div id="ftn.idp48289504" class="footnote"><p><a href="#idp48289504" class="para"><sup class="para">[1] </sup></a> As pointed out earlier, the intention is not for qpdf to be used to bypass security on files. but as any open source PDF consumer may be easily modified to bypass basic PDF document security, and qpdf offers may transformations that can do this as well, there seems to be little point in the added complexity of conditionally enforcing document security. - </p></div></div></div><div class="chapter" title="Chapter 7. Linearization"><div class="titlepage"><div><div><h2 class="title"><a id="ref.linearization"></a>Chapter 7. Linearization</h2></div></div></div><div class="toc"><p><strong>Table of Contents</strong></p><dl><dt><span class="sect1"><a href="#ref.linearization-strategy">7.1. Basic Strategy for Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.linearized.preparation">7.2. Preparing For Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.optimization">7.3. Optimization</a></span></dt><dt><span class="sect1"><a href="#ref.linearization.writing">7.4. Writing Linearized Files</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-data">7.5. Calculating Linearization Data</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-issues">7.6. Known Issues with Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-debugging">7.7. Debugging Note</a></span></dt></dl></div><p> + </p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a id="ref.linearization"></a>Chapter 7. Linearization</h1></div></div></div><div class="toc"><p><strong>Table of Contents</strong></p><dl class="toc"><dt><span class="sect1"><a href="#ref.linearization-strategy">7.1. Basic Strategy for Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.linearized.preparation">7.2. Preparing For Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.optimization">7.3. Optimization</a></span></dt><dt><span class="sect1"><a href="#ref.linearization.writing">7.4. Writing Linearized Files</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-data">7.5. Calculating Linearization Data</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-issues">7.6. Known Issues with Linearization</a></span></dt><dt><span class="sect1"><a href="#ref.linearization-debugging">7.7. Debugging Note</a></span></dt></dl></div><p> This chapter describes how <code class="classname">QPDF</code> and <code class="classname">QPDFWriter</code> implement creation and processing of linearized PDFS. - </p><div class="sect1" title="7.1. Basic Strategy for Linearization"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearization-strategy"></a>7.1. Basic Strategy for Linearization</h2></div></div></div><p> + </p><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearization-strategy"></a>7.1. Basic Strategy for Linearization</h2></div></div></div><p> To avoid the incestuous problem of having the qpdf library validate its own linearized files, we have a special linearized file checking mode which can be invoked via <span class="command"><strong>qpdf @@ -1452,7 +1451,7 @@ outfile.pdf</code> validation code was first tested against linearized files created by external tools (Acrobat and pdlin) and then used to validate files created by <code class="classname">QPDFWriter</code> itself. - </p></div><div class="sect1" title="7.2. Preparing For Linearization"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearized.preparation"></a>7.2. Preparing For Linearization</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearized.preparation"></a>7.2. Preparing For Linearization</h2></div></div></div><p> Before creating a linearized PDF file from any other PDF file, the PDF file must be altered such that all page attributes are propagated down to the page level (and not inherited from parents @@ -1469,14 +1468,14 @@ outfile.pdf</code> </p><p> When creating linearized PDF files from optimized PDF files, there are really only a few issues that need to be dealt with: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Creation of hints tables </p></li><li class="listitem"><p> Placing objects in the correct order </p></li><li class="listitem"><p> Filling in offsets and byte sizes </p></li></ul></div><p> - </p></div><div class="sect1" title="7.3. Optimization"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.optimization"></a>7.3. Optimization</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.optimization"></a>7.3. Optimization</h2></div></div></div><p> In order to perform various operations such as linearization and splitting files into pages, it is necessary to know which objects are referenced by which pages, page thumbnails, and root and @@ -1511,7 +1510,7 @@ outfile.pdf</code> Note that pages and thumbnails have different object user types, so the above test on a page will not include objects referenced by the page's thumbnail dictionary and nothing else. - </p></div><div class="sect1" title="7.4. Writing Linearized Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearization.writing"></a>7.4. Writing Linearized Files</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearization.writing"></a>7.4. Writing Linearized Files</h2></div></div></div><p> We will create files with only primary hint streams. We will never write overflow hint streams. (As of PDF version 1.4, Acrobat doesn't either, and they are never necessary.) The hint @@ -1556,7 +1555,7 @@ outfile.pdf</code> Using this strategy, we can write linearized files to a non-seekable output stream with only a single pass to disk or wherever the output is going. - </p></div><div class="sect1" title="7.5. Calculating Linearization Data"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearization-data"></a>7.5. Calculating Linearization Data</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearization-data"></a>7.5. Calculating Linearization Data</h2></div></div></div><p> Once a file is optimized, we have information about which objects access which other objects. We can then process these tables to decide which part (as described in “Linearized PDF Document @@ -1568,7 +1567,7 @@ outfile.pdf</code> thrown if an object is encountered that has not already been queued. (This could happen only if there were a bug in the traversal code used to calculate the linearization data.) - </p></div><div class="sect1" title="7.6. Known Issues with Linearization"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearization-issues"></a>7.6. Known Issues with Linearization</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearization-issues"></a>7.6. Known Issues with Linearization</h2></div></div></div><p> There are a handful of known issues with this linearization code. These issues do not appear to impact the behavior of linearized files which still work as intended: it is possible for a web @@ -1577,7 +1576,7 @@ outfile.pdf</code> create linearized files have many of these same issues. These items make reference to terminology used in the linearization appendix of the PDF specification. - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Thread Dictionary information keys appear in part 4 with the rest of Threads instead of in part 9. Objects in part 9 are not grouped together functionally. @@ -1592,7 +1591,7 @@ outfile.pdf</code> thumbnail hint tables. There are comments in the code about this. </p></li></ul></div><p> - </p></div><div class="sect1" title="7.7. Debugging Note"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearization-debugging"></a>7.7. Debugging Note</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.linearization-debugging"></a>7.7. Debugging Note</h2></div></div></div><p> The <span class="command"><strong>qpdf --show-linearization</strong></span> command can show the complete contents of linearization hint streams. To look at the raw data, you can extract the filtered contents of the @@ -1610,13 +1609,13 @@ my @ch = split(//, $a); map { printf("%08b", ord($_)) } @ch; print "\n"; </pre><p> - </p></div></div><div class="chapter" title="Chapter 8. Object and Cross-Reference Streams"><div class="titlepage"><div><div><h2 class="title"><a id="ref.object-and-xref-streams"></a>Chapter 8. Object and Cross-Reference Streams</h2></div></div></div><div class="toc"><p><strong>Table of Contents</strong></p><dl><dt><span class="sect1"><a href="#ref.object-streams">8.1. Object Streams</a></span></dt><dt><span class="sect1"><a href="#ref.xref-streams">8.2. Cross-Reference Streams</a></span></dt><dd><dl><dt><span class="sect2"><a href="#ref.xref-stream-data">8.2.1. Cross-Reference Stream Data</a></span></dt></dl></dd><dt><span class="sect1"><a href="#ref.object-streams-linearization">8.3. Implications for Linearized Files</a></span></dt><dt><span class="sect1"><a href="#ref.object-stream-implementation">8.4. Implementation Notes</a></span></dt></dl></div><p> + </p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a id="ref.object-and-xref-streams"></a>Chapter 8. Object and Cross-Reference Streams</h1></div></div></div><div class="toc"><p><strong>Table of Contents</strong></p><dl class="toc"><dt><span class="sect1"><a href="#ref.object-streams">8.1. Object Streams</a></span></dt><dt><span class="sect1"><a href="#ref.xref-streams">8.2. Cross-Reference Streams</a></span></dt><dd><dl><dt><span class="sect2"><a href="#ref.xref-stream-data">8.2.1. Cross-Reference Stream Data</a></span></dt></dl></dd><dt><span class="sect1"><a href="#ref.object-streams-linearization">8.3. Implications for Linearized Files</a></span></dt><dt><span class="sect1"><a href="#ref.object-stream-implementation">8.4. Implementation Notes</a></span></dt></dl></div><p> This chapter provides information about the implementation of object stream and cross-reference stream support in qpdf. - </p><div class="sect1" title="8.1. Object Streams"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.object-streams"></a>8.1. Object Streams</h2></div></div></div><p> + </p><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.object-streams"></a>8.1. Object Streams</h2></div></div></div><p> Object streams can contain any regular object except the following: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> stream objects </p></li><li class="listitem"><p> objects with generation > 0 @@ -1641,7 +1640,7 @@ print "\n"; stream with a regular object. </p><p> The object stream dictionary has the following keys: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> <code class="literal">/N</code>: number of objects </p></li><li class="listitem"><p> <code class="literal">/First</code>: byte offset of first object @@ -1668,7 +1667,7 @@ print "\n"; integers, each of which is the object number and the byte offset of the object relative to the first object in the stream, followed by the objects themselves, concatenated. - </p></div><div class="sect1" title="8.2. Cross-Reference Streams"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.xref-streams"></a>8.2. Cross-Reference Streams</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.xref-streams"></a>8.2. Cross-Reference Streams</h2></div></div></div><p> For non-hybrid files, the value following <code class="literal">startxref</code> is the byte offset to the xref stream rather than the word <code class="literal">xref</code>. @@ -1697,7 +1696,7 @@ print "\n"; Since xref streams must be read very early, they may not be encrypted, and the may not contain indirect objects for keys required to read them, which are these: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> <code class="literal">/Type</code>: value <code class="literal">/XRef</code> </p></li><li class="listitem"><p> <code class="literal">/Size</code>: value <span class="emphasis"><em>n+1</em></span>: where @@ -1721,7 +1720,7 @@ print "\n"; The other fields in the xref stream, which may be indirect if desired, are the union of those from the xref table's trailer dictionary. - </p><div class="sect2" title="8.2.1. Cross-Reference Stream Data"><div class="titlepage"><div><div><h3 class="title"><a id="ref.xref-stream-data"></a>8.2.1. Cross-Reference Stream Data</h3></div></div></div><p> + </p><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="ref.xref-stream-data"></a>8.2.1. Cross-Reference Stream Data</h3></div></div></div><p> The stream data is binary and encoded in big-endian byte order. Entries are concatenated, and each entry has a length equal to the total of the entries in <code class="literal">/W</code> above. Each @@ -1734,7 +1733,7 @@ print "\n"; “<code class="literal">0</code>”. </p><p> PDF 1.5 has three field types: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> 0: for free objects. Format: <code class="literal">0 obj next-generation</code>, same as the free table in a traditional cross-reference table @@ -1751,7 +1750,7 @@ print "\n"; It seems standard to have the first entry in the table be <code class="literal">0 0 0</code> instead of <code class="literal">0 0 ffff</code> if there are no deleted objects. - </p></div></div><div class="sect1" title="8.3. Implications for Linearized Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.object-streams-linearization"></a>8.3. Implications for Linearized Files</h2></div></div></div><p> + </p></div></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.object-streams-linearization"></a>8.3. Implications for Linearized Files</h2></div></div></div><p> For linearized files, the linearization dictionary, document catalog, and page objects may not be contained in object streams. </p><p> @@ -1771,7 +1770,7 @@ print "\n"; When numbering objects, all shared objects within both the first and second halves of the linearized files must be numbered consecutively after all normal uncompressed objects in that half. - </p></div><div class="sect1" title="8.4. Implementation Notes"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.object-stream-implementation"></a>8.4. Implementation Notes</h2></div></div></div><p> + </p></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="ref.object-stream-implementation"></a>8.4. Implementation Notes</h2></div></div></div><p> There are three modes for writing object streams: <code class="option">disable</code>, <code class="option">preserve</code>, and <code class="option">generate</code>. In disable mode, we do not generate @@ -1790,10 +1789,33 @@ print "\n"; We do not support creation of hybrid files. When we write files, even in preserve mode, we will lose any xref tables and merge any appended sections. - </p></div></div><div class="appendix" title="Appendix A. Release Notes"><div class="titlepage"><div><div><h2 class="title"><a id="ref.release-notes"></a>Appendix A. Release Notes</h2></div></div></div><p> + </p></div></div><div class="appendix"><div class="titlepage"><div><div><h1 class="title"><a id="ref.release-notes"></a>Appendix A. Release Notes</h1></div></div></div><p> For a detailed list of changes, please see the file <code class="filename">ChangeLog</code> in the source distribution. - </p><div class="variablelist"><dl><dt><span class="term">5.0.0: July 10, 2013</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="variablelist"><dl class="variablelist"><dt><span class="term">5.0.1: October 18, 2013</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> + Thanks to a detailed review by Florian Weimer and the Red Hat + Product Security Team, this release includes a number of + non-user-visible security hardening changes. Please see the + ChangeLog file in the source distribution for the complete + list. + </p></li><li class="listitem"><p> + When available, operating system-specific secure random number + generation is used for generating initialization vectors and + other random values used during encryption or file creation. + For the Windows build, this results in an added dependency on + Microsoft's cryptography API. To disable the OS-specific + cryptography and use the old version, pass the + <code class="option">--enable-insecure-random</code> option to + <span class="command"><strong>./configure</strong></span>. + </p></li><li class="listitem"><p> + The <span class="command"><strong>qpdf</strong></span> command-line tool now issues a + warning when <code class="option">-accessibility=n</code> is specified + for newer encryption versions stating that the option is + ignored. qpdf, per the spec, has always ignored this flag, + but it previously did so silently. This warning is issued + only by the command-line tool, not by the library. The + library's handling of this flag is unchanged. + </p></li></ul></div></dd><dt><span class="term">5.0.0: July 10, 2013</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Bug fix: previous versions of qpdf would lose objects with generation != 0 when generating object streams. Fixing this required changes to the public API. @@ -1825,7 +1847,7 @@ print "\n"; Various enhancements were made to support different types of broken files or broken readers. Details can be found in <code class="filename">ChangeLog</code>. - </p></li></ul></div></dd><dt><span class="term">4.1.0: April 14, 2013</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">4.1.0: April 14, 2013</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Note to people including qpdf in distributions: the <code class="filename">.la</code> files generated by libtool are now installed by qpdf's <span class="command"><strong>make install</strong></span> target. @@ -1836,7 +1858,7 @@ print "\n"; Major enhancement: API enhancements have been made to support parsing of content streams. This enhancement includes the following changes: - </p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><p> <code class="function">QPDFObjectHandle::parseContentStream</code> method parses objects in a content stream and calls handlers in a callback class. The example @@ -1865,7 +1887,7 @@ print "\n"; Minor compilation enhancements have been made to facilitate easier for support for a broader range of compilers and compiler versions. - </p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><p> Warning flags have been moved into a separate variable in <code class="filename">autoconf.mk</code> </p></li><li class="listitem"><p> @@ -1899,7 +1921,7 @@ print "\n"; include qpdf now that it is a dependency of CUPS filters. </p></li><li class="listitem"><p> A few bug fixes are included: - </p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><p> Overridden compressed objects are properly handled. Before, there were certain constructs that could cause qpdf to see old versions of some objects. The most usual @@ -1913,7 +1935,7 @@ print "\n"; The 64-bit mingw Windows binary package no longer includes a 32-bit DLL. </p></li></ul></div><p> - </p></li></ul></div></dd><dt><span class="term">4.0.1: January 17, 2013</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">4.0.1: January 17, 2013</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Fix detection of binary attachments in test suite to avoid false test failures on some platforms. </p></li><li class="listitem"><p> @@ -1926,7 +1948,7 @@ print "\n"; separate encryption key is used on the file, and that key is independently encrypted using both the user password and the owner password. - </p></li></ul></div></dd><dt><span class="term">4.0.0: December 31, 2012</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">4.0.0: December 31, 2012</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Major enhancement: support has been added for newer encryption schemes supported by version X of Adobe Acrobat. This includes use of 127-character passwords, 256-bit encryption @@ -1995,7 +2017,7 @@ print "\n"; didn't matter. The following public parts of the <code class="classname">QPDF</code> class were changed in a non-compatible way: - </p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><p> Updated nested <code class="classname">QPDF::EncryptionData</code> class to add fields needed by the newer encryption formats, member variables changed to private so that future changes @@ -2049,7 +2071,7 @@ print "\n"; Fix the <span class="command"><strong>pkg-config</strong></span> files to list zlib and pcre in <code class="function">Requires.private</code> to better support static linking using <span class="command"><strong>pkg-config</strong></span>. - </p></li></ul></div></dd></dl></div><div class="variablelist"><dl><dt><span class="term">3.0.2: September 6, 2012</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd></dl></div><div class="variablelist"><dl class="variablelist"><dt><span class="term">3.0.2: September 6, 2012</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Bug fix: <code class="function">QPDFWriter::setOutputMemory</code> did not work when not used with <code class="function">QPDFWriter::setStaticID</code>, which made it @@ -2060,7 +2082,7 @@ print "\n"; additional text near the header of the PDF file. The intended use case is to insert comments that may be consumed by a downstream application, though other use cases may exist. - </p></li></ul></div></dd></dl></div><div class="variablelist"><dl><dt><span class="term">3.0.1: August 11, 2012</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd></dl></div><div class="variablelist"><dl class="variablelist"><dt><span class="term">3.0.1: August 11, 2012</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Version 3.0.0 included addition of files for <span class="command"><strong>pkg-config</strong></span>, but this was not mentioned in the release notes. The release notes for 3.0.0 were updated @@ -2070,7 +2092,7 @@ print "\n"; followed by space, qpdf would incorrectly report that it encountered a premature EOF. This bug has been in qpdf since version 2.0. - </p></li></ul></div></dd></dl></div><div class="variablelist"><dl><dt><span class="term">3.0.0: August 2, 2012</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd></dl></div><div class="variablelist"><dl class="variablelist"><dt><span class="term">3.0.0: August 2, 2012</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Acknowledgment: I would like to express gratitude for the contributions of Tobias Hoffmann toward the release of qpdf version 3.0. He is responsible for most of the implementation @@ -2164,7 +2186,7 @@ print "\n"; building with an autobuilder, you can add the <code class="option">--enable-show-failed-test-output</code> option to <span class="command"><strong>./configure</strong></span> to restore the old behavior. - </p></li></ul></div></dd></dl></div><div class="variablelist"><dl><dt><span class="term">2.3.1: December 28, 2011</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd></dl></div><div class="variablelist"><dl class="variablelist"><dt><span class="term">2.3.1: December 28, 2011</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Fix thread-safety problem resulting from non-thread-safe use of the PCRE library. </p></li><li class="listitem"><p> @@ -2174,7 +2196,7 @@ print "\n"; ghostscript to the test suite </p></li><li class="listitem"><p> Fix minor build issue for Visual C++ 2010. - </p></li></ul></div></dd><dt><span class="term">2.3.0: August 11, 2011</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.3.0: August 11, 2011</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Bug fix: when preserving existing encryption on encrypted files with cleartext metadata, older qpdf versions would generate password-protected files with no valid password. @@ -2214,10 +2236,10 @@ print "\n"; <code class="function">qpdf_get_buffer_length</code>, and <code class="function">qpdf_get_buffer</code> to the C API for writing PDF files to a memory buffer instead of a file. - </p></li></ul></div></dd></dl></div><div class="variablelist"><dl><dt><span class="term">2.2.4: June 25, 2011</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd></dl></div><div class="variablelist"><dl class="variablelist"><dt><span class="term">2.2.4: June 25, 2011</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Fix installation and compilation issues; no functionality changes. - </p></li></ul></div></dd><dt><span class="term">2.2.3: April 30, 2011</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.2.3: April 30, 2011</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Handle some damaged streams with incorrect characters following the stream keyword. </p></li><li class="listitem"><p> @@ -2227,12 +2249,12 @@ print "\n"; Enhance error recovery to properly handle files that use object 0 as a regular object, which is specifically disallowed by the spec. - </p></li></ul></div></dd><dt><span class="term">2.2.2: October 4, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.2.2: October 4, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Add new function <code class="function">qpdf_read_memory</code> to the C API to call <code class="function">QPDF::processMemoryFile</code>. This was an omission in qpdf 2.2.1. - </p></li></ul></div></dd><dt><span class="term">2.2.1: October 1, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.2.1: October 1, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Add new method <code class="function">QPDF::setOutputStreams</code> to replace <code class="varname">std::cout</code> and <code class="varname">std::cerr</code> with other streams for generation @@ -2265,7 +2287,7 @@ print "\n"; Implement miscellaneous enhancements to <code class="classname">PointerHolder</code> and <code class="classname">Buffer</code> to support other changes. - </p></li></ul></div></dd><dt><span class="term">2.2.0: August 14, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.2.0: August 14, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Add new methods to <code class="classname">QPDFObjectHandle</code> (<code class="function">newStream</code> and <code class="function">replaceStreamData</code> for creating new @@ -2302,13 +2324,13 @@ print "\n"; Fix a memory leak that would cause loss of a few bytes for every object involved in a cycle of object references. Thanks to Jian Ma for calling my attention to the leak. - </p></li></ul></div></dd><dt><span class="term">2.1.5: April 25, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.1.5: April 25, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Remove restriction of file identifier strings to 16 bytes. This unnecessary restriction was preventing qpdf from being able to encrypt or decrypt files with identifier strings that were not exactly 16 bytes long. The specification imposes no such restriction. - </p></li></ul></div></dd><dt><span class="term">2.1.4: April 18, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.1.4: April 18, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Apply the same padding calculation fix from version 2.1.2 to the main cross reference stream as well. </p></li><li class="listitem"><p> @@ -2317,22 +2339,22 @@ print "\n"; may be errors that qpdf can't check. This should make it less surprising to people when another PDF reader is unable to read a file that qpdf thinks is okay. - </p></li></ul></div></dd><dt><span class="term">2.1.3: March 27, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.1.3: March 27, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Fix bug that could cause a failure when rewriting PDF files that contain object streams with unreferenced objects that in turn reference indirect scalars. </p></li><li class="listitem"><p> Don't complain about (invalid) AES streams that aren't a multiple of 16 bytes. Instead, pad them before decrypting. - </p></li></ul></div></dd><dt><span class="term">2.1.2: January 24, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.1.2: January 24, 2010</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Fix bug in padding around first half cross reference stream in linearized files. The bug could cause an assertion failure when linearizing certain unlucky files. - </p></li></ul></div></dd><dt><span class="term">2.1.1: December 14, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.1.1: December 14, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> No changes in functionality; insert missing include in an internal library header file to support gcc 4.4, and update test suite to ignore broken Adobe Reader installations. - </p></li></ul></div></dd><dt><span class="term">2.1: October 30, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.1: October 30, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> This is the first version of qpdf to include Windows support. On Windows, it is possible to build a DLL. Additionally, a partial C-language API has been introduced, which makes it @@ -2387,40 +2409,40 @@ print "\n"; </p></li><li class="listitem"><p> There have been a handful of non-compatible API changes. For details, see <a class="xref" href="#ref.upgrading-to-2.1" title="Appendix B. Upgrading from 2.0 to 2.1">Appendix B, <em>Upgrading from 2.0 to 2.1</em></a>. - </p></li></ul></div></dd><dt><span class="term">2.0.6: May 3, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.0.6: May 3, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Do not attempt to uncompress streams that have decode parameters we don't recognize. Earlier versions of qpdf would have rejected files with such streams. - </p></li></ul></div></dd><dt><span class="term">2.0.5: March 10, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.0.5: March 10, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Improve error handling in the LZW decoder, and fix a small error introduced in the previous version with regard to handling full tables. The LZW decoder has been more strongly verified in this release. - </p></li></ul></div></dd><dt><span class="term">2.0.4: February 21, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.0.4: February 21, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Include proper support for LZW streams encoded without the “early code change” flag. Special thanks to Atom Smasher who reported the problem and provided an input file compressed in this way, which I did not previously have. </p></li><li class="listitem"><p> Implement some improvements to file recovery logic. - </p></li></ul></div></dd><dt><span class="term">2.0.3: February 15, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.0.3: February 15, 2009</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Compile cleanly with gcc 4.4. </p></li><li class="listitem"><p> Handle strings encoded as UTF-16BE properly. - </p></li></ul></div></dd><dt><span class="term">2.0.2: June 30, 2008</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.0.2: June 30, 2008</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> Update test suite to work properly with a non-<span class="command"><strong>bash</strong></span> <code class="filename">/bin/sh</code> and with Perl 5.10. No changes were made to the actual qpdf source code itself for this release. - </p></li></ul></div></dd><dt><span class="term">2.0.1: May 6, 2008</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.0.1: May 6, 2008</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> No changes in functionality or interface. This release includes fixes to the source code so that qpdf compiles properly and passes its test suite on a broader range of platforms. See <code class="filename">ChangeLog</code> in the source distribution for details. - </p></li></ul></div></dd><dt><span class="term">2.0: April 29, 2008</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p></li></ul></div></dd><dt><span class="term">2.0: April 29, 2008</span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> First public release. - </p></li></ul></div></dd></dl></div></div><div class="appendix" title="Appendix B. Upgrading from 2.0 to 2.1"><div class="titlepage"><div><div><h2 class="title"><a id="ref.upgrading-to-2.1"></a>Appendix B. Upgrading from 2.0 to 2.1</h2></div></div></div><p> + </p></li></ul></div></dd></dl></div></div><div class="appendix"><div class="titlepage"><div><div><h1 class="title"><a id="ref.upgrading-to-2.1"></a>Appendix B. Upgrading from 2.0 to 2.1</h1></div></div></div><p> Although, as a general rule, we like to avoid introducing source-level incompatibilities in qpdf's interface, there were a few non-compatible changes made in this version. A considerable @@ -2428,7 +2450,7 @@ print "\n"; any changes, but in some cases, you may have to update your code. The changes are enumerated here. There are also some new interfaces; for those, please refer to the header files. - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> QPDF's exception handling mechanism now uses <code class="classname">std::logic_error</code> for internal errors and <code class="classname">std::runtime_error</code> for runtime errors in @@ -2479,10 +2501,10 @@ print "\n"; enumerated types and are now defined in the file <code class="filename">qpdf/Constants.h</code>. This enables them to be shared by both the C and C++ interfaces. - </p></li></ul></div></div><div class="appendix" title="Appendix C. Upgrading to 3.0"><div class="titlepage"><div><div><h2 class="title"><a id="ref.upgrading-to-3.0"></a>Appendix C. Upgrading to 3.0</h2></div></div></div><p> + </p></li></ul></div></div><div class="appendix"><div class="titlepage"><div><div><h1 class="title"><a id="ref.upgrading-to-3.0"></a>Appendix C. Upgrading to 3.0</h1></div></div></div><p> For the most part, the API for qpdf version 3.0 is backward compatible with versions 2.1 and later. There are two exceptions: - </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p> + </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p> The method <code class="function">QPDFObjectHandle::replaceStreamData</code> that uses a <code class="classname">StreamDataProvider</code> to provide the @@ -2511,7 +2533,7 @@ print "\n"; previously been smaller types. This change was required to support files larger than two gigabytes in size. </p></li></ul></div><p> - </p></div><div class="appendix" title="Appendix D. Upgrading to 4.0"><div class="titlepage"><div><div><h2 class="title"><a id="ref.upgrading-to-4.0"></a>Appendix D. Upgrading to 4.0</h2></div></div></div><p> + </p></div><div class="appendix"><div class="titlepage"><div><div><h1 class="title"><a id="ref.upgrading-to-4.0"></a>Appendix D. Upgrading to 4.0</h1></div></div></div><p> While version 4.0 includes a few non-compatible API changes, it is very unlikely that anyone's code would have used any of those parts of the API since they generally required information that would @@ -2519,4 +2541,4 @@ print "\n"; you should run into trouble, please see the ChangeLog. See also <a class="xref" href="#ref.release-notes" title="Appendix A. Release Notes">Appendix A, <em>Release Notes</em></a> for a complete list of the non-compatible API changes made in this version. - </p></div></div></body></html> + </p></div></div></body></html>
\ No newline at end of file diff --git a/doc/qpdf-manual.pdf b/doc/qpdf-manual.pdf Binary files differindex a1297d5..0c1dd22 100644 --- a/doc/qpdf-manual.pdf +++ b/doc/qpdf-manual.pdf diff --git a/examples/pdf-bookmarks.cc b/examples/pdf-bookmarks.cc index 5d11fb3..23b7f0c 100644 --- a/examples/pdf-bookmarks.cc +++ b/examples/pdf-bookmarks.cc @@ -31,7 +31,7 @@ void print_lines(std::vector<int>& numbers) { for (unsigned int i = 0; i < numbers.size() - 1; ++i) { - if (numbers[i]) + if (numbers.at(i)) { std::cout << "| "; } diff --git a/examples/pdf-mod-info.cc b/examples/pdf-mod-info.cc index 8bd9f5d..84cb818 100644 --- a/examples/pdf-mod-info.cc +++ b/examples/pdf-mod-info.cc @@ -128,7 +128,7 @@ int main(int argc, char* argv[]) { QTC::TC("examples", "pdf-mod-info -key"); cur_key = argv[i]; - if (! ((cur_key.length() > 0) && (cur_key[0] == '/'))) + if (! ((cur_key.length() > 0) && (cur_key.at(0) == '/'))) { cur_key = "/" + cur_key; } diff --git a/examples/pdf-parse-content.cc b/examples/pdf-parse-content.cc index 0394e93..7f11be0 100644 --- a/examples/pdf-parse-content.cc +++ b/examples/pdf-parse-content.cc @@ -74,7 +74,7 @@ int main(int argc, char* argv[]) usage(); } - QPDFObjectHandle page = pages[pageno-1]; + QPDFObjectHandle page = pages.at(pageno-1); QPDFObjectHandle contents = page.getKey("/Contents"); ParserCallbacks cb; QPDFObjectHandle::parseContentStream(contents, &cb); diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh index 8bad535..cbdc065 100644 --- a/include/qpdf/QUtil.hh +++ b/include/qpdf/QUtil.hh @@ -108,12 +108,18 @@ namespace QUtil QPDF_DLL std::string toUTF8(unsigned long uval); - // Wrapper around random from stdlib. Calls srandom automatically - // the first time it is called. + // If secure random number generation is supported on your + // platform and qpdf was not compiled with insecure random number + // generation, this returns a crytographically secure random + // number. Otherwise it falls back to random from stdlib and + // calls srandom automatically the first time it is called. QPDF_DLL long random(); - // Wrapper around srandom from stdlib. + // Wrapper around srandom from stdlib. Seeds the standard library + // weak random number generator, which is not used if secure + // random number generation is being used. You never need to call + // this method as it is called automatically if needed. QPDF_DLL void srandom(unsigned int seed); diff --git a/libqpdf/BitStream.cc b/libqpdf/BitStream.cc index eb511f7..14eae55 100644 --- a/libqpdf/BitStream.cc +++ b/libqpdf/BitStream.cc @@ -16,6 +16,10 @@ BitStream::reset() { p = start; bit_offset = 7; + if (static_cast<unsigned int>(nbytes) > static_cast<unsigned int>(-1) / 8) + { + throw std::runtime_error("array too large for bitstream"); + } bits_available = 8 * nbytes; } diff --git a/libqpdf/Pl_LZWDecoder.cc b/libqpdf/Pl_LZWDecoder.cc index f7c6fb3..2cc2077 100644 --- a/libqpdf/Pl_LZWDecoder.cc +++ b/libqpdf/Pl_LZWDecoder.cc @@ -1,6 +1,7 @@ #include <qpdf/Pl_LZWDecoder.hh> #include <qpdf/QTC.hh> +#include <qpdf/QUtil.hh> #include <stdexcept> #include <string.h> #include <assert.h> @@ -100,14 +101,23 @@ Pl_LZWDecoder::getFirstChar(int code) { result = static_cast<unsigned char>(code); } - else + else if (code > 257) { - assert(code > 257); unsigned int idx = code - 258; - assert(idx < table.size()); - Buffer& b = table[idx]; + if (idx >= table.size()) + { + throw std::logic_error( + "Pl_LZWDecoder::getFirstChar: table overflow"); + } + Buffer& b = table.at(idx); result = b.getBuffer()[0]; } + else + { + throw std::logic_error( + "Pl_LZWDecoder::getFirstChar called with invalid code (" + + QUtil::int_to_string(code) + ")"); + } return result; } @@ -124,15 +134,24 @@ Pl_LZWDecoder::addToTable(unsigned char next) last_data = tmp; last_size = 1; } - else + else if (this->last_code > 257) { - assert(this->last_code > 257); unsigned int idx = this->last_code - 258; - assert(idx < table.size()); - Buffer& b = table[idx]; + if (idx >= table.size()) + { + throw std::logic_error( + "Pl_LZWDecoder::addToTable: table overflow"); + } + Buffer& b = table.at(idx); last_data = b.getBuffer(); last_size = b.getSize(); } + else + { + throw std::logic_error( + "Pl_LZWDecoder::addToTable called with invalid code (" + + QUtil::int_to_string(this->last_code) + ")"); + } Buffer entry(1 + last_size); unsigned char* new_data = entry.getBuffer(); @@ -219,7 +238,7 @@ Pl_LZWDecoder::handleCode(int code) } else { - Buffer& b = table[code - 258]; + Buffer& b = table.at(code - 258); getNext()->write(b.getBuffer(), b.getSize()); } } diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 73207ec..cae7a7c 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -19,7 +19,7 @@ #include <qpdf/QPDF_Null.hh> #include <qpdf/QPDF_Dictionary.hh> -std::string QPDF::qpdf_version = "5.0.0"; +std::string QPDF::qpdf_version = "5.0.1"; static char const* EMPTY_PDF = "%PDF-1.3\n" @@ -531,7 +531,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) // For xref_table, these will always be small enough to be ints qpdf_offset_t f1 = QUtil::string_to_ll(m2.getMatch(1).c_str()); int f2 = atoi(m2.getMatch(2).c_str()); - char type = m2.getMatch(3)[0]; + char type = m2.getMatch(3).at(0); if (type == 'f') { // Save deleted items until after we've checked the @@ -699,7 +699,26 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj) "Cross-reference stream does not have" " proper /W and /Index keys"); } - std::vector<int> indx; + + int W[3]; + size_t entry_size = 0; + int max_bytes = sizeof(qpdf_offset_t); + for (int i = 0; i < 3; ++i) + { + W[i] = W_obj.getArrayItem(i).getIntValue(); + if (W[i] > max_bytes) + { + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), + "xref stream", xref_offset, + "Cross-reference stream's /W contains" + " impossibly large values"); + } + entry_size += W[i]; + } + long long max_num_entries = + static_cast<unsigned long long>(-1) / entry_size; + + std::vector<long long> indx; if (Index_obj.isArray()) { int n_index = Index_obj.getArrayNItems(); @@ -731,25 +750,29 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj) else { QTC::TC("qpdf", "QPDF xref /Index is null"); - int size = dict.getKey("/Size").getIntValue(); + long long size = dict.getKey("/Size").getIntValue(); indx.push_back(0); indx.push_back(size); } - int num_entries = 0; + long long num_entries = 0; for (unsigned int i = 1; i < indx.size(); i += 2) { - num_entries += indx[i]; - } - - int W[3]; - int entry_size = 0; - for (int i = 0; i < 3; ++i) - { - W[i] = W_obj.getArrayItem(i).getIntValue(); - entry_size += W[i]; + if (indx.at(i) > max_num_entries - num_entries) + { + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), + "xref stream", xref_offset, + "Cross-reference stream claims to contain" + " too many entries: " + + QUtil::int_to_string(indx.at(i)) + " " + + QUtil::int_to_string(max_num_entries) + " " + + QUtil::int_to_string(num_entries)); + } + num_entries += indx.at(i); } + // entry_size and num_entries have both been validated to ensure + // that this multiplication does not cause an overflow. size_t expected_size = entry_size * num_entries; PointerHolder<Buffer> bp = xref_obj.getStreamData(); @@ -777,6 +800,9 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj) bool saw_first_compressed_object = false; + // Actual size vs. expected size check above ensures that we will + // not overflow any buffers here. We know that entry_size * + // num_entries is equal to the size of the buffer. unsigned char const* data = bp->getBuffer(); for (int i = 0; i < num_entries; ++i) { @@ -803,9 +829,9 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj) // based on /Index. The generation number is 0 unless this is // an uncompressed object record, in which case the generation // number appears as the third field. - int obj = indx[cur_chunk] + chunk_count; + int obj = indx.at(cur_chunk) + chunk_count; ++chunk_count; - if (chunk_count >= indx[cur_chunk + 1]) + if (chunk_count >= indx.at(cur_chunk + 1)) { cur_chunk += 2; chunk_count = 0; diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 442678f..ef92e3a 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -712,7 +712,7 @@ QPDFObjectHandle::parse(std::string const& object_str, size_t offset = input->tell(); while (offset < object_str.length()) { - if (! isspace(object_str[offset])) + if (! isspace(object_str.at(offset))) { QTC::TC("qpdf", "QPDFObjectHandle trailing data in parse"); throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), @@ -966,8 +966,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input, std::string const& value = token.getValue(); if ((value == "R") && (in_array || in_dictionary) && (olist.size() >= 2) && - (olist[olist.size() - 1].isInteger()) && - (olist[olist.size() - 2].isInteger())) + (olist.at(olist.size() - 1).isInteger()) && + (olist.at(olist.size() - 2).isInteger())) { if (context == 0) { @@ -979,8 +979,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input, // Try to resolve indirect objects object = newIndirect( context, - olist[olist.size() - 2].getIntValue(), - olist[olist.size() - 1].getIntValue()); + olist.at(olist.size() - 2).getIntValue(), + olist.at(olist.size() - 1).getIntValue()); olist.pop_back(); olist.pop_back(); } @@ -1067,8 +1067,8 @@ QPDFObjectHandle::parseInternal(PointerHolder<InputSource> input, } for (unsigned int i = 0; i < olist.size(); i += 2) { - QPDFObjectHandle key_obj = olist[i]; - QPDFObjectHandle val = olist[i + 1]; + QPDFObjectHandle key_obj = olist.at(i); + QPDFObjectHandle val = olist.at(i + 1); if (! key_obj.isName()) { throw QPDFExc( diff --git a/libqpdf/QPDFTokenizer.cc b/libqpdf/QPDFTokenizer.cc index 19442ba..91b1682 100644 --- a/libqpdf/QPDFTokenizer.cc +++ b/libqpdf/QPDFTokenizer.cc @@ -61,7 +61,7 @@ QPDFTokenizer::resolveLiteral() { PCRE num_re("^[\\+\\-]?(?:\\.\\d+|\\d+(?:\\.\\d+)?)$"); - if ((val.length() > 0) && (val[0] == '/')) + if ((val.length() > 0) && (val.at(0) == '/')) { type = tt_name; // Deal with # in name token. Note: '/' by itself is a @@ -397,8 +397,8 @@ QPDFTokenizer::presentCharacter(char ch) std::string nval; for (unsigned int i = 0; i < val.length(); i += 2) { - num[0] = val[i]; - num[1] = val[i+1]; + num[0] = val.at(i); + num[1] = val.at(i+1); char nch = static_cast<char>(strtol(num, 0, 16)); nval += nch; } diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index 863e753..4e91746 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -462,7 +462,9 @@ QPDFWriter::setEncryptionParameters( if (R > 3) { - // Bit 10 is deprecated and should always be set. + // Bit 10 is deprecated and should always be set. This used + // to mean accessibility. There is no way to disable + // accessibility with R > 3. bits_to_clear.erase(10); } @@ -772,7 +774,11 @@ QPDFWriter::bytesNeeded(unsigned long long n) void QPDFWriter::writeBinary(unsigned long long val, unsigned int bytes) { - assert(bytes <= sizeof(unsigned long long)); + if (bytes > sizeof(unsigned long long)) + { + throw std::logic_error( + "QPDFWriter::writeBinary called with too many bytes"); + } unsigned char data[sizeof(unsigned long long)]; for (unsigned int i = 0; i < bytes; ++i) { @@ -1097,7 +1103,11 @@ QPDFWriter::writeTrailer(trailer_e which, int size, bool xref_stream, qpdf_offset_t pos = this->pipeline->getCount(); writeString(QUtil::int_to_string(prev)); int nspaces = pos - this->pipeline->getCount() + 21; - assert(nspaces >= 0); + if (nspaces < 0) + { + throw std::logic_error( + "QPDFWriter: no padding required in trailer"); + } writePad(nspaces); } } @@ -1561,7 +1571,7 @@ QPDFWriter::writeObjectStreamOffsets(std::vector<qpdf_offset_t>& offsets, } writeString(QUtil::int_to_string(i + first_obj)); writeString(" "); - writeString(QUtil::int_to_string(offsets[i])); + writeString(QUtil::int_to_string(offsets.at(i))); } writeString("\n"); } @@ -1595,7 +1605,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) { // Adjust offsets to skip over comment before first object - first = offsets[0]; + first = offsets.at(0); for (std::vector<qpdf_offset_t>::iterator iter = offsets.begin(); iter != offsets.end(); ++iter) { @@ -2737,7 +2747,7 @@ QPDFWriter::writeLinearized() if (pass == 2) { std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); - int first_page_object = obj_renumber[pages[0].getObjGen()]; + int first_page_object = obj_renumber[pages.at(0).getObjGen()]; int npages = pages.size(); writeString(" /Linearized 1 /L "); @@ -2814,9 +2824,11 @@ QPDFWriter::writeLinearized() // place as in pass 1. writePad(first_xref_end - endpos); - // A failure of this insertion means we didn't allow - // enough padding for the first pass xref stream. - assert(this->pipeline->getCount() == first_xref_end); + if (this->pipeline->getCount() != first_xref_end) + { + throw std::logic_error( + "insufficient padding for first pass xref stream"); + } } writeString("\n"); } @@ -2897,8 +2909,13 @@ QPDFWriter::writeLinearized() // If this assertion fails, maybe we didn't have // enough padding above. - assert(this->pipeline->getCount() == - second_xref_end + hint_length); + if (this->pipeline->getCount() != + second_xref_end + hint_length) + { + throw std::logic_error( + "count mismatch after xref stream;" + " possible insufficient padding?"); + } } } else diff --git a/libqpdf/QPDF_Array.cc b/libqpdf/QPDF_Array.cc index 00903fa..c526174 100644 --- a/libqpdf/QPDF_Array.cc +++ b/libqpdf/QPDF_Array.cc @@ -60,7 +60,7 @@ QPDF_Array::getItem(int n) const throw std::logic_error( "INTERNAL ERROR: bounds error accessing QPDF_Array element"); } - return this->items[n]; + return this->items.at(n); } std::vector<QPDFObjectHandle> const& @@ -74,7 +74,7 @@ QPDF_Array::setItem(int n, QPDFObjectHandle const& oh) { // Call getItem for bounds checking (void) getItem(n); - this->items[n] = oh; + this->items.at(n) = oh; } void diff --git a/libqpdf/QPDF_Name.cc b/libqpdf/QPDF_Name.cc index 9adb25b..0c2082c 100644 --- a/libqpdf/QPDF_Name.cc +++ b/libqpdf/QPDF_Name.cc @@ -16,11 +16,15 @@ QPDF_Name::~QPDF_Name() std::string QPDF_Name::normalizeName(std::string const& name) { + if (name.empty()) + { + return name; + } std::string result; - result += name[0]; + result += name.at(0); for (unsigned int i = 1; i < name.length(); ++i) { - char ch = name[i]; + char ch = name.at(i); // Don't use locale/ctype here; follow PDF spec guidelines. if (strchr("#()<>[]{}/%", ch) || (ch < 33) || (ch > 126)) { diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 0a82f22..5704e83 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -310,7 +310,7 @@ QPDF_Stream::filterable(std::vector<std::string>& filters, for (unsigned int i = 0; i < filters.size(); ++i) { - QPDFObjectHandle decode_item = decode_parms[i]; + QPDFObjectHandle decode_item = decode_parms.at(i); if (decode_item.isNull()) { // okay @@ -318,7 +318,7 @@ QPDF_Stream::filterable(std::vector<std::string>& filters, else if (decode_item.isDictionary()) { if (! understandDecodeParams( - filters[i], decode_item, + filters.at(i), decode_item, predictor, columns, early_code_change)) { filterable = false; diff --git a/libqpdf/QPDF_String.cc b/libqpdf/QPDF_String.cc index 9673661..ca8d3ad 100644 --- a/libqpdf/QPDF_String.cc +++ b/libqpdf/QPDF_String.cc @@ -55,7 +55,7 @@ QPDF_String::unparse(bool force_binary) int consecutive_printable = 0; for (unsigned int i = 0; i < this->val.length(); ++i) { - char ch = this->val[i]; + char ch = this->val.at(i); // Note: do not use locale to determine printability. The // PDF specification accepts arbitrary binary data. Some // locales imply multibyte characters. We'll consider @@ -97,7 +97,7 @@ QPDF_String::unparse(bool force_binary) result += "("; for (unsigned int i = 0; i < this->val.length(); ++i) { - char ch = this->val[i]; + char ch = this->val.at(i); switch (ch) { case '\n': @@ -135,7 +135,7 @@ QPDF_String::unparse(bool force_binary) default: if (is_iso_latin1_printable(ch)) { - result += this->val[i]; + result += this->val.at(i); } else { @@ -164,7 +164,7 @@ QPDF_String::getUTF8Val() const std::string result; size_t len = this->val.length(); if ((len >= 2) && (len % 2 == 0) && - (this->val[0] == '\xfe') && (this->val[1] == '\xff')) + (this->val.at(0) == '\xfe') && (this->val.at(1) == '\xff')) { // This is a Unicode string using big-endian UTF-16. This // code uses unsigned long and unsigned short to hold @@ -181,8 +181,8 @@ QPDF_String::getUTF8Val() const // discarded, and a low codepoint not preceded by a high // codepoint will just get its low 10 bits output. unsigned short bits = - (static_cast<unsigned char>(this->val[i]) << 8) + - static_cast<unsigned char>(this->val[i+1]); + (static_cast<unsigned char>(this->val.at(i)) << 8) + + static_cast<unsigned char>(this->val.at(i+1)); if ((bits & 0xFC00) == 0xD800) { codepoint = 0x10000 + ((bits & 0x3FF) << 10); @@ -209,7 +209,7 @@ QPDF_String::getUTF8Val() const { for (unsigned int i = 0; i < len; ++i) { - result += QUtil::toUTF8(static_cast<unsigned char>(this->val[i])); + result += QUtil::toUTF8(static_cast<unsigned char>(this->val.at(i))); } } return result; diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc index 88cd707..b6a035e 100644 --- a/libqpdf/QPDF_encryption.cc +++ b/libqpdf/QPDF_encryption.cc @@ -312,7 +312,7 @@ hash_V5(std::string const& password, int E_mod_3 = 0; for (unsigned int i = 0; i < 16; ++i) { - E_mod_3 += static_cast<unsigned char>(E[i]); + E_mod_3 += static_cast<unsigned char>(E.at(i)); } E_mod_3 %= 3; int next_hash = ((E_mod_3 == 0) ? 256 : diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc index 2c4fefc..4e71f8d 100644 --- a/libqpdf/QPDF_linearization.cc +++ b/libqpdf/QPDF_linearization.cc @@ -23,12 +23,21 @@ static void load_vector_int(BitStream& bit_stream, int nitems, std::vector<T>& vec, int bits_wanted, int_type T::*field) { + bool append = vec.empty(); // nitems times, read bits_wanted from the given bit stream, // storing results in the ith vector entry. for (int i = 0; i < nitems; ++i) { - vec[i].*field = bit_stream.getBits(bits_wanted); + if (append) + { + vec.push_back(T()); + } + vec.at(i).*field = bit_stream.getBits(bits_wanted); + } + if (static_cast<int>(vec.size()) != nitems) + { + throw std::logic_error("vector has wrong size in load_vector_int"); } // The PDF spec says that each hint table starts at a byte // boundary. Each "row" actually must start on a byte boundary. @@ -45,9 +54,9 @@ load_vector_vector(BitStream& bit_stream, // into the vec2 vector field of the ith item of vec1. for (int i1 = 0; i1 < nitems1; ++i1) { - for (int i2 = 0; i2 < vec1[i1].*nitems2; ++i2) + for (int i2 = 0; i2 < vec1.at(i1).*nitems2; ++i2) { - (vec1[i1].*vec2).push_back(bit_stream.getBits(bits_wanted)); + (vec1.at(i1).*vec2).push_back(bit_stream.getBits(bits_wanted)); } } bit_stream.skipToNextByte(); @@ -228,8 +237,8 @@ QPDF::readLinearizationData() } // H: hint table offset/length for primary and overflow hint tables - int H0_offset = H_items[0]; - int H0_length = H_items[1]; + int H0_offset = H_items.at(0); + int H0_length = H_items.at(1); int H1_offset = 0; int H1_length = 0; if (H_items.size() == 4) @@ -237,8 +246,8 @@ QPDF::readLinearizationData() // Acrobat doesn't read or write these (as PDF 1.4), so we // don't have a way to generate a test case. // QTC::TC("qpdf", "QPDF overflow hint table"); - H1_offset = H_items[2]; - H1_length = H_items[3]; + H1_offset = H_items.at(2); + H1_length = H_items.at(3); } // P: first page number @@ -255,6 +264,17 @@ QPDF::readLinearizationData() // Store linearization parameter data + // Various places in the code use linp.npages, which is + // initialized from N, to pre-allocate memory, so make sure it's + // accurate and bail right now if it's not. + if (N.getIntValue() != static_cast<long long>(getAllPages().size())) + { + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), + "linearization hint table", + this->file->getLastOffset(), + "/N does not match number of pages"); + } + // file_size initialized by isLinearized() this->linp.first_page_object = O.getIntValue(); this->linp.first_page_end = E.getIntValue(); @@ -295,11 +315,25 @@ QPDF::readLinearizationData() readHPageOffset(BitStream(h_buf, h_size)); int HSi = HS.getIntValue(); + if ((HSi < 0) || (HSi >= h_size)) + { + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), + "linearization hint table", + this->file->getLastOffset(), + "/S (shared object) offset is out of bounds"); + } readHSharedObject(BitStream(h_buf + HSi, h_size - HSi)); if (HO.isInteger()) { int HOi = HO.getIntValue(); + if ((HOi < 0) || (HOi >= h_size)) + { + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), + "linearization hint table", + this->file->getLastOffset(), + "/O (outline) offset is out of bounds"); + } readHGeneric(BitStream(h_buf + HOi, h_size - HOi), this->outline_hints); } @@ -382,10 +416,9 @@ QPDF::readHPageOffset(BitStream h) t.nbits_shared_numerator = h.getBits(16); // 12 t.shared_denominator = h.getBits(16); // 13 - unsigned int nitems = this->linp.npages; std::vector<HPageOffsetEntry>& entries = t.entries; - entries = std::vector<HPageOffsetEntry>(nitems); - + entries.clear(); + unsigned int nitems = this->linp.npages; load_vector_int(h, nitems, entries, t.nbits_delta_nobjects, &HPageOffsetEntry::delta_nobjects); @@ -427,10 +460,9 @@ QPDF::readHSharedObject(BitStream h) QTC::TC("qpdf", "QPDF lin nshared_total > nshared_first_page", (t.nshared_total > t.nshared_first_page) ? 1 : 0); - int nitems = t.nshared_total; std::vector<HSharedObjectEntry>& entries = t.entries; - entries = std::vector<HSharedObjectEntry>(nitems); - + entries.clear(); + int nitems = t.nshared_total; load_vector_int(h, nitems, entries, t.nbits_delta_group_length, &HSharedObjectEntry::delta_group_length); @@ -438,7 +470,7 @@ QPDF::readHSharedObject(BitStream h) 1, &HSharedObjectEntry::signature_present); for (int i = 0; i < nitems; ++i) { - if (entries[i].signature_present) + if (entries.at(i).signature_present) { // Skip 128-bit MD5 hash. These are not supported by // acrobat, so they should probably never be there. We @@ -480,7 +512,7 @@ QPDF::checkLinearizationInternal() // O: object number of first page std::vector<QPDFObjectHandle> const& pages = getAllPages(); - if (p.first_page_object != pages[0].getObjectID()) + if (p.first_page_object != pages.at(0).getObjectID()) { QTC::TC("qpdf", "QPDF err /O mismatch"); errors.push_back("first page object (/O) mismatch"); @@ -496,7 +528,7 @@ QPDF::checkLinearizationInternal() for (int i = 0; i < npages; ++i) { - QPDFObjectHandle const& page = pages[i]; + QPDFObjectHandle const& page = pages.at(i); QPDFObjGen og(page.getObjGen()); if (this->xref_table[og].getType() == 2) { @@ -574,15 +606,21 @@ QPDF::checkLinearizationInternal() // agree with pdlin. As of this writing, the test suite doesn't // contain any files with threads. - assert(! this->part6.empty()); + if (this->part6.empty()) + { + throw std::logic_error("linearization part 6 unexpectedly empty"); + } qpdf_offset_t min_E = -1; qpdf_offset_t max_E = -1; for (std::vector<QPDFObjectHandle>::iterator iter = this->part6.begin(); iter != this->part6.end(); ++iter) { QPDFObjGen og((*iter).getObjGen()); - // All objects have to have been dereferenced to be classified. - assert(this->obj_cache.count(og) > 0); + if (this->obj_cache.count(og) == 0) + { + // All objects have to have been dereferenced to be classified. + throw std::logic_error("linearization part6 object not in cache"); + } ObjCache const& oc = this->obj_cache[og]; min_E = std::max(min_E, oc.end_before_space); max_E = std::max(max_E, oc.end_after_space); @@ -738,7 +776,7 @@ QPDF::checkHPageOffset(std::list<std::string>& errors, unsigned int npages = pages.size(); int table_offset = adjusted_offset( this->page_offset_hints.first_page_offset); - QPDFObjGen first_page_og(pages[0].getObjGen()); + QPDFObjGen first_page_og(pages.at(0).getObjGen()); assert(this->xref_table.count(first_page_og) > 0); int offset = getLinearizationOffset(first_page_og); if (table_offset != offset) @@ -748,13 +786,13 @@ QPDF::checkHPageOffset(std::list<std::string>& errors, for (unsigned int pageno = 0; pageno < npages; ++pageno) { - QPDFObjGen page_og(pages[pageno].getObjGen()); + QPDFObjGen page_og(pages.at(pageno).getObjGen()); int first_object = page_og.getObj(); assert(this->xref_table.count(page_og) > 0); offset = getLinearizationOffset(page_og); - HPageOffsetEntry& he = this->page_offset_hints.entries[pageno]; - CHPageOffsetEntry& ce = this->c_page_offset_data.entries[pageno]; + HPageOffsetEntry& he = this->page_offset_hints.entries.at(pageno); + CHPageOffsetEntry& ce = this->c_page_offset_data.entries.at(pageno); int h_nobjects = he.delta_nobjects + this->page_offset_hints.min_nobjects; if (h_nobjects != ce.nobjects) @@ -799,16 +837,25 @@ QPDF::checkHPageOffset(std::list<std::string>& errors, for (int i = 0; i < he.nshared_objects; ++i) { - int idx = he.shared_identifiers[i]; - assert(shared_idx_to_obj.count(idx) > 0); + int idx = he.shared_identifiers.at(i); + if (shared_idx_to_obj.count(idx) == 0) + { + throw std::logic_error( + "unable to get object for item in" + " shared objects hint table"); + } hint_shared.insert(shared_idx_to_obj[idx]); } for (int i = 0; i < ce.nshared_objects; ++i) { - int idx = ce.shared_identifiers[i]; - assert(idx < this->c_shared_object_data.nshared_total); - int obj = this->c_shared_object_data.entries[idx].object; + int idx = ce.shared_identifiers.at(i); + if (idx >= this->c_shared_object_data.nshared_total) + { + throw std::logic_error( + "index out of bounds for shared object hint table"); + } + int obj = this->c_shared_object_data.entries.at(idx).object; computed_shared.insert(obj); } @@ -876,7 +923,7 @@ QPDF::checkHSharedObject(std::list<std::string>& errors, // The first nshared_first_page objects are consecutive // objects starting with the first page object. The rest are // consecutive starting from the first_shared_obj object. - int cur_object = pages[0].getObjectID(); + int cur_object = pages.at(0).getObjectID(); for (int i = 0; i < so.nshared_total; ++i) { if (i == so.nshared_first_page) @@ -890,7 +937,7 @@ QPDF::checkHSharedObject(std::list<std::string>& errors, } else { - int obj = this->part8[0].getObjectID(); + int obj = this->part8.at(0).getObjectID(); if (obj != so.first_shared_obj) { errors.push_back( @@ -918,7 +965,7 @@ QPDF::checkHSharedObject(std::list<std::string>& errors, } idx_to_obj[i] = cur_object; - HSharedObjectEntry& se = so.entries[i]; + HSharedObjectEntry& se = so.entries.at(i); int nobjects = se.nobjects_minus_one + 1; int length = lengthNextN(cur_object, nobjects, errors); int h_length = so.min_group_length + se.delta_group_length; @@ -1099,7 +1146,7 @@ QPDF::dumpHPageOffset() for (int i1 = 0; i1 < this->linp.npages; ++i1) { - HPageOffsetEntry& pe = t.entries[i1]; + HPageOffsetEntry& pe = t.entries.at(i1); *out_stream << "Page " << i1 << ":" << std::endl << " nobjects: " << pe.delta_nobjects + t.min_nobjects @@ -1115,9 +1162,9 @@ QPDF::dumpHPageOffset() for (int i2 = 0; i2 < pe.nshared_objects; ++i2) { *out_stream << " identifier " << i2 << ": " - << pe.shared_identifiers[i2] << std::endl; + << pe.shared_identifiers.at(i2) << std::endl; *out_stream << " numerator " << i2 << ": " - << pe.shared_numerators[i2] << std::endl; + << pe.shared_numerators.at(i2) << std::endl; } } } @@ -1144,7 +1191,7 @@ QPDF::dumpHSharedObject() for (int i = 0; i < t.nshared_total; ++i) { - HSharedObjectEntry& se = t.entries[i]; + HSharedObjectEntry& se = t.entries.at(i); *out_stream << "Shared Object " << i << ":" << std::endl; *out_stream << " group length: " << se.delta_group_length + t.min_group_length << std::endl; @@ -1452,8 +1499,11 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data) // validation code can compute them relatively easily given the // rest of the information. + // npages is the size of the existing pages vector, which has been + // created by traversing the pages tree, and as such is a + // reasonable size. this->c_linp.npages = npages; - this->c_page_offset_data.entries = std::vector<CHPageOffsetEntry>(npages); + this->c_page_offset_data.entries = std::vector<CHPageOffsetEntry>(npages); // Part 4: open document objects. We don't care about the order. @@ -1472,7 +1522,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data) // will do the same. // First, place the actual first page object itself. - QPDFObjGen first_page_og(pages[0].getObjGen()); + QPDFObjGen first_page_og(pages.at(0).getObjGen()); if (! lc_first_page_private.count(first_page_og)) { throw std::logic_error( @@ -1480,8 +1530,8 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data) "object not in lc_first_page_private"); } lc_first_page_private.erase(first_page_og); - this->c_linp.first_page_object = pages[0].getObjectID(); - this->part6.push_back(pages[0]); + this->c_linp.first_page_object = pages.at(0).getObjectID(); + this->part6.push_back(pages.at(0)); // The PDF spec "recommends" an order for the rest of the objects, // but we are going to disregard it except to the extent that it @@ -1512,7 +1562,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data) // in garbage values for all the shared object identifiers on the // first page. - this->c_page_offset_data.entries[0].nobjects = this->part6.size(); + this->c_page_offset_data.entries.at(0).nobjects = this->part6.size(); // Part 7: other pages' private objects @@ -1521,7 +1571,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data) { // Place this page's page object - QPDFObjGen page_og(pages[i].getObjGen()); + QPDFObjGen page_og(pages.at(i).getObjGen()); if (! lc_other_page_private.count(page_og)) { throw std::logic_error( @@ -1530,12 +1580,12 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data) QUtil::int_to_string(i) + " not in lc_other_page_private"); } lc_other_page_private.erase(page_og); - this->part7.push_back(pages[i]); + this->part7.push_back(pages.at(i)); // Place all non-shared objects referenced by this page, // updating the page object count for the hint table. - this->c_page_offset_data.entries[i].nobjects = 1; + this->c_page_offset_data.entries.at(i).nobjects = 1; ObjUser ou(ObjUser::ou_page, i); assert(this->obj_user_to_objects.count(ou) > 0); @@ -1548,7 +1598,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data) { lc_other_page_private.erase(og); this->part7.push_back(objGenToIndirect(og)); - ++this->c_page_offset_data.entries[i].nobjects; + ++this->c_page_offset_data.entries.at(i).nobjects; } } } @@ -1599,7 +1649,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data) // thumbnail hint tables. for (unsigned int i = 0; i < npages; ++i) { - QPDFObjectHandle thumb = pages[i].getKey("/Thumb"); + QPDFObjectHandle thumb = pages.at(i).getKey("/Thumb"); thumb = getUncompressedObject(thumb, object_stream_data); if (! thumb.isNull()) { @@ -1708,7 +1758,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data) if (! this->part8.empty()) { this->c_shared_object_data.first_shared_obj = - this->part8[0].getObjectID(); + this->part8.at(0).getObjectID(); for (std::vector<QPDFObjectHandle>::iterator iter = this->part8.begin(); iter != this->part8.end(); ++iter) @@ -1719,15 +1769,19 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data) shared.push_back(CHSharedObjectEntry(obj)); } } - assert(static_cast<size_t>(this->c_shared_object_data.nshared_total) == - this->c_shared_object_data.entries.size()); + if (static_cast<size_t>(this->c_shared_object_data.nshared_total) != + this->c_shared_object_data.entries.size()) + { + throw std::logic_error( + "shared object hint table has wrong number of entries"); + } // Now compute the list of shared objects for each page after the // first page. for (unsigned int i = 1; i < npages; ++i) { - CHPageOffsetEntry& pe = this->c_page_offset_data.entries[i]; + CHPageOffsetEntry& pe = this->c_page_offset_data.entries.at(i); ObjUser ou(ObjUser::ou_page, i); assert(this->obj_user_to_objects.count(ou) > 0); std::set<QPDFObjGen> const& ogs = this->obj_user_to_objects[ou]; @@ -1838,15 +1892,16 @@ QPDF::calculateHPageOffset( // Calculate minimum and maximum values for number of objects per // page and page length. - int min_nobjects = cphe[0].nobjects; + int min_nobjects = cphe.at(0).nobjects; int max_nobjects = min_nobjects; int min_length = outputLengthNextN( - pages[0].getObjectID(), min_nobjects, lengths, obj_renumber); + pages.at(0).getObjectID(), min_nobjects, lengths, obj_renumber); int max_length = min_length; - int max_shared = cphe[0].nshared_objects; + int max_shared = cphe.at(0).nshared_objects; HPageOffset& ph = this->page_offset_hints; std::vector<HPageOffsetEntry>& phe = ph.entries; + // npages is the size of the existing pages array. phe = std::vector<HPageOffsetEntry>(npages); for (unsigned int i = 0; i < npages; ++i) @@ -1857,10 +1912,10 @@ QPDF::calculateHPageOffset( // Repeat calculations for page 0 so we can assign to phe[i] // without duplicating those assignments. - int nobjects = cphe[i].nobjects; + int nobjects = cphe.at(i).nobjects; int length = outputLengthNextN( - pages[i].getObjectID(), nobjects, lengths, obj_renumber); - int nshared = cphe[i].nshared_objects; + pages.at(i).getObjectID(), nobjects, lengths, obj_renumber); + int nshared = cphe.at(i).nshared_objects; min_nobjects = std::min(min_nobjects, nobjects); max_nobjects = std::max(max_nobjects, nobjects); @@ -1868,13 +1923,13 @@ QPDF::calculateHPageOffset( max_length = std::max(max_length, length); max_shared = std::max(max_shared, nshared); - phe[i].delta_nobjects = nobjects; - phe[i].delta_page_length = length; - phe[i].nshared_objects = nshared; + phe.at(i).delta_nobjects = nobjects; + phe.at(i).delta_page_length = length; + phe.at(i).nshared_objects = nshared; } ph.min_nobjects = min_nobjects; - int in_page0_id = pages[0].getObjectID(); + int in_page0_id = pages.at(0).getObjectID(); int out_page0_id = (*(obj_renumber.find(in_page0_id))).second; ph.first_page_offset = (*(xref.find(out_page0_id))).second.getOffset(); ph.nbits_delta_nobjects = nbits(max_nobjects - min_nobjects); @@ -1896,17 +1951,17 @@ QPDF::calculateHPageOffset( for (unsigned int i = 0; i < npages; ++i) { // Adjust delta entries - assert(phe[i].delta_nobjects >= min_nobjects); - assert(phe[i].delta_page_length >= min_length); - phe[i].delta_nobjects -= min_nobjects; - phe[i].delta_page_length -= min_length; - phe[i].delta_content_length = phe[i].delta_page_length; + assert(phe.at(i).delta_nobjects >= min_nobjects); + assert(phe.at(i).delta_page_length >= min_length); + phe.at(i).delta_nobjects -= min_nobjects; + phe.at(i).delta_page_length -= min_length; + phe.at(i).delta_content_length = phe.at(i).delta_page_length; - for (int j = 0; j < cphe[i].nshared_objects; ++j) + for (int j = 0; j < cphe.at(i).nshared_objects; ++j) { - phe[i].shared_identifiers.push_back( - cphe[i].shared_identifiers[j]); - phe[i].shared_numerators.push_back(0); + phe.at(i).shared_identifiers.push_back( + cphe.at(i).shared_identifiers.at(j)); + phe.at(i).shared_numerators.push_back(0); } } } @@ -1921,20 +1976,25 @@ QPDF::calculateHSharedObject( std::vector<CHSharedObjectEntry>& csoe = cso.entries; HSharedObject& so = this->shared_object_hints; std::vector<HSharedObjectEntry>& soe = so.entries; - soe = std::vector<HSharedObjectEntry>(cso.nshared_total); + soe.clear(); int min_length = outputLengthNextN( - csoe[0].object, 1, lengths, obj_renumber); + csoe.at(0).object, 1, lengths, obj_renumber); int max_length = min_length; for (int i = 0; i < cso.nshared_total; ++i) { // Assign absolute numbers to deltas; adjust later int length = outputLengthNextN( - csoe[i].object, 1, lengths, obj_renumber); + csoe.at(i).object, 1, lengths, obj_renumber); min_length = std::min(min_length, length); max_length = std::max(max_length, length); - soe[i].delta_group_length = length; + soe.push_back(HSharedObjectEntry()); + soe.at(i).delta_group_length = length; + } + if (soe.size() != static_cast<size_t>(cso.nshared_total)) + { + throw std::logic_error("soe has wrong size after initialization"); } so.nshared_total = cso.nshared_total; @@ -1952,8 +2012,8 @@ QPDF::calculateHSharedObject( for (int i = 0; i < cso.nshared_total; ++i) { // Adjust deltas - assert(soe[i].delta_group_length >= min_length); - soe[i].delta_group_length -= min_length; + assert(soe.at(i).delta_group_length >= min_length); + soe.at(i).delta_group_length -= min_length; } } @@ -1991,7 +2051,7 @@ write_vector_int(BitWriter& w, int nitems, std::vector<T>& vec, for (int i = 0; i < nitems; ++i) { - w.writeBits(vec[i].*field, bits); + w.writeBits(vec.at(i).*field, bits); } // The PDF spec says that each hint table starts at a byte // boundary. Each "row" actually must start on a byte boundary. @@ -2008,9 +2068,9 @@ write_vector_vector(BitWriter& w, // from the vec2 vector field of the ith item of vec1. for (int i1 = 0; i1 < nitems1; ++i1) { - for (int i2 = 0; i2 < vec1[i1].*nitems2; ++i2) + for (int i2 = 0; i2 < vec1.at(i1).*nitems2; ++i2) { - w.writeBits((vec1[i1].*vec2)[i2], bits); + w.writeBits((vec1.at(i1).*vec2).at(i2), bits); } } w.flush(); @@ -2091,7 +2151,7 @@ QPDF::writeHSharedObject(BitWriter& w) for (int i = 0; i < nitems; ++i) { // If signature were present, we'd have to write a 128-bit hash. - assert(entries[i].signature_present == 0); + assert(entries.at(i).signature_present == 0); } write_vector_int(w, nitems, entries, t.nbits_nobjects, diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc index 35663c4..5299d90 100644 --- a/libqpdf/QPDF_optimization.cc +++ b/libqpdf/QPDF_optimization.cc @@ -91,7 +91,7 @@ QPDF::optimize(std::map<int, int> const& object_stream_data, for (int pageno = 0; pageno < n; ++pageno) { updateObjectMaps(ObjUser(ObjUser::ou_page, pageno), - this->all_pages[pageno]); + this->all_pages.at(pageno)); } // Traverse document-level items diff --git a/libqpdf/QPDF_pages.cc b/libqpdf/QPDF_pages.cc index 0604cd4..e8d107b 100644 --- a/libqpdf/QPDF_pages.cc +++ b/libqpdf/QPDF_pages.cc @@ -114,13 +114,16 @@ QPDF::flattenPagesTree() for (int pos = 0; pos < len; ++pos) { // populate pageobj_to_pages_pos and fix parent pointer - insertPageobjToPage(this->all_pages[pos], pos, true); - this->all_pages[pos].replaceKey("/Parent", pages); + insertPageobjToPage(this->all_pages.at(pos), pos, true); + this->all_pages.at(pos).replaceKey("/Parent", pages); } pages.replaceKey("/Kids", QPDFObjectHandle::newArray(this->all_pages)); // /Count has not changed - assert(pages.getKey("/Count").getIntValue() == len); + if (pages.getKey("/Count").getIntValue() != len) + { + throw std::logic_error("/Count is wrong after flattening pages tree"); + } } void @@ -191,7 +194,7 @@ QPDF::insertPage(QPDFObjectHandle newpage, int pos) assert(this->all_pages.size() == static_cast<size_t>(npages)); for (int i = pos + 1; i < npages; ++i) { - insertPageobjToPage(this->all_pages[i], i, false); + insertPageobjToPage(this->all_pages.at(i), i, false); } insertPageobjToPage(newpage, pos, true); assert(this->pageobj_to_pages_pos.size() == static_cast<size_t>(npages)); @@ -218,7 +221,7 @@ QPDF::removePage(QPDFObjectHandle page) assert(this->pageobj_to_pages_pos.size() == static_cast<size_t>(npages)); for (int i = pos; i < npages; ++i) { - insertPageobjToPage(this->all_pages[i], i, false); + insertPageobjToPage(this->all_pages.at(i), i, false); } } diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc index c158e80..cf45506 100644 --- a/libqpdf/QUtil.cc +++ b/libqpdf/QUtil.cc @@ -17,6 +17,7 @@ #include <Windows.h> #include <direct.h> #include <io.h> +#include <Wincrypt.h> #else #include <unistd.h> #endif @@ -200,7 +201,7 @@ QUtil::hex_encode(std::string const& input) for (unsigned int i = 0; i < input.length(); ++i) { result += QUtil::int_to_string_base( - static_cast<int>(static_cast<unsigned char>(input[i])), 16, 2); + static_cast<int>(static_cast<unsigned char>(input.at(i))), 16, 2); } return result; } @@ -360,11 +361,11 @@ QUtil::toUTF8(unsigned long uval) // Maximum that will fit in high byte now shrinks by one bit maxval >>= 1; // Slide to the left one byte - --cur_byte; - if (cur_byte < bytes) + if (cur_byte <= bytes) { throw std::logic_error("QUtil::toUTF8: overflow error"); } + --cur_byte; } // If maxval is k bits long, the high (7 - k) bits of the // resulting byte must be high. @@ -377,6 +378,8 @@ QUtil::toUTF8(unsigned long uval) return result; } +#ifdef USE_INSECURE_RANDOM + long QUtil::random() { @@ -390,28 +393,100 @@ QUtil::random() seeded_random = true; } -#ifdef HAVE_RANDOM +# ifdef HAVE_RANDOM return ::random(); -#else +# else return rand(); -#endif +# endif } void -QUtil::srandom(unsigned int seed) +QUtil::initializeWithRandomBytes(unsigned char* data, size_t len) { -#ifdef HAVE_RANDOM - ::srandom(seed); + for (size_t i = 0; i < len; ++i) + { + data[i] = static_cast<unsigned char>((QUtil::random() & 0xff0) >> 4); + } +} + #else - srand(seed); -#endif + +long +QUtil::random() +{ + long result = 0L; + initializeWithRandomBytes( + reinterpret_cast<unsigned char*>(&result), + sizeof(result)); + return result; } +#ifdef _WIN32 +class WindowsCryptProvider +{ + public: + WindowsCryptProvider() + { + if (! CryptAcquireContext(&crypt_prov, NULL, NULL, PROV_RSA_FULL, 0)) + { + throw std::runtime_error("unable to acquire crypt context"); + } + } + ~WindowsCryptProvider() + { + // Ignore error + CryptReleaseContext(crypt_prov, 0); + } + + HCRYPTPROV crypt_prov; +}; +#endif + void QUtil::initializeWithRandomBytes(unsigned char* data, size_t len) { - for (size_t i = 0; i < len; ++i) +#if defined(_WIN32) + + // Optimization: make the WindowsCryptProvider static as long as + // it can be done in a thread-safe fashion. + WindowsCryptProvider c; + if (! CryptGenRandom(c.crypt_prov, len, reinterpret_cast<BYTE*>(data))) { - data[i] = static_cast<unsigned char>((QUtil::random() & 0xff0) >> 4); + throw std::runtime_error("unable to generate secure random data"); } + +#elif defined(RANDOM_DEVICE) + + // Optimization: wrap the file open and close in a class so that + // the file is closed in a destructor, then make this static to + // keep the file handle open. Only do this if it can be done in a + // thread-safe fashion. + FILE* f = QUtil::safe_fopen(RANDOM_DEVICE, "rb"); + size_t fr = fread(data, 1, len, f); + fclose(f); + if (fr != len) + { + throw std::runtime_error( + "unable to read " + + QUtil::int_to_string(len) + + " bytes from " + std::string(RANDOM_DEVICE)); + } + +#else + +# error "Don't know how to generate secure random numbers on this platform. See random number generation in the top-level README" + +#endif +} + +#endif + +void +QUtil::srandom(unsigned int seed) +{ +#ifdef HAVE_RANDOM + ::srandom(seed); +#else + srand(seed); +#endif } diff --git a/libqpdf/build.mk b/libqpdf/build.mk index 9f3de30..2078123 100644 --- a/libqpdf/build.mk +++ b/libqpdf/build.mk @@ -94,4 +94,4 @@ $(COBJS_libqpdf): libqpdf/$(OUTPUT_DIR)/%.$(LOBJ): libqpdf/%.c # * Otherwise, increment REVISION $(TARGETS_libqpdf): $(OBJS_libqpdf) - $(call makelib,$(OBJS_libqpdf),$@,$(LDFLAGS),$(LIBS),13,0,0) + $(call makelib,$(OBJS_libqpdf),$@,$(LDFLAGS),$(LIBS),13,1,0) diff --git a/libqpdf/qpdf/qpdf-config.h.in b/libqpdf/qpdf/qpdf-config.h.in index 6449a3f..74ee315 100644 --- a/libqpdf/qpdf/qpdf-config.h.in +++ b/libqpdf/qpdf/qpdf-config.h.in @@ -18,6 +18,9 @@ /* Define to 1 if you have the `random' function. */ #undef HAVE_RANDOM +/* Define to 1 (and set RANDOM_DEVICE) if a random device is available */ +#undef HAVE_RANDOM_DEVICE + /* Define to 1 if you have the <stdint.h> header file. */ #undef HAVE_STDINT_H @@ -61,9 +64,15 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* Define to the filename of the random device (and set HAVE_RANDOM_DEVICE) */ +#undef RANDOM_DEVICE + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS +/* Whether to use inscure random numbers */ +#undef USE_INSECURE_RANDOM + /* Enable large inode numbers on Mac OS X 10.5. */ #ifndef _DARWIN_USE_64_BIT_INODE # define _DARWIN_USE_64_BIT_INODE 1 diff --git a/m4/ax_random_device.m4 b/m4/ax_random_device.m4 new file mode 100644 index 0000000..aa8bf4c --- /dev/null +++ b/m4/ax_random_device.m4 @@ -0,0 +1,31 @@ +dnl @synopsis AX_RANDOM_DEVICE +dnl +dnl This macro will check for a random device, allowing the user to explicitly +dnl set the path. The user uses '--with-random=FILE' as an argument to +dnl configure. +dnl +dnl If A random device is found then HAVE_RANDOM_DEVICE is set to 1 and +dnl RANDOM_DEVICE contains the path. +dnl +dnl @category Miscellaneous +dnl @author Martin Ebourne +dnl @version 2005/07/01 +dnl @license AllPermissive + +AC_DEFUN([AX_RANDOM_DEVICE], [ + AC_ARG_WITH([random], + [AC_HELP_STRING([--with-random=FILE], [Use FILE as random number seed [auto-detected]])], + [RANDOM_DEVICE="$withval"], + [AC_CHECK_FILE("/dev/urandom", [RANDOM_DEVICE="/dev/urandom";], + [AC_CHECK_FILE("/dev/arandom", [RANDOM_DEVICE="/dev/arandom";], + [AC_CHECK_FILE("/dev/random", [RANDOM_DEVICE="/dev/random";])] + )]) + ]) + if test "x$RANDOM_DEVICE" != "x" ; then + AC_DEFINE([HAVE_RANDOM_DEVICE], 1, + [Define to 1 (and set RANDOM_DEVICE) if a random device is available]) + AC_SUBST([RANDOM_DEVICE]) + AC_DEFINE_UNQUOTED([RANDOM_DEVICE], ["$RANDOM_DEVICE"], + [Define to the filename of the random device (and set HAVE_RANDOM_DEVICE)]) + fi +])dnl diff --git a/manual/qpdf-manual.xml b/manual/qpdf-manual.xml index 631f759..8d68db9 100644 --- a/manual/qpdf-manual.xml +++ b/manual/qpdf-manual.xml @@ -5,8 +5,8 @@ <!ENTITY mdash "—"> <!ENTITY ndash "–"> <!ENTITY nbsp " "> -<!ENTITY swversion "5.0.0"> -<!ENTITY lastreleased "July 10, 2013"> +<!ENTITY swversion "5.0.1"> +<!ENTITY lastreleased "October 18, 2013"> ]> <book> <bookinfo> @@ -2641,6 +2641,45 @@ print "\n"; </para> <variablelist> <varlistentry> + <term>5.0.1: October 18, 2013</term> + <listitem> + <itemizedlist> + <listitem> + <para> + Thanks to a detailed review by Florian Weimer and the Red Hat + Product Security Team, this release includes a number of + non-user-visible security hardening changes. Please see the + ChangeLog file in the source distribution for the complete + list. + </para> + </listitem> + <listitem> + <para> + When available, operating system-specific secure random number + generation is used for generating initialization vectors and + other random values used during encryption or file creation. + For the Windows build, this results in an added dependency on + Microsoft's cryptography API. To disable the OS-specific + cryptography and use the old version, pass the + <option>--enable-insecure-random</option> option to + <command>./configure</command>. + </para> + </listitem> + <listitem> + <para> + The <command>qpdf</command> command-line tool now issues a + warning when <option>-accessibility=n</option> is specified + for newer encryption versions stating that the option is + ignored. qpdf, per the spec, has always ignored this flag, + but it previously did so silently. This warning is issued + only by the command-line tool, not by the library. The + library's handling of this flag is unchanged. + </para> + </listitem> + </itemizedlist> + </listitem> + </varlistentry> + <varlistentry> <term>5.0.0: July 10, 2013</term> <listitem> <itemizedlist> diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc index 876b150..be8198e 100644 --- a/qpdf/qpdf.cc +++ b/qpdf/qpdf.cc @@ -441,7 +441,7 @@ static std::vector<int> parse_numrange(char const* range, int max, p = 0; for (size_t i = 0; i < work.size(); i += 2) { - int num = work[i]; + int num = work.at(i); // max == 0 means we don't know the max and are just // testing for valid syntax. if ((max > 0) && ((num < 1) || (num > max))) @@ -451,11 +451,11 @@ static std::vector<int> parse_numrange(char const* range, int max, } if (i == 0) { - result.push_back(work[i]); + result.push_back(work.at(i)); } else { - int separator = work[i-1]; + int separator = work.at(i-1); if (separator == comma) { result.push_back(num); @@ -740,13 +740,13 @@ parse_encrypt_options( { usage("invalid -accessibility parameter"); } - if (keylen == 128) + if (keylen == 40) { - r3_accessibility = result; + usage("-accessibility invalid for 40-bit keys"); } else { - usage("-accessibility invalid for 40-bit keys"); + r3_accessibility = result; } } else if (strcmp(arg, "cleartext-metadata") == 0) @@ -1664,7 +1664,7 @@ int main(int argc, char* argv[]) // Pages are specified from 1 but numbered // from 0 in the vector int pageno = *pageno_iter - 1; - pdf.addPage(page_data.orig_pages[pageno], false); + pdf.addPage(page_data.orig_pages.at(pageno), false); if (page_data.qpdf == &pdf) { // This is a page from the original file. @@ -1683,7 +1683,7 @@ int main(int argc, char* argv[]) { if (selected_from_orig.count(pageno) == 0) { - pdf.replaceObject(orig_pages[pageno].getObjGen(), + pdf.replaceObject(orig_pages.at(pageno).getObjGen(), QPDFObjectHandle::newNull()); } } @@ -1730,49 +1730,77 @@ int main(int argc, char* argv[]) } if (encrypt) { + int R = 0; if (keylen == 40) { - w.setR2EncryptionParameters( - user_password.c_str(), owner_password.c_str(), - r2_print, r2_modify, r2_extract, r2_annotate); + R = 2; } else if (keylen == 128) { if (force_V4 || cleartext_metadata || use_aes) { - w.setR4EncryptionParameters( - user_password.c_str(), owner_password.c_str(), - r3_accessibility, r3_extract, r3_print, r3_modify, - !cleartext_metadata, use_aes); + R = 4; } else { - w.setR3EncryptionParameters( - user_password.c_str(), owner_password.c_str(), - r3_accessibility, r3_extract, r3_print, r3_modify); + R = 3; } } else if (keylen == 256) { if (force_R5) { - w.setR5EncryptionParameters( - user_password.c_str(), owner_password.c_str(), - r3_accessibility, r3_extract, r3_print, r3_modify, - !cleartext_metadata); + R = 5; } else { - w.setR6EncryptionParameters( - user_password.c_str(), owner_password.c_str(), - r3_accessibility, r3_extract, r3_print, r3_modify, - !cleartext_metadata); + R = 6; } } else { throw std::logic_error("bad encryption keylen"); } + if ((R > 3) && (r3_accessibility == false)) + { + std::cerr << whoami + << ": -accessibility=n is ignored for modern" + << " encryption formats" << std::endl; + } + switch (R) + { + case 2: + w.setR2EncryptionParameters( + user_password.c_str(), owner_password.c_str(), + r2_print, r2_modify, r2_extract, r2_annotate); + break; + case 3: + w.setR3EncryptionParameters( + user_password.c_str(), owner_password.c_str(), + r3_accessibility, r3_extract, r3_print, r3_modify); + break; + case 4: + w.setR4EncryptionParameters( + user_password.c_str(), owner_password.c_str(), + r3_accessibility, r3_extract, r3_print, r3_modify, + !cleartext_metadata, use_aes); + break; + case 5: + w.setR5EncryptionParameters( + user_password.c_str(), owner_password.c_str(), + r3_accessibility, r3_extract, r3_print, r3_modify, + !cleartext_metadata); + break; + case 6: + w.setR6EncryptionParameters( + user_password.c_str(), owner_password.c_str(), + r3_accessibility, r3_extract, r3_print, r3_modify, + !cleartext_metadata); + break; + default: + throw std::logic_error("bad encryption R value"); + break; + } } if (linearize) { diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index a30dd7b..1aff3d2 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -199,7 +199,7 @@ $td->runtest("remove page we don't have", show_ntests(); # ---------- $td->notify("--- Miscellaneous Tests ---"); -$n_tests += 67; +$n_tests += 70; $td->runtest("qpdf version", {$td->COMMAND => "qpdf --version"}, @@ -527,6 +527,23 @@ $td->runtest("ignore broken decode parms with no filters", {$td->FILE => "broken-decode-parms-no-filter.out", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); +$td->runtest("bounds check linearization data 1", + {$td->COMMAND => "qpdf --check linearization-bounds-1.pdf"}, + {$td->FILE => "linearization-bounds-1.out", + $td->EXIT_STATUS => 2}, + $td->NORMALIZE_NEWLINES); +$td->runtest("bounds check linearization data 2", + {$td->COMMAND => "qpdf --check linearization-bounds-2.pdf"}, + {$td->FILE => "linearization-bounds-2.out", + $td->EXIT_STATUS => 2}, + $td->NORMALIZE_NEWLINES); +# Throws logic error, not bad_alloc +$td->runtest("sanity check array size", + {$td->COMMAND => + "qpdf --check linearization-large-vector-alloc.pdf"}, + {$td->FILE => "linearization-large-vector-alloc.out", + $td->EXIT_STATUS => 2}, + $td->NORMALIZE_NEWLINES); show_ntests(); # ---------- @@ -1382,6 +1399,9 @@ my @encrypted_files = ['XI-R6,V5,U=view,O=master', 'master', '-print=low', -2052, 1, 1, 1, 0, 1, 1, 1, 1, 1], + ['XI-R6,V5,U=view,O=master', 'master', + '-accessibility=n', -4, # -accessibility=n has no effect + 1, 1, 1, 1, 1, 1, 1, 1, 1], ['XI-long-password', 'qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnm'], ['XI-long-password', 'qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcv'], ); @@ -1484,12 +1504,20 @@ foreach my $d (@encrypted_files) # password. $upass = ""; } + my $accessibility_warning = ""; + if (($R > 3) && ($eflags =~ /accessibility=n/)) + { + $accessibility_warning = + "qpdf: -accessibility=n is ignored" . + " for modern encryption formats\n"; + } $td->runtest("encrypt $file", {$td->COMMAND => "qpdf --static-id --no-original-object-ids -qdf" . " $eflags $file.enc $file.enc2"}, - {$td->STRING => "", - $td->EXIT_STATUS => 0}); + {$td->STRING => $accessibility_warning, + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); $td->runtest("check /P", {$td->COMMAND => "qpdf --show-encryption --password=\"$pass\"" . diff --git a/qpdf/qtest/qpdf/linearization-bounds-1.out b/qpdf/qtest/qpdf/linearization-bounds-1.out new file mode 100644 index 0000000..eaeef14 --- /dev/null +++ b/qpdf/qtest/qpdf/linearization-bounds-1.out @@ -0,0 +1,6 @@ +checking linearization-bounds-1.pdf +PDF Version: 1.3 +File is not encrypted +File is linearized +WARNING: linearization-bounds-1.pdf (linearization hint stream: object 62 0, file position 1183): attempting to recover stream length +linearization-bounds-1.pdf (linearization hint table, file position 1183): /S (shared object) offset is out of bounds diff --git a/qpdf/qtest/qpdf/linearization-bounds-1.pdf b/qpdf/qtest/qpdf/linearization-bounds-1.pdf Binary files differnew file mode 100644 index 0000000..44befc2 --- /dev/null +++ b/qpdf/qtest/qpdf/linearization-bounds-1.pdf diff --git a/qpdf/qtest/qpdf/linearization-bounds-2.out b/qpdf/qtest/qpdf/linearization-bounds-2.out new file mode 100644 index 0000000..bdf7c91 --- /dev/null +++ b/qpdf/qtest/qpdf/linearization-bounds-2.out @@ -0,0 +1,6 @@ +checking linearization-bounds-2.pdf +PDF Version: 1.3 +File is not encrypted +File is linearized +WARNING: linearization-bounds-2.pdf (linearization hint stream: object 62 0, file position 1183): attempting to recover stream length +linearization-bounds-2.pdf (linearization hint table, file position 1183): /S (shared object) offset is out of bounds diff --git a/qpdf/qtest/qpdf/linearization-bounds-2.pdf b/qpdf/qtest/qpdf/linearization-bounds-2.pdf Binary files differnew file mode 100644 index 0000000..bdd6177 --- /dev/null +++ b/qpdf/qtest/qpdf/linearization-bounds-2.pdf diff --git a/qpdf/qtest/qpdf/linearization-large-vector-alloc.out b/qpdf/qtest/qpdf/linearization-large-vector-alloc.out new file mode 100644 index 0000000..2c807d3 --- /dev/null +++ b/qpdf/qtest/qpdf/linearization-large-vector-alloc.out @@ -0,0 +1,6 @@ +checking linearization-large-vector-alloc.pdf +PDF Version: 1.3 +File is not encrypted +File is linearized +WARNING: linearization-large-vector-alloc.pdf (linearization hint stream: object 62 0, file position 1183): attempting to recover stream length +overflow reading bit stream diff --git a/qpdf/qtest/qpdf/linearization-large-vector-alloc.pdf b/qpdf/qtest/qpdf/linearization-large-vector-alloc.pdf Binary files differnew file mode 100644 index 0000000..1807b08 --- /dev/null +++ b/qpdf/qtest/qpdf/linearization-large-vector-alloc.pdf diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index 31fc052..49dfbc6 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -603,10 +603,10 @@ void runtest(int n, char const* filename1, char const* arg2) else if (n == 10) { std::vector<QPDFObjectHandle> pages = pdf.getAllPages(); - pages[0].addPageContents( + pages.at(0).addPageContents( QPDFObjectHandle::newStream( &pdf, "BT /F1 12 Tf 72 620 Td (Baked) Tj ET\n"), true); - pages[0].addPageContents( + pages.at(0).addPageContents( QPDFObjectHandle::newStream( &pdf, "BT /F1 18 Tf 72 520 Td (Mashed) Tj ET\n"), false); @@ -659,7 +659,7 @@ void runtest(int n, char const* filename1, char const* arg2) " not called 4-page file"); } // Swap pages 2 and 3 - pdf.swapObjects(pages[1].getObjGen(), pages[2].getObjGen()); + pdf.swapObjects(pages.at(1).getObjGen(), pages.at(2).getObjGen()); // Replace object and swap objects QPDFObjectHandle trailer = pdf.getTrailer(); QPDFObjectHandle qdict = trailer.getKey("/QDict"); @@ -700,7 +700,7 @@ void runtest(int n, char const* filename1, char const* arg2) std::map<std::string, QPDFObjectHandle> dict_items = qarray.getDictAsMap(); if ((array_elements.size() == 1) && - (array_elements[0].getName() == "/Array") && + (array_elements.at(0).getName() == "/Array") && (dict_items.size() == 1) && (dict_items["/NewDict"].getIntValue() == 2)) { @@ -738,12 +738,12 @@ void runtest(int n, char const* filename1, char const* arg2) assert(pages.size() == 9); pdf.removePage(*pages.begin()); // original page 0 assert(pages.size() == 8); - checkPageContents(pages[4], "Original page 5"); - pdf.removePage(pages[4]); // original page 5 + checkPageContents(pages.at(4), "Original page 5"); + pdf.removePage(pages.at(4)); // original page 5 assert(pages.size() == 7); - checkPageContents(pages[4], "Original page 6"); - checkPageContents(pages[0], "Original page 1"); - checkPageContents(pages[6], "Original page 8"); + checkPageContents(pages.at(4), "Original page 6"); + checkPageContents(pages.at(0), "Original page 1"); + checkPageContents(pages.at(6), "Original page 8"); // Insert pages @@ -760,7 +760,7 @@ void runtest(int n, char const* filename1, char const* arg2) // dictionary and modify it. Using the results of // getDictAsMap to create a new dictionary effectively creates // a shallow copy. - QPDFObjectHandle page_template = pages[0]; + QPDFObjectHandle page_template = pages.at(0); std::vector<QPDFObjectHandle> new_pages; for (std::vector<QPDFObjectHandle>::iterator iter = contents.begin(); iter != contents.end(); ++iter) @@ -781,25 +781,25 @@ void runtest(int n, char const* filename1, char const* arg2) } // Now insert the pages - pdf.addPage(new_pages[0], true); - checkPageContents(pages[0], "New page 1"); - pdf.addPageAt(new_pages[1], true, pages[0]); - assert(pages[0].getObjGen() == new_pages[1].getObjGen()); - pdf.addPageAt(new_pages[2], true, pages[5]); - assert(pages[5].getObjGen() == new_pages[2].getObjGen()); - pdf.addPageAt(new_pages[3], false, pages[5]); - assert(pages[6].getObjGen() == new_pages[3].getObjGen()); + pdf.addPage(new_pages.at(0), true); + checkPageContents(pages.at(0), "New page 1"); + pdf.addPageAt(new_pages.at(1), true, pages.at(0)); + assert(pages.at(0).getObjGen() == new_pages.at(1).getObjGen()); + pdf.addPageAt(new_pages.at(2), true, pages.at(5)); + assert(pages.at(5).getObjGen() == new_pages.at(2).getObjGen()); + pdf.addPageAt(new_pages.at(3), false, pages.at(5)); + assert(pages.at(6).getObjGen() == new_pages.at(3).getObjGen()); assert(pages.size() == 11); - pdf.addPage(new_pages[4], false); - assert(pages[11].getObjGen() == new_pages[4].getObjGen()); - pdf.addPageAt(new_pages[5], false, pages.back()); + pdf.addPage(new_pages.at(4), false); + assert(pages.at(11).getObjGen() == new_pages.at(4).getObjGen()); + pdf.addPageAt(new_pages.at(5), false, pages.back()); assert(pages.size() == 13); - checkPageContents(pages[0], "New page 0"); - checkPageContents(pages[1], "New page 1"); - checkPageContents(pages[5], "New page 5"); - checkPageContents(pages[6], "New page 6"); - checkPageContents(pages[11], "New page 11"); - checkPageContents(pages[12], "New page 12"); + checkPageContents(pages.at(0), "New page 0"); + checkPageContents(pages.at(1), "New page 1"); + checkPageContents(pages.at(5), "New page 5"); + checkPageContents(pages.at(6), "New page 6"); + checkPageContents(pages.at(11), "New page 11"); + checkPageContents(pages.at(12), "New page 12"); // Exercise writing to FILE* FILE* out = QUtil::safe_fopen("a.pdf", "wb"); @@ -816,7 +816,7 @@ void runtest(int n, char const* filename1, char const* arg2) QPDFObjectHandle contents = createPageContents(pdf, "New page 10"); QPDFObjectHandle page = pdf.makeIndirectObject( - QPDFObjectHandle(all_pages[0]).shallowCopy()); + QPDFObjectHandle(all_pages.at(0)).shallowCopy()); page.replaceKey("/Contents", contents); // Insert the page manually. @@ -843,7 +843,7 @@ void runtest(int n, char const* filename1, char const* arg2) // The input file to this test case is broken to exercise an // error condition. std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); - pdf.removePage(pages[0]); + pdf.removePage(pages.at(0)); std::cout << "you can't see this" << std::endl; } else if (n == 18) @@ -854,7 +854,7 @@ void runtest(int n, char const* filename1, char const* arg2) // Remove pages from various places, checking to make sure // that our pages reference is getting updated. assert(pages.size() == 10); - QPDFObjectHandle page5 = pages[5]; + QPDFObjectHandle page5 = pages.at(5); pdf.removePage(page5); pdf.addPage(page5, false); assert(pages.size() == 10); @@ -871,7 +871,7 @@ void runtest(int n, char const* filename1, char const* arg2) std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); // Try to insert a page that's already there. - pdf.addPage(pages[5], false); + pdf.addPage(pages.at(5), false); std::cout << "you can't see this" << std::endl; } else if (n == 20) @@ -893,7 +893,7 @@ void runtest(int n, char const* filename1, char const* arg2) { // Try to shallow copy a stream std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); - QPDFObjectHandle page = pages[0]; + QPDFObjectHandle page = pages.at(0); QPDFObjectHandle contents = page.getKey("/Contents"); contents.shallowCopy(); std::cout << "you can't see this" << std::endl; @@ -902,7 +902,7 @@ void runtest(int n, char const* filename1, char const* arg2) { // Try to remove a page we don't have std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); - QPDFObjectHandle page = pages[0]; + QPDFObjectHandle page = pages.at(0); pdf.removePage(page); pdf.removePage(page); std::cout << "you can't see this" << std::endl; @@ -1109,9 +1109,9 @@ void runtest(int n, char const* filename1, char const* arg2) QPDF final; final.processFile("b.pdf", "user"); std::vector<QPDFObjectHandle> pages = pdf.getAllPages(); - std::string orig_contents = getPageContents(pages[0]); + std::string orig_contents = getPageContents(pages.at(0)); pages = final.getAllPages(); - std::string new_contents = getPageContents(pages[0]); + std::string new_contents = getPageContents(pages.at(0)); if (orig_contents != new_contents) { std::cout << "oops -- page contents don't match" << std::endl @@ -1226,7 +1226,7 @@ void runtest(int n, char const* filename1, char const* arg2) bool is_binary = false; for (size_t i = 0; i < data.size(); ++i) { - if ((data[i] < 0) || (data[i] > 126)) + if ((data.at(i) < 0) || (data.at(i) > 126)) { is_binary = true; break; @@ -1239,9 +1239,9 @@ void runtest(int n, char const* filename1, char const* arg2) i < std::min(data.size(), static_cast<size_t>(20)); ++i) { - if ((data[i] >= 32) && (data[i] <= 126)) + if ((data.at(i) >= 32) && (data.at(i) <= 126)) { - t += data[i]; + t += data.at(i); } else { diff --git a/qpdf/test_large_file.cc b/qpdf/test_large_file.cc index 7ba5f6c..a7ed717 100644 --- a/qpdf/test_large_file.cc +++ b/qpdf/test_large_file.cc @@ -286,8 +286,8 @@ static void check_pdf(char const* filename) { int pageno = i + 1; std::cout << "page " << pageno << " of " << npages << std::endl; - check_page_contents(pageno, pages[i]); - check_image(pageno, pages[i]); + check_page_contents(pageno, pages.at(i)); + check_image(pageno, pages.at(i)); } } |