mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 02:49:00 +00:00
update release date to actual date
git-svn-id: svn+q:///qpdf/trunk@599 71b93d88-0707-0410-a8cf-f5a4172ac649
This commit is contained in:
commit
9a0b88bf77
191
Artistic-2.0
Normal file
191
Artistic-2.0
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
Artistic License 2.0
|
||||||
|
|
||||||
|
Copyright (c) 2000-2006, The Perl Foundation.
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
This license establishes the terms under which a given free software
|
||||||
|
Package may be copied, modified, distributed, and/or
|
||||||
|
redistributed. The intent is that the Copyright Holder maintains some
|
||||||
|
artistic control over the development of that Package while still
|
||||||
|
keeping the Package available as open source and free software.
|
||||||
|
|
||||||
|
You are always permitted to make arrangements wholly outside of this
|
||||||
|
license directly with the Copyright Holder of a given Package. If the
|
||||||
|
terms of this license do not permit the full use that you propose to
|
||||||
|
make of the Package, you should contact the Copyright Holder and seek
|
||||||
|
a different licensing arrangement.
|
||||||
|
|
||||||
|
Definitions
|
||||||
|
|
||||||
|
"Copyright Holder" means the individual(s) or organization(s) named
|
||||||
|
in the copyright notice for the entire Package.
|
||||||
|
|
||||||
|
"Contributor" means any party that has contributed code or other
|
||||||
|
material to the Package, in accordance with the Copyright Holder's
|
||||||
|
procedures.
|
||||||
|
|
||||||
|
"You" and "your" means any person who would like to copy,
|
||||||
|
distribute, or modify the Package.
|
||||||
|
|
||||||
|
"Package" means the collection of files distributed by the
|
||||||
|
Copyright Holder, and derivatives of that collection and/or of
|
||||||
|
those files. A given Package may consist of either the Standard
|
||||||
|
Version, or a Modified Version.
|
||||||
|
|
||||||
|
"Distribute" means providing a copy of the Package or making it
|
||||||
|
accessible to anyone else, or in the case of a company or
|
||||||
|
organization, to others outside of your company or organization.
|
||||||
|
|
||||||
|
"Distributor Fee" means any fee that you charge for Distributing
|
||||||
|
this Package or providing support for this Package to another
|
||||||
|
party. It does not mean licensing fees.
|
||||||
|
|
||||||
|
"Standard Version" refers to the Package if it has not been
|
||||||
|
modified, or has been modified only in ways explicitly requested by
|
||||||
|
the Copyright Holder.
|
||||||
|
|
||||||
|
"Modified Version" means the Package, if it has been changed, and
|
||||||
|
such changes were not explicitly requested by the Copyright Holder.
|
||||||
|
|
||||||
|
"Original License" means this Artistic License as Distributed with
|
||||||
|
the Standard Version of the Package, in its current version or as
|
||||||
|
it may be modified by The Perl Foundation in the future.
|
||||||
|
|
||||||
|
"Source" form means the source code, documentation source, and
|
||||||
|
configuration files for the Package.
|
||||||
|
|
||||||
|
"Compiled" form means the compiled bytecode, object code, binary,
|
||||||
|
or any other form resulting from mechanical transformation or
|
||||||
|
translation of the Source form.
|
||||||
|
|
||||||
|
Permission for Use and Modification Without Distribution
|
||||||
|
|
||||||
|
(1) You are permitted to use the Standard Version and create and use
|
||||||
|
Modified Versions for any purpose without restriction, provided that
|
||||||
|
you do not Distribute the Modified Version.
|
||||||
|
|
||||||
|
Permissions for Redistribution of the Standard Version
|
||||||
|
|
||||||
|
(2) You may Distribute verbatim copies of the Source form of the
|
||||||
|
Standard Version of this Package in any medium without restriction,
|
||||||
|
either gratis or for a Distributor Fee, provided that you duplicate
|
||||||
|
all of the original copyright notices and associated disclaimers. At
|
||||||
|
your discretion, such verbatim copies may or may not include a
|
||||||
|
Compiled form of the Package.
|
||||||
|
|
||||||
|
(3) You may apply any bug fixes, portability changes, and other
|
||||||
|
modifications made available from the Copyright Holder. The resulting
|
||||||
|
Package will still be considered the Standard Version, and as such
|
||||||
|
will be subject to the Original License.
|
||||||
|
|
||||||
|
Distribution of Modified Versions of the Package as Source
|
||||||
|
|
||||||
|
(4) You may Distribute your Modified Version as Source (either gratis
|
||||||
|
or for a Distributor Fee, and with or without a Compiled form of the
|
||||||
|
Modified Version) provided that you clearly document how it differs
|
||||||
|
from the Standard Version, including, but not limited to, documenting
|
||||||
|
any non-standard features, executables, or modules, and provided that
|
||||||
|
you do at least ONE of the following:
|
||||||
|
|
||||||
|
(a) make the Modified Version available to the Copyright Holder of
|
||||||
|
the Standard Version, under the Original License, so that the
|
||||||
|
Copyright Holder may include your modifications in the Standard
|
||||||
|
Version.
|
||||||
|
|
||||||
|
(b) ensure that installation of your Modified Version does not
|
||||||
|
prevent the user installing or running the Standard Version. In
|
||||||
|
addition, the Modified Version must bear a name that is different
|
||||||
|
from the name of the Standard Version.
|
||||||
|
|
||||||
|
(c) allow anyone who receives a copy of the Modified Version to
|
||||||
|
make the Source form of the Modified Version available to others
|
||||||
|
under
|
||||||
|
|
||||||
|
(i) the Original License or
|
||||||
|
|
||||||
|
(ii) a license that permits the licensee to freely copy, modify
|
||||||
|
and redistribute the Modified Version using the same licensing
|
||||||
|
terms that apply to the copy that the licensee received, and
|
||||||
|
requires that the Source form of the Modified Version, and of
|
||||||
|
any works derived from it, be made freely available in that
|
||||||
|
license fees are prohibited but Distributor Fees are allowed.
|
||||||
|
Distribution of Compiled Forms of the Standard Version or
|
||||||
|
Modified Versions without the Source
|
||||||
|
|
||||||
|
(5) You may Distribute Compiled forms of the Standard Version without
|
||||||
|
the Source, provided that you include complete instructions on how to
|
||||||
|
get the Source of the Standard Version. Such instructions must be
|
||||||
|
valid at the time of your distribution. If these instructions, at any
|
||||||
|
time while you are carrying out such distribution, become invalid, you
|
||||||
|
must provide new instructions on demand or cease further
|
||||||
|
distribution. If you provide valid instructions or cease distribution
|
||||||
|
within thirty days after you become aware that the instructions are
|
||||||
|
invalid, then you do not forfeit any of your rights under this
|
||||||
|
license.
|
||||||
|
|
||||||
|
(6) You may Distribute a Modified Version in Compiled form without the
|
||||||
|
Source, provided that you comply with Section 4 with respect to the
|
||||||
|
Source of the Modified Version.
|
||||||
|
|
||||||
|
Aggregating or Linking the Package
|
||||||
|
|
||||||
|
(7) You may aggregate the Package (either the Standard Version or
|
||||||
|
Modified Version) with other packages and Distribute the resulting
|
||||||
|
aggregation provided that you do not charge a licensing fee for the
|
||||||
|
Package. Distributor Fees are permitted, and licensing fees for other
|
||||||
|
components in the aggregation are permitted. The terms of this license
|
||||||
|
apply to the use and Distribution of the Standard or Modified Versions
|
||||||
|
as included in the aggregation.
|
||||||
|
|
||||||
|
(8) You are permitted to link Modified and Standard Versions with
|
||||||
|
other works, to embed the Package in a larger work of your own, or to
|
||||||
|
build stand-alone binary or bytecode versions of applications that
|
||||||
|
include the Package, and Distribute the result without restriction,
|
||||||
|
provided the result does not expose a direct interface to the Package.
|
||||||
|
|
||||||
|
Items That are Not Considered Part of a Modified Version
|
||||||
|
|
||||||
|
(9) Works (including, but not limited to, modules and scripts) that
|
||||||
|
merely extend or make use of the Package, do not, by themselves, cause
|
||||||
|
the Package to be a Modified Version. In addition, such works are not
|
||||||
|
considered parts of the Package itself, and are not subject to the
|
||||||
|
terms of this license.
|
||||||
|
|
||||||
|
General Provisions
|
||||||
|
|
||||||
|
(10) Any use, modification, and distribution of the Standard or
|
||||||
|
Modified Versions is governed by this Artistic License. By using,
|
||||||
|
modifying or distributing the Package, you accept this license. Do not
|
||||||
|
use, modify, or distribute the Package, if you do not accept this
|
||||||
|
license.
|
||||||
|
|
||||||
|
(11) If your Modified Version has been derived from a Modified Version
|
||||||
|
made by someone other than you, you are nevertheless required to
|
||||||
|
ensure that your Modified Version complies with the requirements of
|
||||||
|
this license.
|
||||||
|
|
||||||
|
(12) This license does not grant you the right to use any trademark,
|
||||||
|
service mark, tradename, or logo of the Copyright Holder.
|
||||||
|
|
||||||
|
(13) This license includes the non-exclusive, worldwide,
|
||||||
|
free-of-charge patent license to make, have made, use, offer to sell,
|
||||||
|
sell, import and otherwise transfer the Package with respect to any
|
||||||
|
patent claims licensable by the Copyright Holder that are necessarily
|
||||||
|
infringed by the Package. If you institute patent litigation
|
||||||
|
(including a cross-claim or counterclaim) against any party alleging
|
||||||
|
that the Package constitutes direct or contributory patent
|
||||||
|
infringement, then this Artistic License to you shall terminate on the
|
||||||
|
date that such litigation is filed.
|
||||||
|
|
||||||
|
(14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT
|
||||||
|
HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT
|
||||||
|
PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT
|
||||||
|
HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
3
ChangeLog
Normal file
3
ChangeLog
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
2008-04-26 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* 2.0: initial public release
|
216
INSTALL
Normal file
216
INSTALL
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
These instructions based on the generic INSTALL file from automake
|
||||||
|
1.10. However, qpdf does not use automake, so not all of that file
|
||||||
|
applies.
|
||||||
|
|
||||||
|
Basic Installation
|
||||||
|
==================
|
||||||
|
|
||||||
|
Briefly, the shell commands `./configure; make; make install' should
|
||||||
|
configure, build, and install this package. The following
|
||||||
|
more-detailed instructions are generic; see the `README' file for
|
||||||
|
instructions specific to this package.
|
||||||
|
|
||||||
|
The `configure' shell script attempts to guess correct values for
|
||||||
|
various system-dependent variables used during compilation. It uses
|
||||||
|
those values to create a `Makefile' in each directory of the package.
|
||||||
|
It may also create one or more `.h' files containing system-dependent
|
||||||
|
definitions. Finally, it creates a shell script `config.status' that
|
||||||
|
you can run in the future to recreate the current configuration, and a
|
||||||
|
file `config.log' containing compiler output (useful mainly for
|
||||||
|
debugging `configure').
|
||||||
|
|
||||||
|
It can also use an optional file (typically called `config.cache'
|
||||||
|
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
||||||
|
the results of its tests to speed up reconfiguring. Caching is
|
||||||
|
disabled by default to prevent problems with accidental use of stale
|
||||||
|
cache files.
|
||||||
|
|
||||||
|
If you need to do unusual things to compile the package, please try
|
||||||
|
to figure out how `configure' could check whether to do them, and mail
|
||||||
|
diffs or instructions to the address given in the `README' so they can
|
||||||
|
be considered for the next release. If you are using the cache, and at
|
||||||
|
some point `config.cache' contains results you don't want to keep, you
|
||||||
|
may remove or edit it.
|
||||||
|
|
||||||
|
The file `configure.ac' (or `configure.in') is used to create
|
||||||
|
`configure' by a program called `autoconf'. You need `configure.ac' if
|
||||||
|
you want to change it or regenerate `configure' using a newer version
|
||||||
|
of `autoconf'.
|
||||||
|
|
||||||
|
The simplest way to compile this package is:
|
||||||
|
|
||||||
|
1. `cd' to the directory containing the package's source code and type
|
||||||
|
`./configure' to configure the package for your system.
|
||||||
|
|
||||||
|
Running `configure' might take a while. While running, it prints
|
||||||
|
some messages telling which features it is checking for.
|
||||||
|
|
||||||
|
2. Type `make' to compile the package.
|
||||||
|
|
||||||
|
3. Optionally, type `make check' to run any self-tests that come with
|
||||||
|
the package.
|
||||||
|
|
||||||
|
4. Type `make install' to install the programs and any data files and
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
5. You can remove the program binaries and object files from the
|
||||||
|
source code directory by typing `make clean'. To also remove the
|
||||||
|
files that `configure' created (so you can compile the package for
|
||||||
|
a different kind of computer), type `make distclean'. There is
|
||||||
|
also a `make maintainer-clean' target, but that is intended mainly
|
||||||
|
for the package's developers. If you use it, you may have to get
|
||||||
|
all sorts of other programs in order to regenerate files that came
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
Compilers and Options
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Some systems require unusual options for compilation or linking that the
|
||||||
|
`configure' script does not know about. Run `./configure --help' for
|
||||||
|
details on some of the pertinent environment variables.
|
||||||
|
|
||||||
|
You can give `configure' initial values for configuration parameters
|
||||||
|
by setting variables in the command line or in the environment. Here
|
||||||
|
is an example:
|
||||||
|
|
||||||
|
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
||||||
|
|
||||||
|
Installation Names
|
||||||
|
==================
|
||||||
|
|
||||||
|
By default, `make install' installs the package's commands under
|
||||||
|
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||||
|
can specify an installation prefix other than `/usr/local' by giving
|
||||||
|
`configure' the option `--prefix=PREFIX'.
|
||||||
|
|
||||||
|
You can specify separate installation prefixes for
|
||||||
|
architecture-specific files and architecture-independent files. If you
|
||||||
|
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
||||||
|
PREFIX as the prefix for installing programs and libraries.
|
||||||
|
Documentation and other data files still use the regular prefix.
|
||||||
|
|
||||||
|
In addition, if you use an unusual directory layout you can give
|
||||||
|
options like `--bindir=DIR' to specify different values for particular
|
||||||
|
kinds of files. Run `configure --help' for a list of the directories
|
||||||
|
you can set and what kinds of files go in them.
|
||||||
|
|
||||||
|
If the package supports it, you can cause programs to be installed
|
||||||
|
with an extra prefix or suffix on their names by giving `configure' the
|
||||||
|
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||||
|
|
||||||
|
You can also define the variable DESTDIR when you run make install
|
||||||
|
to install the package in a separate subdirectory. This is useful for
|
||||||
|
packaging.
|
||||||
|
|
||||||
|
Optional Features
|
||||||
|
=================
|
||||||
|
|
||||||
|
Some packages pay attention to `--enable-FEATURE' options to
|
||||||
|
`configure', where FEATURE indicates an optional part of the package.
|
||||||
|
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||||
|
is something like `gnu-as' or `x' (for the X Window System). The
|
||||||
|
`README' should mention any `--enable-' and `--with-' options that the
|
||||||
|
package recognizes.
|
||||||
|
|
||||||
|
For packages that use the X Window System, `configure' can usually
|
||||||
|
find the X include and library files automatically, but if it doesn't,
|
||||||
|
you can use the `configure' options `--x-includes=DIR' and
|
||||||
|
`--x-libraries=DIR' to specify their locations.
|
||||||
|
|
||||||
|
Specifying the System Type
|
||||||
|
==========================
|
||||||
|
|
||||||
|
There may be some features `configure' cannot figure out automatically,
|
||||||
|
but needs to determine by the type of machine the package will run on.
|
||||||
|
Usually, assuming the package is built to be run on the _same_
|
||||||
|
architectures, `configure' can figure that out, but if it prints a
|
||||||
|
message saying it cannot guess the machine type, give it the
|
||||||
|
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||||
|
type, such as `sun4', or a canonical name which has the form:
|
||||||
|
|
||||||
|
CPU-COMPANY-SYSTEM
|
||||||
|
|
||||||
|
where SYSTEM can have one of these forms:
|
||||||
|
|
||||||
|
OS KERNEL-OS
|
||||||
|
|
||||||
|
See the file `config.sub' for the possible values of each field. If
|
||||||
|
`config.sub' isn't included in this package, then this package doesn't
|
||||||
|
need to know the machine type.
|
||||||
|
|
||||||
|
If you are _building_ compiler tools for cross-compiling, you should
|
||||||
|
use the option `--target=TYPE' to select the type of system they will
|
||||||
|
produce code for.
|
||||||
|
|
||||||
|
If you want to _use_ a cross compiler, that generates code for a
|
||||||
|
platform different from the build platform, you should specify the
|
||||||
|
"host" platform (i.e., that on which the generated programs will
|
||||||
|
eventually be run) with `--host=TYPE'.
|
||||||
|
|
||||||
|
Sharing Defaults
|
||||||
|
================
|
||||||
|
|
||||||
|
If you want to set default values for `configure' scripts to share, you
|
||||||
|
can create a site shell script called `config.site' that gives default
|
||||||
|
values for variables like `CC', `cache_file', and `prefix'.
|
||||||
|
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||||
|
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||||
|
`CONFIG_SITE' environment variable to the location of the site script.
|
||||||
|
A warning: not all `configure' scripts look for a site script.
|
||||||
|
|
||||||
|
Defining Variables
|
||||||
|
==================
|
||||||
|
|
||||||
|
Variables not defined in a site shell script can be set in the
|
||||||
|
environment passed to `configure'. However, some packages may run
|
||||||
|
configure again during the build, and the customized values of these
|
||||||
|
variables may be lost. In order to avoid this problem, you should set
|
||||||
|
them in the `configure' command line, using `VAR=value'. For example:
|
||||||
|
|
||||||
|
./configure CC=/usr/local2/bin/gcc
|
||||||
|
|
||||||
|
causes the specified `gcc' to be used as the C compiler (unless it is
|
||||||
|
overridden in the site shell script).
|
||||||
|
|
||||||
|
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
||||||
|
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
||||||
|
|
||||||
|
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||||
|
|
||||||
|
`configure' Invocation
|
||||||
|
======================
|
||||||
|
|
||||||
|
`configure' recognizes the following options to control how it operates.
|
||||||
|
|
||||||
|
`--help'
|
||||||
|
`-h'
|
||||||
|
Print a summary of the options to `configure', and exit.
|
||||||
|
|
||||||
|
`--version'
|
||||||
|
`-V'
|
||||||
|
Print the version of Autoconf used to generate the `configure'
|
||||||
|
script, and exit.
|
||||||
|
|
||||||
|
`--cache-file=FILE'
|
||||||
|
Enable the cache: use and save the results of the tests in FILE,
|
||||||
|
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
||||||
|
disable caching.
|
||||||
|
|
||||||
|
`--config-cache'
|
||||||
|
`-C'
|
||||||
|
Alias for `--cache-file=config.cache'.
|
||||||
|
|
||||||
|
`--quiet'
|
||||||
|
`--silent'
|
||||||
|
`-q'
|
||||||
|
Do not print messages saying which checks are being made. To
|
||||||
|
suppress all normal output, redirect it to `/dev/null' (any error
|
||||||
|
messages will still be shown).
|
||||||
|
|
||||||
|
`--srcdir=DIR'
|
||||||
|
Look for the package's source code in directory DIR. Usually
|
||||||
|
`configure' can determine that directory automatically.
|
||||||
|
|
||||||
|
`configure' also accepts some other, not widely useful, options. Run
|
||||||
|
`configure --help' for more details.
|
||||||
|
|
144
Makefile
Normal file
144
Makefile
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
|
||||||
|
# This makefile is inspired by abuild (http://www.abuild.org), which
|
||||||
|
# was used during the development of qpdf. The goal here is to have a
|
||||||
|
# non-recursive build with all the proper dependencies so we can start
|
||||||
|
# the build from anywhere and get the right stuff. Each directory has
|
||||||
|
# a build.mk that is included from here and is written from this
|
||||||
|
# directory's perspective. Each directory also has a proxy Makefile
|
||||||
|
# that allows you to start the build from any directory and get
|
||||||
|
# reasonable semantics for the all, check, and clean targets.
|
||||||
|
|
||||||
|
# Our "build items" are directories. They are listed here such that
|
||||||
|
# no item precedes any item it depends on. Therefore, each item can
|
||||||
|
# safely reference variables set in its predecessors.
|
||||||
|
|
||||||
|
# For each build item B, you can run make build_B, make check_B, or
|
||||||
|
# make clean_B to build, test, or clean B. Full dependencies are
|
||||||
|
# represented across all the items, so it is possible to start
|
||||||
|
# anywhere. From the top level, the "all", "check", and "clean"
|
||||||
|
# targets build, test, or clean everything.
|
||||||
|
|
||||||
|
# Although this is not a GNU package and does not use automake, you
|
||||||
|
# can still run make clean to remove everything that is compiled, make
|
||||||
|
# distclean to remove everything that is generated by the end user,
|
||||||
|
# and make maintainer-clean to remove everything that is generated
|
||||||
|
# including things distributed with the source distribution. You can
|
||||||
|
# pass CLEAN=1 to prevent this Makefile from complaining if
|
||||||
|
# ./configure has not been run.
|
||||||
|
|
||||||
|
# The install target works as usual and obeys --prefix and so forth
|
||||||
|
# passed to ./configure. You can also pass DESTDIR=/dir to make
|
||||||
|
# install to install in a separate location. This is useful for
|
||||||
|
# packagers.
|
||||||
|
|
||||||
|
BUILD_ITEMS = manual libqpdf zlib-flate libtests qpdf examples
|
||||||
|
OUTPUT_DIR = build
|
||||||
|
ALL_TARGETS =
|
||||||
|
|
||||||
|
.PHONY: default
|
||||||
|
default: all
|
||||||
|
|
||||||
|
CLEAN ?=
|
||||||
|
ifneq ($(CLEAN),1)
|
||||||
|
ifeq ($(words $(wildcard autoconf.mk)),0)
|
||||||
|
DUMMY := $(shell echo 1>&2)
|
||||||
|
DUMMY := $(shell echo 1>&2 Please run ./configure before running $(MAKE))
|
||||||
|
DUMMY := $(shell echo 1>&2)
|
||||||
|
$(error unable to continue with build)
|
||||||
|
endif
|
||||||
|
|
||||||
|
autoconf.mk:
|
||||||
|
|
||||||
|
include autoconf.mk
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Prevent gnu make from trying to rebuild .dep files
|
||||||
|
$(foreach B,$(BUILD_ITEMS),$(eval \
|
||||||
|
$(B)/$(OUTPUT_DIR)/%.dep: ;))
|
||||||
|
|
||||||
|
# Prevent gnu make from trying to rebuild .mk files
|
||||||
|
$(foreach B,$(BUILD_ITEMS),$(eval \
|
||||||
|
$(B)/%.mk: ;))
|
||||||
|
%.mk: ;
|
||||||
|
make/%.mk: ;
|
||||||
|
|
||||||
|
include make/rules.mk
|
||||||
|
|
||||||
|
DUMMY := $(shell mkdir $(foreach B,$(BUILD_ITEMS),$(B)/$(OUTPUT_DIR)) 2>/dev/null)
|
||||||
|
|
||||||
|
include $(foreach B,$(BUILD_ITEMS),$(B)/build.mk)
|
||||||
|
|
||||||
|
ALL_TARGETS = $(foreach B,$(BUILD_ITEMS),$(TARGETS_$(B)))
|
||||||
|
|
||||||
|
TEST_ITEMS = $(foreach D,\
|
||||||
|
$(wildcard $(foreach B,$(BUILD_ITEMS),$(B)/qtest)),\
|
||||||
|
$(subst /,,$(dir $(D))))
|
||||||
|
|
||||||
|
TEST_TARGETS = $(foreach B,$(TEST_ITEMS),check_$(B))
|
||||||
|
|
||||||
|
CLEAN_TARGETS = $(foreach B,$(BUILD_ITEMS),clean_$(B))
|
||||||
|
|
||||||
|
# For test suitse
|
||||||
|
export QPDF_BIN = $(abspath qpdf/$(OUTPUT_DIR)/qpdf)
|
||||||
|
export SKIP_TEST_COMPARE_IMAGES
|
||||||
|
|
||||||
|
clean: $(CLEAN_TARGETS)
|
||||||
|
|
||||||
|
.PHONY: $(CLEAN_TARGETS)
|
||||||
|
$(foreach B,$(BUILD_ITEMS),$(eval \
|
||||||
|
clean_$(B): ; \
|
||||||
|
$(RM) -r $(B)/$(OUTPUT_DIR)))
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
$(RM) -r autoconf.mk autom4te.cache config.log config.status libtool
|
||||||
|
$(RM) manual/html.xsl
|
||||||
|
$(RM) manual/print.xsl
|
||||||
|
$(RM) doc/*.1
|
||||||
|
|
||||||
|
maintainer-clean: distclean
|
||||||
|
$(RM) configure doc/qpdf-manual.*
|
||||||
|
|
||||||
|
.PHONY: $(TEST_TARGETS)
|
||||||
|
$(foreach B,$(TEST_ITEMS),$(eval \
|
||||||
|
check_$(B): $(TARGETS_$(B))))
|
||||||
|
|
||||||
|
.PHONY: $(foreach B,$(BUILD_ITEMS),build_$(B))
|
||||||
|
$(foreach B,$(BUILD_ITEMS),$(eval \
|
||||||
|
build_$(B): $(TARGETS_$(B))))
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(ALL_TARGETS) ;
|
||||||
|
|
||||||
|
check: $(TEST_TARGETS)
|
||||||
|
|
||||||
|
install_docs::
|
||||||
|
install: all
|
||||||
|
./mkinstalldirs $(DESTDIR)$(libdir)
|
||||||
|
./mkinstalldirs $(DESTDIR)$(bindir)
|
||||||
|
./mkinstalldirs $(DESTDIR)$(includedir)/qpdf
|
||||||
|
./mkinstalldirs $(DESTDIR)$(docdir)
|
||||||
|
./mkinstalldirs $(DESTDIR)$(mandir)/man1
|
||||||
|
$(LIBTOOL) --mode=install install -s -c \
|
||||||
|
libqpdf/$(OUTPUT_DIR)/libqpdf.la \
|
||||||
|
$(DESTDIR)$(libdir)/libqpdf.la
|
||||||
|
$(LIBTOOL) --finish $(DESTDIR)$(libdir)
|
||||||
|
$(LIBTOOL) --mode=install install -s -c \
|
||||||
|
qpdf/$(OUTPUT_DIR)/qpdf \
|
||||||
|
$(DESTDIR)$(bindir)/qpdf
|
||||||
|
$(LIBTOOL) --mode=install install -s -c \
|
||||||
|
zlib-flate/$(OUTPUT_DIR)/zlib-flate \
|
||||||
|
$(DESTDIR)$(bindir)/zlib-flate
|
||||||
|
cp qpdf/fix-qdf $(DESTDIR)$(bindir)
|
||||||
|
cp include/qpdf/*.hh $(DESTDIR)$(includedir)/qpdf
|
||||||
|
cp doc/stylesheet.css $(DESTDIR)$(docdir)
|
||||||
|
cp doc/qpdf-manual.html $(DESTDIR)$(docdir)
|
||||||
|
cp doc/qpdf-manual.pdf $(DESTDIR)$(docdir)
|
||||||
|
cp doc/*.1 $(DESTDIR)$(mandir)/man1
|
||||||
|
|
||||||
|
QTEST=$(abspath qtest/bin/qtest-driver)
|
||||||
|
$(TEST_TARGETS):
|
||||||
|
@echo running qtest-driver for $(subst check_,,$@)
|
||||||
|
@(cd $(subst check_,,$@)/$(OUTPUT_DIR); \
|
||||||
|
TC_SRCS="$(foreach T,$(TC_SRCS_$(subst check_,,$@)),../../$(T))" \
|
||||||
|
$(QTEST) -bindirs .:.. -datadir ../qtest -covdir ..)
|
32
README
Normal file
32
README
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
This is the QPDF package. Information about it can be found at
|
||||||
|
http://qpdf.qbilt.org.
|
||||||
|
|
||||||
|
QPDF is copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
|
||||||
|
This software may be distributed under the terms of version 2 of the
|
||||||
|
Artistic License which may be found in the source distribution as
|
||||||
|
"Artistic-2.0". It is provided "as is" without express or implied
|
||||||
|
warranty.
|
||||||
|
|
||||||
|
To install this software, you can run
|
||||||
|
|
||||||
|
./configure
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
|
||||||
|
For more detailed information, see the "INSTALL" in this directory.
|
||||||
|
|
||||||
|
The QPDF package provides some executables and a software library. A
|
||||||
|
user's manual can be found in the "doc" directory. The docbook
|
||||||
|
sources to the user's manual can be found in the "manual" directory.
|
||||||
|
|
||||||
|
The software library is just libqpdf, and all the header files are in
|
||||||
|
the qpdf subdirectory. If you link with -lqpdf and your system does
|
||||||
|
not know how to read libtool .la files, then you will also need to
|
||||||
|
link with -lpcre and -lz.
|
||||||
|
|
||||||
|
To learn about using the library, please read comments in the header
|
||||||
|
files in include/qpdf, especially QPDF.hh, QPDFObjectHandle.hh, and
|
||||||
|
QPDFWriter.hh. You can also study the code of qpdf/qpdf.cc, which
|
||||||
|
exercises most of the public interface. There are additional example
|
||||||
|
programs in the examples directory.
|
69
README.maintainer
Normal file
69
README.maintainer
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
Release Reminders
|
||||||
|
=================
|
||||||
|
|
||||||
|
* To create a source release, do an export from the version control
|
||||||
|
system to a directory called qpdf-version. From the parent of that
|
||||||
|
directory, run make_dist with the directory as an argument. For
|
||||||
|
internally testing releases, you can run make_dist with the
|
||||||
|
--no-tests option.
|
||||||
|
|
||||||
|
* Make sure version numbers are consistent in the following
|
||||||
|
locations:
|
||||||
|
|
||||||
|
configure.ac
|
||||||
|
qpdf.spec
|
||||||
|
qpdf/qpdf.cc
|
||||||
|
manual/qpdf-manual.xml
|
||||||
|
|
||||||
|
make_dist does this automatically.
|
||||||
|
|
||||||
|
* Each year, update copyright notices. Just search for Copyright.
|
||||||
|
Last updated: 2008.
|
||||||
|
|
||||||
|
* To construct a source distribution from a pristine checkout,
|
||||||
|
make_release does the following.
|
||||||
|
|
||||||
|
autoconf
|
||||||
|
./configure --enable-doc-maintenance
|
||||||
|
make build_manual
|
||||||
|
make distclean
|
||||||
|
|
||||||
|
* Remember to update documentation in the "files" subdirectory of the
|
||||||
|
website on sourceforge.net.
|
||||||
|
|
||||||
|
General Build Stuff
|
||||||
|
===================
|
||||||
|
|
||||||
|
QPDF supports autoconf and libtool but does not use automake. In
|
||||||
|
addition, there is no header file generated by autoconf. The only
|
||||||
|
file distributed with the qpdf source distribution that is not a
|
||||||
|
controlled file is "configure", and it is generated by just running
|
||||||
|
"autoconf". There is no need to run autoreconf, automake, autoheader,
|
||||||
|
aclocal, or any other autotools programs beyond autoconf.
|
||||||
|
|
||||||
|
A small handful of additional files have been taken from autotools
|
||||||
|
programs. These should probably be updated from time to time.
|
||||||
|
|
||||||
|
* config.guess, config.sub, ltmain.sh: these were created by running
|
||||||
|
libtoolize -c. To update, run libtoolize -f -c or remove the files
|
||||||
|
and rerun libtoolize.
|
||||||
|
|
||||||
|
* Other files copied as indicated:
|
||||||
|
|
||||||
|
cp /usr/share/aclocal/libtool.m4 aclocal.m4
|
||||||
|
cp /usr/share/automake-1.10/install-sh .
|
||||||
|
cp /usr/share/automake-1.10/mkinstalldirs .
|
||||||
|
|
||||||
|
The entire contents of aclocal.m4 came from libtool.m4. If we had
|
||||||
|
some additional local parts, we could manually combine them or we
|
||||||
|
could run aclocal. For now, the simple copy statement above is
|
||||||
|
sufficient.
|
||||||
|
|
||||||
|
If building or editing documentation, configure with
|
||||||
|
--enable-doc-maintenance. This will ensure that all tools or files
|
||||||
|
required to validate and build documentation are available.
|
||||||
|
|
||||||
|
If you want to run make maintainer-clean or make distclean and you
|
||||||
|
haven't run ./configure, you can pass CLEAN=1 to make on the command
|
||||||
|
line to prevent it from complaining about configure not having been
|
||||||
|
run.
|
68
TODO
Normal file
68
TODO
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
General
|
||||||
|
=======
|
||||||
|
|
||||||
|
* See whether we can do anything with /V > 3 in the encryption
|
||||||
|
dictionary. (V = 4 is Crypt Filters.)
|
||||||
|
|
||||||
|
* The second xref stream for linearized files has to be padded only
|
||||||
|
because we need file_size as computed in pass 1 to be accurate. If
|
||||||
|
we were not allowing writing to a pipe, we could seek back to the
|
||||||
|
beginning and fill in the value of /L in the linearization
|
||||||
|
dictionary as an optimization to alleviate the need for this
|
||||||
|
padding. Doing so would require us to pad the /L value
|
||||||
|
individually and also to save the file descriptor and determine
|
||||||
|
whether it's seekable. This is probably not worth bothering with.
|
||||||
|
|
||||||
|
* The whole xref handling code in the QPDF object allows the same
|
||||||
|
object with more than one generation to coexist, but a lot of logic
|
||||||
|
assumes this isn't the case. Anything that creates mappings only
|
||||||
|
with the object number and not the generation is this way,
|
||||||
|
including most of the interaction between QPDFWriter and QPDF. If
|
||||||
|
we wanted to allow the same object with more than one generation to
|
||||||
|
coexist, which I'm not sure is allowed, we could fix this by
|
||||||
|
changing xref_table. Alternatively, we could detect and disallow
|
||||||
|
that case. In fact, it appears that Adobe reader and other PDF
|
||||||
|
viewing software silently ignores objects of this type, so this is
|
||||||
|
probably not a big deal.
|
||||||
|
|
||||||
|
* Pl_PNGFilter is only partially implemented. If we ever decoded
|
||||||
|
images, we'd have to finish implementing it along with the other
|
||||||
|
filter decode parameters and types. For just handling xref
|
||||||
|
streams, there's really no need as it wouldn't make sense to use
|
||||||
|
any kind of predictor other than 12 (PNG UP filter).
|
||||||
|
|
||||||
|
* If we ever want to have check mode check the integrity of the free
|
||||||
|
list, this can be done by looking at the code from prior to the
|
||||||
|
object stream support of 4/5/2008. It's in an if (0) block and
|
||||||
|
there's a comment about it. There's also something about it in
|
||||||
|
qpdf.test -- search for "free table". On the other hand, the value
|
||||||
|
of doing this seems very low since no viewer seems to care, so it's
|
||||||
|
probably not worth it.
|
||||||
|
|
||||||
|
* Embedded files streams: figure out why running qpdf over the pdf
|
||||||
|
1.7 spec results in a file that crashes acrobat reader when you try
|
||||||
|
to save nested documents.
|
||||||
|
|
||||||
|
* QPDFObjectHandle::getPageImages() doesn't notice images in
|
||||||
|
inherited resource dictionaries. See comments in that function.
|
||||||
|
|
||||||
|
Splitting by Pages
|
||||||
|
==================
|
||||||
|
|
||||||
|
Although qpdf does not currently support splitting a file into pages,
|
||||||
|
the work done for linearization covers almost all the work. To do
|
||||||
|
page splitting. If this functionality is needed, study
|
||||||
|
obj_user_to_objects and object_to_obj_users created in
|
||||||
|
QPDF_optimization for ideas. It's quite possible that the information
|
||||||
|
computed by calculateLinearizationData is actually sufficient to do
|
||||||
|
page splitting in many circumstances. That code knows which objects
|
||||||
|
are used by which pages, though it doesn't do anything page-specific
|
||||||
|
with outlines, thumbnails, page labels, or anything else.
|
||||||
|
|
||||||
|
Another approach would be to traverse only pages that are being output
|
||||||
|
taking care not to traverse into the pages tree, and then to fabricate
|
||||||
|
a new pages tree.
|
||||||
|
|
||||||
|
Either way, care must be taken to handle other things such as
|
||||||
|
outlines, page labels, thumbnails, threads, etc. in a sensible way.
|
||||||
|
This may include simply omitting information other than page content.
|
6672
aclocal.m4
vendored
Normal file
6672
aclocal.m4
vendored
Normal file
File diff suppressed because it is too large
Load Diff
30
autoconf.mk.in
Normal file
30
autoconf.mk.in
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
PACKAGE_TARNAME=@PACKAGE_TARNAME@
|
||||||
|
PACKAGE_VERSION=@PACKAGE_VERSION@
|
||||||
|
top_builddir=@top_builddir@
|
||||||
|
prefix=@prefix@
|
||||||
|
exec_prefix=@exec_prefix@
|
||||||
|
bindir=@bindir@
|
||||||
|
libdir=@libdir@
|
||||||
|
includedir=@includedir@
|
||||||
|
datarootdir=@datarootdir@
|
||||||
|
mandir=@mandir@
|
||||||
|
docdir=@docdir@
|
||||||
|
htmldir=@htmldir@
|
||||||
|
pdfdir=@pdfdir
|
||||||
|
CC=@CC@
|
||||||
|
CFLAGS=@CFLAGS@
|
||||||
|
LDFLAGS=@LDFLAGS@
|
||||||
|
LIBS=@LIBS@
|
||||||
|
CPPFLAGS=@CPPFLAGS@
|
||||||
|
CXX=@CXX@
|
||||||
|
CXXFLAGS=@CXXFLAGS@
|
||||||
|
GENDEPS=@GENDEPS@
|
||||||
|
LIBTOOL=@LIBTOOL@
|
||||||
|
DOCBOOKX_DTD=@DOCBOOKX_DTD@
|
||||||
|
FOP=@FOP@
|
||||||
|
XSLTPROC=@XSLTPROC@
|
||||||
|
XMLLINT=@XMLLINT@
|
||||||
|
BUILD_HTML=@BUILD_HTML@
|
||||||
|
BUILD_PDF=@BUILD_PDF@
|
||||||
|
VALIDATE_DOC=@VALIDATE_DOC@
|
||||||
|
SKIP_TEST_COMPARE_IMAGES=@SKIP_TEST_COMPARE_IMAGES@
|
1526
config.guess
vendored
Executable file
1526
config.guess
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1658
config.sub
vendored
Executable file
1658
config.sub
vendored
Executable file
File diff suppressed because it is too large
Load Diff
282
configure.ac
Normal file
282
configure.ac
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
dnl Process this file with autoconf to produce a configure script.
|
||||||
|
dnl This config.in requires autoconf 2.5 or greater.
|
||||||
|
|
||||||
|
AC_PREREQ(2.59)
|
||||||
|
AC_INIT(qpdf,2.0)
|
||||||
|
|
||||||
|
dnl No AC_CONFIG_HEADERS. If this changes, update README.maintainer.
|
||||||
|
AC_CONFIG_FILES([autoconf.mk])
|
||||||
|
AC_CONFIG_FILES([manual/html.xsl manual/print.xsl])
|
||||||
|
|
||||||
|
AC_PROG_CC
|
||||||
|
AC_PROG_CXX
|
||||||
|
AC_HEADER_STDC
|
||||||
|
AC_PROG_LIBTOOL
|
||||||
|
|
||||||
|
AC_CHECK_HEADER(zlib.h,,[MISSING_ZLIB_H=1; MISSING_ANY=1])
|
||||||
|
AC_SEARCH_LIBS(deflate,z zlib,,[MISSING_ZLIB=1; MISSING_ANY=1])
|
||||||
|
AC_CHECK_HEADER(pcre.h,,[MISSING_PCRE_H=1; MISSING_ANY=1])
|
||||||
|
AC_SEARCH_LIBS(pcre_compile,pcre,,[MISSING_PCRE=1; MISSING_ANY=1])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING(for gnu make >= 3.81)
|
||||||
|
make_okay=0
|
||||||
|
if make --version >/dev/null 2>&1; then
|
||||||
|
v=`make --version | grep 'GNU Make' | sed -e 's/.*Make //'`
|
||||||
|
maj=`echo $v | cut -d. -f 1`
|
||||||
|
min=`echo $v | cut -d. -f 2`
|
||||||
|
if test $maj -gt 3 -o '(' $maj -eq 3 -a $min -ge 81 ')'; then
|
||||||
|
make_okay=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if test "$make_okay" = "1"; then
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
else
|
||||||
|
dnl Don't set MISSING_ANY=1 -- maybe user calls make something else
|
||||||
|
MISSING_MAKE_381=1
|
||||||
|
ISSUE_WARNINGS=1
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_SUBST(GENDEPS)
|
||||||
|
GENDEPS=0
|
||||||
|
AC_MSG_CHECKING(for whether $CC supports -MD -MF x.dep -MP)
|
||||||
|
oCFLAGS=$CFLAGS
|
||||||
|
rm -f x.dep
|
||||||
|
CFLAGS="$CFLAGS -MD -MF x.dep -MP"
|
||||||
|
AC_TRY_COMPILE([#include <stdio.h>], [FILE* a = stdout],
|
||||||
|
qpdf_DEPFLAGS=yes,
|
||||||
|
qpdf_DEPFLAGS=no)
|
||||||
|
CFLAGS=$oCFLAGS
|
||||||
|
if test "$qpdf_DEPFLAGS" = "yes"; then
|
||||||
|
if ! grep stdio.h x.dep >/dev/null 2>&1; then
|
||||||
|
qpdf_DEPFLAGS=no
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
rm -f x.dep
|
||||||
|
if test "$qpdf_DEPFLAGS" = "yes"; then
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
GENDEPS=1
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
fi
|
||||||
|
|
||||||
|
qpdf_USE_WALL=0
|
||||||
|
AC_MSG_CHECKING(for whether $CC supports -Wall)
|
||||||
|
oCFLAGS=$CFLAGS
|
||||||
|
CFLAGS="$CFLAGS -Wall"
|
||||||
|
AC_TRY_COMPILE([], [int a = 1; int b = a; a = b;],
|
||||||
|
qpdf_USE_WALL=1,
|
||||||
|
qpdf_USE_WALL=0)
|
||||||
|
if test "$qpdf_USE_WALL" = "1"; then
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
CXXFLAGS="$CXXFLAGS -Wall"
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
CFLAGS=$oCFLAGS
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_MSG_CHECKING(for whether to use -Werror)
|
||||||
|
AC_ARG_ENABLE(werror,
|
||||||
|
AS_HELP_STRING([--enable-werror],
|
||||||
|
[whether to use werror (default is yes if -Wall works)]),
|
||||||
|
[if test "$enableval" = "yes"; then
|
||||||
|
qpdf_USE_WERROR=1;
|
||||||
|
else
|
||||||
|
qpdf_USE_WERROR=0;
|
||||||
|
fi], [qpdf_USE_WERROR=$qpdf_USE_WALL])
|
||||||
|
if test "$qpdf_USE_WERROR" = "1"; then
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
CFLAGS="$CFLAGS -Werror"
|
||||||
|
CXXFLAGS="$CXXFLAGS -Werror"
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_SUBST(SKIP_TEST_COMPARE_IMAGES)
|
||||||
|
AC_ARG_ENABLE(test-compare-images,
|
||||||
|
AS_HELP_STRING([--enable-test-compare-images],
|
||||||
|
[whether to compare images in test suite; enabled by default, enabling requires ghostscript and tiffcmp to be available]),
|
||||||
|
[if test "$enableval" = "no"; then
|
||||||
|
SKIP_TEST_COMPARE_IMAGES=1
|
||||||
|
else
|
||||||
|
SKIP_TEST_COMPARE_IMAGES=0
|
||||||
|
fi],
|
||||||
|
[SKIP_TEST_COMPARE_IMAGES=0])
|
||||||
|
|
||||||
|
AC_ARG_WITH(docbook-xsl,
|
||||||
|
AS_HELP_STRING([--with-docbook-xsl=DIR],
|
||||||
|
[location of docbook 4.x xml stylesheets]),
|
||||||
|
[DOCBOOK_XSL=$withval],
|
||||||
|
[DOCBOOK_XSL=/usr/share/xml/docbook/stylesheet/nwalsh])
|
||||||
|
|
||||||
|
DOCBOOK_XHTML=
|
||||||
|
AC_SUBST(DOCBOOK_XHTML)
|
||||||
|
AC_MSG_CHECKING(for xml to xhtml docbook stylesheets)
|
||||||
|
if test -f "$DOCBOOK_XSL/xhtml/docbook.xsl"; then
|
||||||
|
DOCBOOK_XHTML="$DOCBOOK_XSL/xhtml/docbook.xsl"
|
||||||
|
AC_MSG_RESULT($DOCBOOK_XHTML)
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
fi
|
||||||
|
DOCBOOK_FO=
|
||||||
|
AC_SUBST(DOCBOOK_FO)
|
||||||
|
AC_MSG_CHECKING(for xml to fo docbook stylesheets)
|
||||||
|
if test -f "$DOCBOOK_XSL/fo/docbook.xsl"; then
|
||||||
|
DOCBOOK_FO="$DOCBOOK_XSL/fo/docbook.xsl"
|
||||||
|
AC_MSG_RESULT($DOCBOOK_FO)
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
fi
|
||||||
|
|
||||||
|
DOCBOOKX_DTD=
|
||||||
|
AC_SUBST(DOCBOOKX_DTD)
|
||||||
|
AC_ARG_WITH(docbookx-dtd,
|
||||||
|
AS_HELP_STRING([--with-docbookx-dtd=FILE],
|
||||||
|
[location of docbook 4.x xml DTD]),
|
||||||
|
[DOCBOOKX_DTD=$withval],
|
||||||
|
[DOCBOOKX_DTD=/usr/share/xml/docbook/schema/dtd/4/docbookx.dtd])
|
||||||
|
AC_MSG_CHECKING(for docbook 4.x xml DTD)
|
||||||
|
if test -f "$DOCBOOKX_DTD"; then
|
||||||
|
AC_MSG_RESULT($DOCBOOKX_DTD)
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_CHECK_PROG(FOP,fop,fop,[])
|
||||||
|
AC_CHECK_PROG(XSLTPROC,xsltproc,xsltproc,[])
|
||||||
|
AC_CHECK_PROG(XMLLINT,xmllint,xmllint,[])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(doc-maintenance,
|
||||||
|
AS_HELP_STRING([--enable-doc-maintenance],
|
||||||
|
[if set, enables all documentation options]),
|
||||||
|
[if test "$enableval" = "yes"; then
|
||||||
|
doc_default=1;
|
||||||
|
else
|
||||||
|
doc_default=0;
|
||||||
|
fi],
|
||||||
|
[doc_default=0])
|
||||||
|
|
||||||
|
BUILD_HTML=0
|
||||||
|
AC_SUBST(BUILD_HTML)
|
||||||
|
AC_ARG_ENABLE(html-doc,
|
||||||
|
AS_HELP_STRING([--enable-html-doc],
|
||||||
|
[whether to build HTML documents]),
|
||||||
|
[if test "$enableval" = "yes"; then
|
||||||
|
BUILD_HTML=1;
|
||||||
|
else
|
||||||
|
BUILD_HTML=0;
|
||||||
|
fi],
|
||||||
|
[BUILD_HTML=$doc_default])
|
||||||
|
BUILD_PDF=0
|
||||||
|
AC_SUBST(BUILD_PDF)
|
||||||
|
AC_ARG_ENABLE(pdf-doc,
|
||||||
|
AS_HELP_STRING([--enable-pdf-doc],
|
||||||
|
[whether to build PDF documents]),
|
||||||
|
[if test "$enableval" = "yes"; then
|
||||||
|
BUILD_PDF=1;
|
||||||
|
else
|
||||||
|
BUILD_PDF=0;
|
||||||
|
fi],
|
||||||
|
[BUILD_PDF=$doc_default])
|
||||||
|
VALIDATE_DOC=0
|
||||||
|
AC_SUBST(VALIDATE_DOC)
|
||||||
|
AC_ARG_ENABLE(validate-doc,
|
||||||
|
AS_HELP_STRING([--enable-validate-doc],
|
||||||
|
[whether to validate xml document source]),
|
||||||
|
[if test "$enableval" = "yes"; then
|
||||||
|
VALIDATE_DOC=1;
|
||||||
|
else
|
||||||
|
VALIDATE_DOC=0;
|
||||||
|
fi],
|
||||||
|
[VALIDATE_DOC=$doc_default])
|
||||||
|
|
||||||
|
if test "$VALIDATE_DOC" = "1"; then
|
||||||
|
if test "$XMLLINT" = ""; then
|
||||||
|
MISSING_XMLLINT=1
|
||||||
|
MISSING_ANY=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if test "$BUILD_HTML" = "1"; then
|
||||||
|
if test "$XSLTPROC" = ""; then
|
||||||
|
MISSING_XSLTPROC=1
|
||||||
|
MISSING_ANY=1
|
||||||
|
fi
|
||||||
|
if test "$DOCBOOK_XHTML" = ""; then
|
||||||
|
MISSING_DOCBOOK_XHTML=1
|
||||||
|
MISSING_ANY=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if test "$BUILD_PDF" = "1"; then
|
||||||
|
if test "$XSLTPROC" = ""; then
|
||||||
|
MISSING_XSLTPROC=1
|
||||||
|
MISSING_ANY=1
|
||||||
|
fi
|
||||||
|
if test "$DOCBOOK_FO" = ""; then
|
||||||
|
MISSING_DOCBOOK_FO=1
|
||||||
|
MISSING_ANY=1
|
||||||
|
fi
|
||||||
|
if test "$FOP" = ""; then
|
||||||
|
MISSING_FOP=1
|
||||||
|
MISSING_ANY=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if test "$MISSING_ANY" = "1"; then
|
||||||
|
ISSUE_WARNINGS=1
|
||||||
|
fi
|
||||||
|
if test "$ISSUE_WARNINGS" = "1"; then
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$MISSING_MAKE_381" = "1"; then
|
||||||
|
AC_MSG_WARN(gnu make >= 3.81 is required)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$MISSING_ZLIB_H" = "1"; then
|
||||||
|
AC_MSG_WARN(unable to find required header zlib.h)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$MISSING_ZLIB" = "1"; then
|
||||||
|
AC_MSG_WARN(unable to find required library z (or zlib))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$MISSING_PCRE_H" = "1"; then
|
||||||
|
AC_MSG_WARN(unable to find required header pcre.h)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$MISSING_PCRE" = "1"; then
|
||||||
|
AC_MSG_WARN(unable to find required library pcre)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$MISSING_DOCBOOK_FO" = "1"; then
|
||||||
|
AC_MSG_WARN(docbook fo stylesheets are required to build PDF documentation)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$MISSING_DOCBOOK_XHTML" = "1"; then
|
||||||
|
AC_MSG_WARN(docbook xhmtl stylesheets are required to build HTML documentation)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$MISSING_FOP" = "1"; then
|
||||||
|
AC_MSG_WARN(apache fop is required to build PDF documentation)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$MISSING_XMLLINT" = "1"; then
|
||||||
|
AC_MSG_WARN(xmllint is required to validate documentation)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$MISSING_XSLTPROC" = "1"; then
|
||||||
|
AC_MSG_WARN(xsltproc is required to build documentation)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$ISSUE_WARNINGS" = "1"; then
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$MISSING_ANY" = "1"; then
|
||||||
|
AC_MSG_ERROR(some required prerequisites were not found)
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_OUTPUT()
|
284
doc/stylesheet.css
Normal file
284
doc/stylesheet.css
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
/**************************************************************/
|
||||||
|
/* Custom style-sheet for the QPDF manual in HTML form. */
|
||||||
|
/**************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is the CSS for the QPDF manual. It is based heavily on
|
||||||
|
* the CSS for the Subversion book. That file contains the following
|
||||||
|
* copyright and attribution:
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2007
|
||||||
|
* Ben Collins-Sussman, Brian W. Fitzpatrick, C. Michael Pilato.
|
||||||
|
*
|
||||||
|
* This work is licensed under the Creative Commons Attribution License.
|
||||||
|
* To view a copy of this license, visit
|
||||||
|
* http://creativecommons.org/licenses/by/2.0/ or send a letter to
|
||||||
|
* Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body
|
||||||
|
{
|
||||||
|
background: white;
|
||||||
|
margin: 0.5in;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li, ul, ol, dd, dt
|
||||||
|
{
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt, pre
|
||||||
|
{
|
||||||
|
font-family: courier new,courier,fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
a
|
||||||
|
{
|
||||||
|
color: blue;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover
|
||||||
|
{
|
||||||
|
background: rgb(75%,75%,100%);
|
||||||
|
color: blue;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited
|
||||||
|
{
|
||||||
|
color: purple;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
img
|
||||||
|
{
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.title
|
||||||
|
{
|
||||||
|
font-size: 250%;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2.subtitle
|
||||||
|
{
|
||||||
|
font-size: 150%;
|
||||||
|
font-style: italic;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2.title
|
||||||
|
{
|
||||||
|
font-size: 150%;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3.title
|
||||||
|
{
|
||||||
|
font-size: 125%;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4.title
|
||||||
|
{
|
||||||
|
font-size: 100%;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc b
|
||||||
|
{
|
||||||
|
font-size: 125%;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen, .programlisting, .literal
|
||||||
|
{
|
||||||
|
font-family: courier new,courier,fixed;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command, .option, .type
|
||||||
|
{
|
||||||
|
font-family: courier new,courier,fixed;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filename
|
||||||
|
{
|
||||||
|
font-family: arial,helvetica,sans-serif;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.property
|
||||||
|
{
|
||||||
|
font-family: arial,helvetica,sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.classname
|
||||||
|
{
|
||||||
|
font-family: arial,helvetica,sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.varname, .function, .envar
|
||||||
|
{
|
||||||
|
font-family: arial,helvetica,sans-serif;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.replaceable
|
||||||
|
{
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.figure, .example, .table
|
||||||
|
{
|
||||||
|
margin: 0.125in 0.25in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table table
|
||||||
|
{
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: black;
|
||||||
|
border-spacing: 0;
|
||||||
|
background: rgb(240,240,240);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table td
|
||||||
|
{
|
||||||
|
border: none;
|
||||||
|
border-right: 1px black solid;
|
||||||
|
border-bottom: 1px black solid;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th
|
||||||
|
{
|
||||||
|
background: rgb(180,180,180);
|
||||||
|
border: none;
|
||||||
|
border-right: 1px black solid;
|
||||||
|
border-bottom: 1px black solid;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table p.title, .figure p.title, .example p.title
|
||||||
|
{
|
||||||
|
text-align: left !important;
|
||||||
|
font-size: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author, .pubdate
|
||||||
|
{
|
||||||
|
margin: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: normal;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preface div.author, .preface .pubdate
|
||||||
|
{
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar
|
||||||
|
{
|
||||||
|
border-top: dotted 1px black;
|
||||||
|
border-left: dotted 1px black;
|
||||||
|
border-right: solid 2px black;
|
||||||
|
border-bottom: solid 2px black;
|
||||||
|
background: rgb(240,220,170);
|
||||||
|
padding: 0 0.12in;
|
||||||
|
margin: 0.25in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note .programlisting, .note .screen,
|
||||||
|
.tip .programlisting, .tip .screen,
|
||||||
|
.warning .programlisting, .warning .screen,
|
||||||
|
.sidebar .programlisting, .sidebar .screen
|
||||||
|
{
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar p.title
|
||||||
|
{
|
||||||
|
text-align: center;
|
||||||
|
font-size: 125%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note
|
||||||
|
{
|
||||||
|
border: black solid 1px;
|
||||||
|
background: url(./images/note.png) no-repeat rgb(252,246,220);
|
||||||
|
margin: 0.125in 0;
|
||||||
|
padding: 0 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip
|
||||||
|
{
|
||||||
|
border: black solid 1px;
|
||||||
|
background: url(./images/tip.png) no-repeat rgb(224,244,255);
|
||||||
|
margin: 0.125in 0;
|
||||||
|
padding: 0 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning
|
||||||
|
{
|
||||||
|
border: black solid 1px;
|
||||||
|
background: url(./images/warning.png) no-repeat rgb(255,210,210);
|
||||||
|
margin: 0.125in 0;
|
||||||
|
padding: 0 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
.note .title, .tip .title, .warning .title
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
.programlisting, .screen
|
||||||
|
{
|
||||||
|
font-size: 90%;
|
||||||
|
color: black;
|
||||||
|
margin: 1em 0.25in;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: rgb(240,240,240);
|
||||||
|
border-top: black dotted 1px;
|
||||||
|
border-left: black dotted 1px;
|
||||||
|
border-right: black solid 2px;
|
||||||
|
border-bottom: black solid 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navheader, .navfooter
|
||||||
|
{
|
||||||
|
border: black solid 1px;
|
||||||
|
background: rgb(180,180,200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navheader hr, .navfooter hr
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
1
examples/Makefile
Normal file
1
examples/Makefile
Normal file
@ -0,0 +1 @@
|
|||||||
|
include ../make/proxy.mk
|
26
examples/build.mk
Normal file
26
examples/build.mk
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
BINS_examples = pdf-bookmarks pdf-mod-info pdf-npages
|
||||||
|
|
||||||
|
TARGETS_examples = $(foreach B,$(BINS_examples),examples/$(OUTPUT_DIR)/$(B))
|
||||||
|
|
||||||
|
$(TARGETS_examples): $(TARGETS_qpdf)
|
||||||
|
|
||||||
|
INCLUDES_examples = include
|
||||||
|
|
||||||
|
TC_SRCS_examples = $(wildcard examples/*.cc)
|
||||||
|
|
||||||
|
# -----
|
||||||
|
|
||||||
|
$(foreach B,$(BINS_examples),$(eval \
|
||||||
|
OBJS_$(B) = $(call src_to_obj,examples/$(B).cc)))
|
||||||
|
|
||||||
|
ifeq ($(GENDEPS),1)
|
||||||
|
-include $(foreach B,$(BINS_examples),$(call obj_to_dep,$(OBJS_$(B))))
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(foreach B,$(BINS_examples),$(eval \
|
||||||
|
$(OBJS_$(B)): examples/$(OUTPUT_DIR)/%.o: examples/$(B).cc ; \
|
||||||
|
$(call compile,examples/$(B).cc,$(INCLUDES_examples))))
|
||||||
|
|
||||||
|
$(foreach B,$(BINS_examples),$(eval \
|
||||||
|
examples/$(OUTPUT_DIR)/$(B): $(OBJS_$(B)) ; \
|
||||||
|
$(call makebin,$(OBJS_$(B)),$$@)))
|
19
examples/examples.testcov
Normal file
19
examples/examples.testcov
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
pdf-bookmarks lines 0
|
||||||
|
pdf-bookmarks numbers 0
|
||||||
|
pdf-bookmarks none 0
|
||||||
|
pdf-bookmarks has count 0
|
||||||
|
pdf-bookmarks no count 0
|
||||||
|
pdf-bookmarks open 0
|
||||||
|
pdf-bookmarks closed 0
|
||||||
|
pdf-bookmarks dest 0
|
||||||
|
pdf-bookmarks targets 0
|
||||||
|
pdf-mod-info --dump 0
|
||||||
|
pdf-mod-info no in file 0
|
||||||
|
pdf-mod-info in-place 0
|
||||||
|
pdf-mod-info -key 0
|
||||||
|
pdf-mod-info usage wrong val 0
|
||||||
|
pdf-mod-info -val 0
|
||||||
|
pdf-mod-info usage junk 0
|
||||||
|
pdf-mod-info no keys 0
|
||||||
|
pdf-mod-info has info 0
|
||||||
|
pdf-mod-info file no info 0
|
262
examples/pdf-bookmarks.cc
Normal file
262
examples/pdf-bookmarks.cc
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <string.h>
|
||||||
|
#include <qpdf/QPDF.hh>
|
||||||
|
#include <qpdf/QUtil.hh>
|
||||||
|
#include <qpdf/QTC.hh>
|
||||||
|
|
||||||
|
static char const* whoami = 0;
|
||||||
|
static enum { st_none, st_numbers, st_lines } style = st_none;
|
||||||
|
static bool show_open = false;
|
||||||
|
static bool show_targets = false;
|
||||||
|
static std::map<int, int> page_map;
|
||||||
|
|
||||||
|
void usage()
|
||||||
|
{
|
||||||
|
std::cerr << "Usage: " << whoami << " [options] file.pdf [password]"
|
||||||
|
<< std::endl
|
||||||
|
<< "Options:" << std::endl
|
||||||
|
<< " -numbers give bookmarks outline-style numbers"
|
||||||
|
<< std::endl
|
||||||
|
<< " -lines draw lines to show bookmark hierarchy"
|
||||||
|
<< std::endl
|
||||||
|
<< " -show-open indicate whether a bookmark is initially open"
|
||||||
|
<< std::endl
|
||||||
|
<< " -show-targets show target if possible"
|
||||||
|
<< std::endl;
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_lines(std::vector<int>& numbers)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < numbers.size() - 1; ++i)
|
||||||
|
{
|
||||||
|
if (numbers[i])
|
||||||
|
{
|
||||||
|
std::cout << "| ";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_page_map(QPDF& qpdf)
|
||||||
|
{
|
||||||
|
std::vector<QPDFObjectHandle> pages = qpdf.getAllPages();
|
||||||
|
int n = 0;
|
||||||
|
for (std::vector<QPDFObjectHandle>::iterator iter = pages.begin();
|
||||||
|
iter != pages.end(); ++iter)
|
||||||
|
{
|
||||||
|
QPDFObjectHandle& oh = *iter;
|
||||||
|
page_map[oh.getObjectID()] = ++n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void extract_bookmarks(QPDFObjectHandle outlines, std::vector<int>& numbers)
|
||||||
|
{
|
||||||
|
if (outlines.hasKey("/Title"))
|
||||||
|
{
|
||||||
|
// No default so gcc will warn on missing tag
|
||||||
|
switch (style)
|
||||||
|
{
|
||||||
|
case st_none:
|
||||||
|
QTC::TC("examples", "pdf-bookmarks none");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case st_numbers:
|
||||||
|
QTC::TC("examples", "pdf-bookmarks numbers");
|
||||||
|
for (std::vector<int>::iterator iter = numbers.begin();
|
||||||
|
iter != numbers.end(); ++iter)
|
||||||
|
{
|
||||||
|
std::cout << *iter << ".";
|
||||||
|
}
|
||||||
|
std::cout << " ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case st_lines:
|
||||||
|
QTC::TC("examples", "pdf-bookmarks lines");
|
||||||
|
print_lines(numbers);
|
||||||
|
std::cout << "|" << std::endl;
|
||||||
|
print_lines(numbers);
|
||||||
|
std::cout << "+-+ ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_open)
|
||||||
|
{
|
||||||
|
if (outlines.hasKey("/Count"))
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-bookmarks has count");
|
||||||
|
int count = outlines.getKey("/Count").getIntValue();
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
// hierarchy is open at this point
|
||||||
|
QTC::TC("examples", "pdf-bookmarks open");
|
||||||
|
std::cout << "(v) ";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-bookmarks closed");
|
||||||
|
std::cout << "(>) ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-bookmarks no count");
|
||||||
|
std::cout << "( ) ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_targets)
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-bookmarks targets");
|
||||||
|
std::string target = "unknown";
|
||||||
|
// Only explicit destinations supported for now
|
||||||
|
if (outlines.hasKey("/Dest"))
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-bookmarks dest");
|
||||||
|
QPDFObjectHandle dest = outlines.getKey("/Dest");
|
||||||
|
if ((dest.isArray()) && (dest.getArrayNItems() > 0))
|
||||||
|
{
|
||||||
|
QPDFObjectHandle first = dest.getArrayItem(0);
|
||||||
|
int object_id = first.getObjectID();
|
||||||
|
if (page_map.count(object_id))
|
||||||
|
{
|
||||||
|
target = QUtil::int_to_string(page_map[object_id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "[ -> " << target << " ] ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << outlines.getKey("/Title").getUTF8Value() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outlines.hasKey("/First"))
|
||||||
|
{
|
||||||
|
numbers.push_back(0);
|
||||||
|
QPDFObjectHandle child = outlines.getKey("/First");
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
++(numbers.back());
|
||||||
|
bool has_next = child.hasKey("/Next");
|
||||||
|
if ((style == st_lines) && (! has_next))
|
||||||
|
{
|
||||||
|
numbers.back() = 0;
|
||||||
|
}
|
||||||
|
extract_bookmarks(child, numbers);
|
||||||
|
if (has_next)
|
||||||
|
{
|
||||||
|
child = child.getKey("/Next");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numbers.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
if ((whoami = strrchr(argv[0], '/')) == NULL)
|
||||||
|
{
|
||||||
|
whoami = argv[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++whoami;
|
||||||
|
}
|
||||||
|
// For libtool's sake....
|
||||||
|
if (strncmp(whoami, "lt-", 3) == 0)
|
||||||
|
{
|
||||||
|
whoami += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((argc == 2) && (strcmp(argv[1], "--version") == 0))
|
||||||
|
{
|
||||||
|
std::cout << whoami << " version 1.5" << std::endl;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int arg;
|
||||||
|
for (arg = 1; arg < argc; ++arg)
|
||||||
|
{
|
||||||
|
if (argv[arg][0] == '-')
|
||||||
|
{
|
||||||
|
if (strcmp(argv[arg], "-numbers") == 0)
|
||||||
|
{
|
||||||
|
style = st_numbers;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[arg], "-lines") == 0)
|
||||||
|
{
|
||||||
|
style = st_lines;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[arg], "-show-open") == 0)
|
||||||
|
{
|
||||||
|
show_open = true;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[arg], "-show-targets") == 0)
|
||||||
|
{
|
||||||
|
show_targets = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg >= argc)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
char const* filename = argv[arg++];
|
||||||
|
char const* password = "";
|
||||||
|
|
||||||
|
if (arg < argc)
|
||||||
|
{
|
||||||
|
password = argv[arg++];
|
||||||
|
}
|
||||||
|
if (arg != argc)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QPDF qpdf;
|
||||||
|
qpdf.processFile(filename, password);
|
||||||
|
|
||||||
|
QPDFObjectHandle root = qpdf.getRoot();
|
||||||
|
if (root.hasKey("/Outlines"))
|
||||||
|
{
|
||||||
|
std::vector<int> numbers;
|
||||||
|
if (show_targets)
|
||||||
|
{
|
||||||
|
generate_page_map(qpdf);
|
||||||
|
}
|
||||||
|
extract_bookmarks(root.getKey("/Outlines"), numbers);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << filename << " has no bookmarks" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
std::cerr << whoami << " processing file " << filename << ": "
|
||||||
|
<< e.what() << std::endl;
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
219
examples/pdf-mod-info.cc
Normal file
219
examples/pdf-mod-info.cc
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
// Author: Vitaliy Pavlyuk
|
||||||
|
|
||||||
|
#include <qpdf/QPDF.hh>
|
||||||
|
#include <qpdf/QPDFWriter.hh>
|
||||||
|
#include <qpdf/QPDFObjectHandle.hh>
|
||||||
|
#include <qpdf/QUtil.hh>
|
||||||
|
#include <qpdf/QTC.hh>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static char const* version = "1.1";
|
||||||
|
static char const* whoami = 0;
|
||||||
|
|
||||||
|
void usage()
|
||||||
|
{
|
||||||
|
std::cerr
|
||||||
|
<< "Usage: " << whoami
|
||||||
|
<< " -in in_file [-out out_file] [-key key [-val val]?]+\n"
|
||||||
|
<< "Modifies/Adds/Removes PDF /Info entries in the in_file\n"
|
||||||
|
<< "and stores the result in out_file\n"
|
||||||
|
<< "Special mode: " << whoami << " --dump file\n"
|
||||||
|
<< "dumps all /Info entries to stdout\n";
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dumpInfoDict(QPDF& pdf,
|
||||||
|
std::ostream& os = std::cout,
|
||||||
|
std::string const& sep = ":\t")
|
||||||
|
{
|
||||||
|
QPDFObjectHandle trailer = pdf.getTrailer();
|
||||||
|
if (trailer.hasKey("/Info"))
|
||||||
|
{
|
||||||
|
QPDFObjectHandle info = trailer.getKey("/Info");
|
||||||
|
std::set<std::string> keys = info.getKeys();
|
||||||
|
for (std::set<std::string>::const_iterator it = keys.begin();
|
||||||
|
keys.end() != it; ++it)
|
||||||
|
{
|
||||||
|
QPDFObjectHandle elt = info.getKey(*it);
|
||||||
|
std::string val;
|
||||||
|
if (false) {}
|
||||||
|
else if (elt.isString())
|
||||||
|
{
|
||||||
|
val = elt.getStringValue();
|
||||||
|
}
|
||||||
|
else if (elt.isName())
|
||||||
|
{
|
||||||
|
val = elt.getName();
|
||||||
|
}
|
||||||
|
else // according to PDF Spec 1.5, shouldn't happen
|
||||||
|
{
|
||||||
|
val = elt.unparseResolved();
|
||||||
|
}
|
||||||
|
os << it->substr(1) << sep << val << std::endl; // skip '/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pdfDumpInfoDict(char const* fname)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QPDF pdf;
|
||||||
|
pdf.processFile(fname);
|
||||||
|
dumpInfoDict(pdf);
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
bool static_id = false;
|
||||||
|
std::map<std::string, std::string> Keys;
|
||||||
|
|
||||||
|
if ((whoami = strrchr(argv[0], '/')) == NULL)
|
||||||
|
{
|
||||||
|
whoami = argv[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++whoami;
|
||||||
|
}
|
||||||
|
// For libtool's sake....
|
||||||
|
if (strncmp(whoami, "lt-", 3) == 0)
|
||||||
|
{
|
||||||
|
whoami += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((argc == 2) && (! strcmp(argv[1], "--version")) )
|
||||||
|
{
|
||||||
|
std::cout << whoami << " version " << version << std::endl;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if ((argc == 4) && (! strcmp(argv[1], "--dump")) &&
|
||||||
|
(strcmp(argv[2], "-in") == 0) )
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-mod-info --dump");
|
||||||
|
pdfDumpInfoDict(argv[3]);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* fl_in = 0;
|
||||||
|
char* fl_out = 0;
|
||||||
|
char* cur_key = 0;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; ++i)
|
||||||
|
{
|
||||||
|
if ((! strcmp(argv[i], "-in")) && (++i < argc))
|
||||||
|
{
|
||||||
|
fl_in = argv[i];
|
||||||
|
}
|
||||||
|
else if ((! strcmp(argv[i], "-out")) && (++i < argc))
|
||||||
|
{
|
||||||
|
fl_out = argv[i];
|
||||||
|
}
|
||||||
|
else if (! strcmp(argv[i], "--static-id")) // don't document
|
||||||
|
{
|
||||||
|
static_id = true; // this should be used in test suites only
|
||||||
|
}
|
||||||
|
else if ((! strcmp(argv[i], "-key")) && (++i < argc))
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-mod-info -key");
|
||||||
|
cur_key = argv[i];
|
||||||
|
Keys[cur_key] = "";
|
||||||
|
}
|
||||||
|
else if ((! strcmp(argv[i], "-val")) && (++i < argc))
|
||||||
|
{
|
||||||
|
if (cur_key == 0)
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-mod-info usage wrong val");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
QTC::TC("examples", "pdf-mod-info -val");
|
||||||
|
Keys[cur_key] = argv[i];
|
||||||
|
cur_key = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-mod-info usage junk");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! fl_in)
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-mod-info no in file");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
if (! fl_out)
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-mod-info in-place");
|
||||||
|
fl_out = fl_in;
|
||||||
|
}
|
||||||
|
if (Keys.size() == 0)
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-mod-info no keys");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QPDF file;
|
||||||
|
file.processFile(fl_in);
|
||||||
|
|
||||||
|
QPDFObjectHandle filetrailer = file.getTrailer();
|
||||||
|
QPDFObjectHandle fileinfo;
|
||||||
|
|
||||||
|
for (std::map<std::string, std::string>::const_iterator it =
|
||||||
|
Keys.begin(); Keys.end() != it; ++it)
|
||||||
|
{
|
||||||
|
if (! fileinfo.isInitialized())
|
||||||
|
{
|
||||||
|
if (filetrailer.hasKey("/Info"))
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-mod-info has info");
|
||||||
|
fileinfo = filetrailer.getKey("/Info");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QTC::TC("examples", "pdf-mod-info file no info");
|
||||||
|
std::map<std::string, QPDFObjectHandle> vacant;
|
||||||
|
fileinfo = fileinfo.newDictionary(vacant);
|
||||||
|
filetrailer.replaceKey("/Info", fileinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (it->second == "")
|
||||||
|
{
|
||||||
|
fileinfo.removeKey(it->first);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QPDFObjectHandle elt = fileinfo.newString(it->second);
|
||||||
|
elt.makeDirect();
|
||||||
|
fileinfo.replaceKey(it->first, elt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string fl_tmp = fl_out;
|
||||||
|
fl_tmp += ".tmp";
|
||||||
|
QPDFWriter w(file, fl_tmp.c_str());
|
||||||
|
w.setStreamDataMode(QPDFWriter::s_preserve);
|
||||||
|
w.setLinearization(true);
|
||||||
|
w.setStaticID(static_id);
|
||||||
|
w.write();
|
||||||
|
QUtil::os_wrapper("rename " + fl_tmp + " " + std::string(fl_out),
|
||||||
|
rename(fl_tmp.c_str(), fl_out));
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
61
examples/pdf-npages.cc
Normal file
61
examples/pdf-npages.cc
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <qpdf/QPDF.hh>
|
||||||
|
|
||||||
|
static char const* whoami = 0;
|
||||||
|
|
||||||
|
void usage()
|
||||||
|
{
|
||||||
|
std::cerr << "Usage: " << whoami << " filename" << std::endl
|
||||||
|
<< "Prints the number of pages in filename" << std::endl;
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
if ((whoami = strrchr(argv[0], '/')) == NULL)
|
||||||
|
{
|
||||||
|
whoami = argv[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++whoami;
|
||||||
|
}
|
||||||
|
// For libtool's sake....
|
||||||
|
if (strncmp(whoami, "lt-", 3) == 0)
|
||||||
|
{
|
||||||
|
whoami += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((argc == 2) && (strcmp(argv[1], "--version") == 0))
|
||||||
|
{
|
||||||
|
std::cout << whoami << " version 1.3" << std::endl;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
char const* filename = argv[1];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QPDF pdf;
|
||||||
|
pdf.processFile(filename);
|
||||||
|
QPDFObjectHandle root = pdf.getRoot();
|
||||||
|
QPDFObjectHandle pages = root.getKey("/Pages");
|
||||||
|
QPDFObjectHandle count = pages.getKey("/Count");
|
||||||
|
std::cout << count.getIntValue() << std::endl;
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << whoami << ": " << e.what() << std::endl;
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
49
examples/qtest/bookmarks.test
Normal file
49
examples/qtest/bookmarks.test
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
require 5.008;
|
||||||
|
BEGIN { $^W = 1; }
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
chdir("bookmarks");
|
||||||
|
|
||||||
|
require TestDriver;
|
||||||
|
|
||||||
|
my $td = new TestDriver('pdf-bookmarks');
|
||||||
|
|
||||||
|
foreach my $show ("", " -show-open")
|
||||||
|
{
|
||||||
|
foreach my $style ("", " -lines", " -numbers")
|
||||||
|
{
|
||||||
|
my $out = "test.$show.$style.out";
|
||||||
|
$out =~ s/ //g;
|
||||||
|
$td->runtest("show:$show, style:$style",
|
||||||
|
{$td->COMMAND => "pdf-bookmarks $show $style 1.pdf"},
|
||||||
|
{$td->FILE => $out, $td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$td->runtest("no bookmarks",
|
||||||
|
{$td->COMMAND => "pdf-bookmarks 2.pdf"},
|
||||||
|
{$td->STRING => "2.pdf has no bookmarks\n",
|
||||||
|
$td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
|
||||||
|
$td->runtest("bad",
|
||||||
|
{$td->COMMAND => "pdf-bookmarks 3.pdf"},
|
||||||
|
{$td->STRING => "pdf-bookmarks processing file 3.pdf: " .
|
||||||
|
"3.pdf: offset 0: not a PDF file\n",
|
||||||
|
$td->EXIT_STATUS => 2},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
|
||||||
|
$td->runtest("encrypted, targets",
|
||||||
|
{$td->COMMAND => "pdf-bookmarks -show-targets 4.pdf user"},
|
||||||
|
{$td->FILE => "encrypted.out",
|
||||||
|
$td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
|
||||||
|
$td->runtest("bookmarks deleted",
|
||||||
|
{$td->COMMAND => "pdf-bookmarks 5.pdf user"},
|
||||||
|
{$td->STRING => "5.pdf has no bookmarks\n",
|
||||||
|
$td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
|
||||||
|
$td->report(10);
|
1502
examples/qtest/bookmarks/1.pdf
Normal file
1502
examples/qtest/bookmarks/1.pdf
Normal file
File diff suppressed because it is too large
Load Diff
79
examples/qtest/bookmarks/2.pdf
Normal file
79
examples/qtest/bookmarks/2.pdf
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
%PDF-1.3
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Catalog
|
||||||
|
/Pages 2 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Pages
|
||||||
|
/Kids [
|
||||||
|
3 0 R
|
||||||
|
]
|
||||||
|
/Count 1
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Page
|
||||||
|
/Parent 2 0 R
|
||||||
|
/MediaBox [0 0 612 792]
|
||||||
|
/Contents 4 0 R
|
||||||
|
/Resources <<
|
||||||
|
/ProcSet 5 0 R
|
||||||
|
/Font <<
|
||||||
|
/F1 6 0 R
|
||||||
|
>>
|
||||||
|
>>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/Length 44
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
BT
|
||||||
|
/F1 24 Tf
|
||||||
|
72 720 Td
|
||||||
|
(Potato) Tj
|
||||||
|
ET
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
[
|
||||||
|
/PDF
|
||||||
|
/Text
|
||||||
|
]
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Font
|
||||||
|
/Subtype /Type1
|
||||||
|
/Name /F1
|
||||||
|
/BaseFont /Helvetica
|
||||||
|
/Encoding /WinAnsiEncoding
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000009 00000 n
|
||||||
|
0000000063 00000 n
|
||||||
|
0000000135 00000 n
|
||||||
|
0000000307 00000 n
|
||||||
|
0000000403 00000 n
|
||||||
|
0000000438 00000 n
|
||||||
|
trailer <<
|
||||||
|
/Size 7
|
||||||
|
/Root 1 0 R
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
556
|
||||||
|
%%EOF
|
1
examples/qtest/bookmarks/3.pdf
Normal file
1
examples/qtest/bookmarks/3.pdf
Normal file
@ -0,0 +1 @@
|
|||||||
|
potato salad
|
BIN
examples/qtest/bookmarks/4.pdf
Normal file
BIN
examples/qtest/bookmarks/4.pdf
Normal file
Binary file not shown.
1573
examples/qtest/bookmarks/5.pdf
Normal file
1573
examples/qtest/bookmarks/5.pdf
Normal file
File diff suppressed because it is too large
Load Diff
11
examples/qtest/bookmarks/encrypted.out
Normal file
11
examples/qtest/bookmarks/encrypted.out
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[ -> 6 ] Isís 1 -> 5: /XYZ null null null
|
||||||
|
[ -> 12 ] Amanda 1.1 -> 11: /Fit
|
||||||
|
[ -> 13 ] Isosicle 1.1.1 -> 12: /FitV 100
|
||||||
|
[ -> 19 ] Isosicle 1.1.1.1 -> 18: /XYZ null null null
|
||||||
|
[ -> 20 ] Isosicle 1.1.1.2 -> 19: /XYZ null null null
|
||||||
|
[ -> 13 ] Isosicle 1.1.2 -> 12: /XYZ null null null
|
||||||
|
[ -> 23 ] Isosicle 1.1.2.1 -> 22: /XYZ null null null
|
||||||
|
[ -> 14 ] Sandy ÷Σανδι÷ 1.2 -> 13: /FitH 792
|
||||||
|
[ -> 2 ] Trepsichord 1.2.1 -> 1: /FitR 66 714 180 770
|
||||||
|
[ -> 1 ] Trepsicle 1.2.2 -> 0: /XYZ null null null
|
||||||
|
[ -> 16 ] Trepak 2 -> 15: /XYZ 66 756 3
|
22
examples/qtest/bookmarks/test.-show-open.-lines.out
Normal file
22
examples/qtest/bookmarks/test.-show-open.-lines.out
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
|
||||||
|
+-+ ( ) Trepak 2 -> 15: /XYZ 66 756 3
|
||||||
|
|
|
||||||
|
+-+ (v) Isis 1 -> 5: /XYZ null null null
|
||||||
|
|
|
||||||
|
+-+ (>) Amanda 1.1 -> 11: /Fit
|
||||||
|
| |
|
||||||
|
| +-+ (>) Isosicle 1.1.1 -> 12: /FitV 100
|
||||||
|
| | |
|
||||||
|
| | +-+ ( ) Isosicle 1.1.1.1 -> 18: /XYZ null null null
|
||||||
|
| | |
|
||||||
|
| | +-+ ( ) Isosicle 1.1.1.2 -> 19: /XYZ null null null
|
||||||
|
| |
|
||||||
|
| +-+ (v) Isosicle 1.1.2 -> 12: /XYZ null null null
|
||||||
|
| |
|
||||||
|
| +-+ ( ) Isosicle 1.1.2.1 -> 22: /XYZ null null null
|
||||||
|
|
|
||||||
|
+-+ (v) Sandy 1.2 -> 13: /FitH 792
|
||||||
|
|
|
||||||
|
+-+ ( ) Trepsichord 1.2.1 -> 1: /FitR 66 714 180 770
|
||||||
|
|
|
||||||
|
+-+ ( ) Trepsicle 1.2.2 -> 0: /XYZ null null null
|
11
examples/qtest/bookmarks/test.-show-open.-numbers.out
Normal file
11
examples/qtest/bookmarks/test.-show-open.-numbers.out
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
1. ( ) Trepak 2 -> 15: /XYZ 66 756 3
|
||||||
|
2. (v) Isis 1 -> 5: /XYZ null null null
|
||||||
|
2.1. (>) Amanda 1.1 -> 11: /Fit
|
||||||
|
2.1.1. (>) Isosicle 1.1.1 -> 12: /FitV 100
|
||||||
|
2.1.1.1. ( ) Isosicle 1.1.1.1 -> 18: /XYZ null null null
|
||||||
|
2.1.1.2. ( ) Isosicle 1.1.1.2 -> 19: /XYZ null null null
|
||||||
|
2.1.2. (v) Isosicle 1.1.2 -> 12: /XYZ null null null
|
||||||
|
2.1.2.1. ( ) Isosicle 1.1.2.1 -> 22: /XYZ null null null
|
||||||
|
2.2. (v) Sandy 1.2 -> 13: /FitH 792
|
||||||
|
2.2.1. ( ) Trepsichord 1.2.1 -> 1: /FitR 66 714 180 770
|
||||||
|
2.2.2. ( ) Trepsicle 1.2.2 -> 0: /XYZ null null null
|
11
examples/qtest/bookmarks/test.-show-open..out
Normal file
11
examples/qtest/bookmarks/test.-show-open..out
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
( ) Trepak 2 -> 15: /XYZ 66 756 3
|
||||||
|
(v) Isis 1 -> 5: /XYZ null null null
|
||||||
|
(>) Amanda 1.1 -> 11: /Fit
|
||||||
|
(>) Isosicle 1.1.1 -> 12: /FitV 100
|
||||||
|
( ) Isosicle 1.1.1.1 -> 18: /XYZ null null null
|
||||||
|
( ) Isosicle 1.1.1.2 -> 19: /XYZ null null null
|
||||||
|
(v) Isosicle 1.1.2 -> 12: /XYZ null null null
|
||||||
|
( ) Isosicle 1.1.2.1 -> 22: /XYZ null null null
|
||||||
|
(v) Sandy 1.2 -> 13: /FitH 792
|
||||||
|
( ) Trepsichord 1.2.1 -> 1: /FitR 66 714 180 770
|
||||||
|
( ) Trepsicle 1.2.2 -> 0: /XYZ null null null
|
22
examples/qtest/bookmarks/test..-lines.out
Normal file
22
examples/qtest/bookmarks/test..-lines.out
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
|
||||||
|
+-+ Trepak 2 -> 15: /XYZ 66 756 3
|
||||||
|
|
|
||||||
|
+-+ Isis 1 -> 5: /XYZ null null null
|
||||||
|
|
|
||||||
|
+-+ Amanda 1.1 -> 11: /Fit
|
||||||
|
| |
|
||||||
|
| +-+ Isosicle 1.1.1 -> 12: /FitV 100
|
||||||
|
| | |
|
||||||
|
| | +-+ Isosicle 1.1.1.1 -> 18: /XYZ null null null
|
||||||
|
| | |
|
||||||
|
| | +-+ Isosicle 1.1.1.2 -> 19: /XYZ null null null
|
||||||
|
| |
|
||||||
|
| +-+ Isosicle 1.1.2 -> 12: /XYZ null null null
|
||||||
|
| |
|
||||||
|
| +-+ Isosicle 1.1.2.1 -> 22: /XYZ null null null
|
||||||
|
|
|
||||||
|
+-+ Sandy 1.2 -> 13: /FitH 792
|
||||||
|
|
|
||||||
|
+-+ Trepsichord 1.2.1 -> 1: /FitR 66 714 180 770
|
||||||
|
|
|
||||||
|
+-+ Trepsicle 1.2.2 -> 0: /XYZ null null null
|
11
examples/qtest/bookmarks/test..-numbers.out
Normal file
11
examples/qtest/bookmarks/test..-numbers.out
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
1. Trepak 2 -> 15: /XYZ 66 756 3
|
||||||
|
2. Isis 1 -> 5: /XYZ null null null
|
||||||
|
2.1. Amanda 1.1 -> 11: /Fit
|
||||||
|
2.1.1. Isosicle 1.1.1 -> 12: /FitV 100
|
||||||
|
2.1.1.1. Isosicle 1.1.1.1 -> 18: /XYZ null null null
|
||||||
|
2.1.1.2. Isosicle 1.1.1.2 -> 19: /XYZ null null null
|
||||||
|
2.1.2. Isosicle 1.1.2 -> 12: /XYZ null null null
|
||||||
|
2.1.2.1. Isosicle 1.1.2.1 -> 22: /XYZ null null null
|
||||||
|
2.2. Sandy 1.2 -> 13: /FitH 792
|
||||||
|
2.2.1. Trepsichord 1.2.1 -> 1: /FitR 66 714 180 770
|
||||||
|
2.2.2. Trepsicle 1.2.2 -> 0: /XYZ null null null
|
11
examples/qtest/bookmarks/test...out
Normal file
11
examples/qtest/bookmarks/test...out
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Trepak 2 -> 15: /XYZ 66 756 3
|
||||||
|
Isis 1 -> 5: /XYZ null null null
|
||||||
|
Amanda 1.1 -> 11: /Fit
|
||||||
|
Isosicle 1.1.1 -> 12: /FitV 100
|
||||||
|
Isosicle 1.1.1.1 -> 18: /XYZ null null null
|
||||||
|
Isosicle 1.1.1.2 -> 19: /XYZ null null null
|
||||||
|
Isosicle 1.1.2 -> 12: /XYZ null null null
|
||||||
|
Isosicle 1.1.2.1 -> 22: /XYZ null null null
|
||||||
|
Sandy 1.2 -> 13: /FitH 792
|
||||||
|
Trepsichord 1.2.1 -> 1: /FitR 66 714 180 770
|
||||||
|
Trepsicle 1.2.2 -> 0: /XYZ null null null
|
93
examples/qtest/mod-info.test
Normal file
93
examples/qtest/mod-info.test
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
require 5.008;
|
||||||
|
BEGIN { $^W = 1; }
|
||||||
|
use strict;
|
||||||
|
use File::Copy;
|
||||||
|
|
||||||
|
chdir("mod-info");
|
||||||
|
|
||||||
|
require TestDriver;
|
||||||
|
|
||||||
|
my $td = new TestDriver('pdf-mod-info');
|
||||||
|
|
||||||
|
my $prg = "pdf-mod-info";
|
||||||
|
my $qpdf = $ENV{'QPDF_BIN'} or die;
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
$td->runtest("usage #1",
|
||||||
|
{$td->COMMAND => "$prg -in target.pdf"},
|
||||||
|
{$td->FILE => "usage.out",
|
||||||
|
$td->EXIT_STATUS => 2});
|
||||||
|
|
||||||
|
$td->runtest("usage #2",
|
||||||
|
{$td->COMMAND => "$prg -key abc -val def"},
|
||||||
|
{$td->FILE => "usage.out",
|
||||||
|
$td->EXIT_STATUS => 2});
|
||||||
|
|
||||||
|
$td->runtest("usage #3",
|
||||||
|
{$td->COMMAND => "$prg -key abc -val def abc"},
|
||||||
|
{$td->FILE => "usage.out",
|
||||||
|
$td->EXIT_STATUS => 2});
|
||||||
|
|
||||||
|
$td->runtest("usage #4",
|
||||||
|
{$td->COMMAND => "$prg -in source1.pdf -key /date -val 01/01/01 -val 12/12/12"},
|
||||||
|
{$td->FILE => "usage.out",
|
||||||
|
$td->EXIT_STATUS => 2});
|
||||||
|
|
||||||
|
$td->runtest("dump #1",
|
||||||
|
{$td->COMMAND => "$prg --dump -in files/source1.pdf"},
|
||||||
|
{$td->FILE => "dump.out",
|
||||||
|
$td->EXIT_STATUS => 0});
|
||||||
|
|
||||||
|
$td->runtest("dump #2",
|
||||||
|
{$td->COMMAND => "$prg --dump -in files/no-info.pdf"},
|
||||||
|
{$td->STRING => "",
|
||||||
|
$td->EXIT_STATUS => 0});
|
||||||
|
|
||||||
|
$td->runtest("dump #3",
|
||||||
|
{$td->COMMAND => "$prg --dump -in files/empty-info.pdf"},
|
||||||
|
{$td->STRING => "",
|
||||||
|
$td->EXIT_STATUS => 0});
|
||||||
|
|
||||||
|
run_and_cmp("modify Subject",
|
||||||
|
"$prg -in files/source1.pdf -out out.pdf -key /Subject " .
|
||||||
|
"-val \"Export Business\"",
|
||||||
|
"", "out.pdf", "files/1.qdf");
|
||||||
|
|
||||||
|
run_and_cmp("add Subject, remove Producer, modify CreationDate",
|
||||||
|
"$prg -in files/source2.pdf -out out.pdf -key /Subject " .
|
||||||
|
"-val \"Tammlin\" -key /Producer -key /CreationDate -val 12/12",
|
||||||
|
"", "out.pdf", "files/2.qdf");
|
||||||
|
|
||||||
|
run_and_cmp("add Subject (empty-info file)",
|
||||||
|
"$prg -in files/empty-info.pdf -out out.pdf -key /Subject " .
|
||||||
|
"-val Tammlin",
|
||||||
|
"", "out.pdf", "files/3.qdf");
|
||||||
|
|
||||||
|
copy("files/no-info.pdf", "no-info.pdf") or die "can't copy no-info: $!";
|
||||||
|
run_and_cmp("in-place Producer added (no-info file)",
|
||||||
|
"$prg -in no-info.pdf -key /Producer -val \"Obivan Kinobi\"",
|
||||||
|
"", "no-info.pdf", "files/4.qdf");
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
$td->report(15);
|
||||||
|
|
||||||
|
sub cleanup
|
||||||
|
{
|
||||||
|
unlink (<*.pdf>);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub run_and_cmp
|
||||||
|
{
|
||||||
|
my ($dsc, $cmd, $out, $fout, $fexp) = @_;
|
||||||
|
$td->runtest($dsc,
|
||||||
|
{$td->COMMAND => "$cmd --static-id"},
|
||||||
|
{$td->STRING => $out,
|
||||||
|
$td->EXIT_STATUS => 0});
|
||||||
|
$td->runtest("$dsc output",
|
||||||
|
{$td->COMMAND => "$qpdf --static-id -qdf $fout -"},
|
||||||
|
{$td->FILE => $fexp,
|
||||||
|
$td->EXIT_STATUS => 0});
|
||||||
|
}
|
11
examples/qtest/mod-info/dump.out
Normal file
11
examples/qtest/mod-info/dump.out
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Author: Yours Truly
|
||||||
|
ContentTemperature: 100F
|
||||||
|
CreationDate: D:20040212104653-05'00'
|
||||||
|
Creator: Adobe Acrobat 6.0
|
||||||
|
FormerlyKnownAs: target/branch/leaf/leaf.pdf
|
||||||
|
Keywords: 40, 128, public, encryption, ignition, primarily prime
|
||||||
|
ModDate: D:20040212112832-05'00'
|
||||||
|
Producer: Adobe Acrobat 6.0 Image Conversion Plug-in
|
||||||
|
Subject: Of The Matter
|
||||||
|
Title: My New Car Title
|
||||||
|
VeryImportantNote: pordofor stands for portable document format
|
BIN
examples/qtest/mod-info/files/1.qdf
Normal file
BIN
examples/qtest/mod-info/files/1.qdf
Normal file
Binary file not shown.
1338
examples/qtest/mod-info/files/2.qdf
Normal file
1338
examples/qtest/mod-info/files/2.qdf
Normal file
File diff suppressed because it is too large
Load Diff
BIN
examples/qtest/mod-info/files/3.qdf
Normal file
BIN
examples/qtest/mod-info/files/3.qdf
Normal file
Binary file not shown.
BIN
examples/qtest/mod-info/files/4.qdf
Normal file
BIN
examples/qtest/mod-info/files/4.qdf
Normal file
Binary file not shown.
BIN
examples/qtest/mod-info/files/empty-info.pdf
Normal file
BIN
examples/qtest/mod-info/files/empty-info.pdf
Normal file
Binary file not shown.
BIN
examples/qtest/mod-info/files/no-info.pdf
Normal file
BIN
examples/qtest/mod-info/files/no-info.pdf
Normal file
Binary file not shown.
BIN
examples/qtest/mod-info/files/source1.pdf
Normal file
BIN
examples/qtest/mod-info/files/source1.pdf
Normal file
Binary file not shown.
BIN
examples/qtest/mod-info/files/source2.pdf
Normal file
BIN
examples/qtest/mod-info/files/source2.pdf
Normal file
Binary file not shown.
5
examples/qtest/mod-info/usage.out
Normal file
5
examples/qtest/mod-info/usage.out
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Usage: pdf-mod-info -in in_file [-out out_file] [-key key [-val val]?]+
|
||||||
|
Modifies/Adds/Removes PDF /Info entries in the in_file
|
||||||
|
and stores the result in out_file
|
||||||
|
Special mode: pdf-mod-info --dump file
|
||||||
|
dumps all /Info entries to stdout
|
23
examples/qtest/npages.test
Normal file
23
examples/qtest/npages.test
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
require 5.008;
|
||||||
|
BEGIN { $^W = 1; }
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
chdir("npages");
|
||||||
|
|
||||||
|
require TestDriver;
|
||||||
|
|
||||||
|
my $td = new TestDriver('pdf-npages');
|
||||||
|
|
||||||
|
$td->runtest("normal",
|
||||||
|
{$td->COMMAND => "pdf-npages minimal.pdf"},
|
||||||
|
{$td->STRING => "1\n", $td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
|
||||||
|
$td->runtest("error",
|
||||||
|
{$td->COMMAND => "pdf-npages bad"},
|
||||||
|
{$td->STRING => "pdf-npages: bad: offset 0: not a PDF file\n",
|
||||||
|
$td->EXIT_STATUS => 2},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
|
||||||
|
$td->report(2);
|
1
examples/qtest/npages/bad
Normal file
1
examples/qtest/npages/bad
Normal file
@ -0,0 +1 @@
|
|||||||
|
test
|
79
examples/qtest/npages/minimal.pdf
Normal file
79
examples/qtest/npages/minimal.pdf
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
%PDF-1.3
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Catalog
|
||||||
|
/Pages 2 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Pages
|
||||||
|
/Kids [
|
||||||
|
3 0 R
|
||||||
|
]
|
||||||
|
/Count 1
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Page
|
||||||
|
/Parent 2 0 R
|
||||||
|
/MediaBox [0 0 612 792]
|
||||||
|
/Contents 4 0 R
|
||||||
|
/Resources <<
|
||||||
|
/ProcSet 5 0 R
|
||||||
|
/Font <<
|
||||||
|
/F1 6 0 R
|
||||||
|
>>
|
||||||
|
>>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/Length 44
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
BT
|
||||||
|
/F1 24 Tf
|
||||||
|
72 720 Td
|
||||||
|
(Potato) Tj
|
||||||
|
ET
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
[
|
||||||
|
/PDF
|
||||||
|
/Text
|
||||||
|
]
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Font
|
||||||
|
/Subtype /Type1
|
||||||
|
/Name /F1
|
||||||
|
/BaseFont /Helvetica
|
||||||
|
/Encoding /WinAnsiEncoding
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000009 00000 n
|
||||||
|
0000000063 00000 n
|
||||||
|
0000000135 00000 n
|
||||||
|
0000000307 00000 n
|
||||||
|
0000000403 00000 n
|
||||||
|
0000000438 00000 n
|
||||||
|
trailer <<
|
||||||
|
/Size 7
|
||||||
|
/Root 1 0 R
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
556
|
||||||
|
%%EOF
|
32
include/qpdf/Buffer.hh
Normal file
32
include/qpdf/Buffer.hh
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __BUFFER_HH__
|
||||||
|
#define __BUFFER_HH__
|
||||||
|
|
||||||
|
class Buffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Buffer();
|
||||||
|
Buffer(unsigned long size);
|
||||||
|
Buffer(Buffer const&);
|
||||||
|
Buffer& operator=(Buffer const&);
|
||||||
|
~Buffer();
|
||||||
|
unsigned long getSize() const;
|
||||||
|
unsigned char const* getBuffer() const;
|
||||||
|
unsigned char* getBuffer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init(unsigned long size);
|
||||||
|
void copy(Buffer const&);
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
unsigned long size;
|
||||||
|
unsigned char* buf;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __BUFFER_HH__
|
73
include/qpdf/Pipeline.hh
Normal file
73
include/qpdf/Pipeline.hh
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
// Generalized Pipeline interface. By convention, subclasses of
|
||||||
|
// Pipeline are called Pl_Something.
|
||||||
|
//
|
||||||
|
// When an instance of Pipeline is created with a pointer to a next
|
||||||
|
// pipeline, that pipeline writes its data to the next one when it
|
||||||
|
// finishes with it. In order to make possible a usage style in which
|
||||||
|
// a pipeline may be passed to a function which may stick other
|
||||||
|
// pipelines in front of it, the allocator of a pipeline is
|
||||||
|
// responsible for its destruction. In other words, one pipeline
|
||||||
|
// object does not attempt to manage the memory of its successor.
|
||||||
|
//
|
||||||
|
// The client is required to call finish() before destroying a
|
||||||
|
// Pipeline in order to avoid loss of data. A Pipeline class should
|
||||||
|
// not throw an exception in the destructor if this hasn't been done
|
||||||
|
// though since doing so causes too mcuh trouble when deleting
|
||||||
|
// pipelines during error conditions.
|
||||||
|
//
|
||||||
|
// Some pipelines are resuable (i.e., you can call write() after
|
||||||
|
// calling finish() and can call finish() multiple times) while others
|
||||||
|
// are not. It is up to the caller to use a pipeline according to its
|
||||||
|
// own restrictions.
|
||||||
|
|
||||||
|
#ifndef __PIPELINE_HH__
|
||||||
|
#define __PIPELINE_HH__
|
||||||
|
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
|
||||||
|
class Pipeline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class Exception: public QEXC::General
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Exception(std::string const& message) :
|
||||||
|
QEXC::General(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Exception() throw()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Pipeline(char const* identifier, Pipeline* next);
|
||||||
|
|
||||||
|
virtual ~Pipeline();
|
||||||
|
|
||||||
|
// Subclasses should implement write and finish to do their jobs
|
||||||
|
// and then, if they are not end-of-line pipelines, call
|
||||||
|
// getNext()->write or getNext()->finish.
|
||||||
|
virtual void write(unsigned char* data, int len) = 0;
|
||||||
|
virtual void finish() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Pipeline* getNext(bool allow_null = false);
|
||||||
|
std::string identifier;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Do not implement copy or assign
|
||||||
|
Pipeline(Pipeline const&);
|
||||||
|
Pipeline& operator=(Pipeline const&);
|
||||||
|
|
||||||
|
Pipeline* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __PIPELINE_HH__
|
46
include/qpdf/Pl_Buffer.hh
Normal file
46
include/qpdf/Pl_Buffer.hh
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __PL_BUFFER_HH__
|
||||||
|
#define __PL_BUFFER_HH__
|
||||||
|
|
||||||
|
// This pipeline accumulates the data passed to it into a memory
|
||||||
|
// buffer. Each subsequent use of this buffer appends to the data
|
||||||
|
// accumulated so far. getBuffer() may be called only after calling
|
||||||
|
// finish() and before calling any subsequent write(). At that point,
|
||||||
|
// a dynamically allocated Buffer object is returned and the internal
|
||||||
|
// buffer is reset. The caller is responseible for deleting the
|
||||||
|
// returned Buffer.
|
||||||
|
//
|
||||||
|
// For this pipeline, "next" may be null. If a next pointer is
|
||||||
|
// provided, this pipeline will also pass the data through to it.
|
||||||
|
|
||||||
|
#include <qpdf/Pipeline.hh>
|
||||||
|
#include <qpdf/PointerHolder.hh>
|
||||||
|
#include <qpdf/Buffer.hh>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
class Pl_Buffer: public Pipeline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pl_Buffer(char const* identifier, Pipeline* next = 0);
|
||||||
|
virtual ~Pl_Buffer();
|
||||||
|
virtual void write(unsigned char*, int);
|
||||||
|
virtual void finish();
|
||||||
|
|
||||||
|
// Each call to getBuffer() resets this object -- see notes above.
|
||||||
|
// The caller is responsible for deleting the returned Buffer
|
||||||
|
// object.
|
||||||
|
Buffer* getBuffer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ready;
|
||||||
|
std::list<PointerHolder<Buffer> > data;
|
||||||
|
size_t total_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __PL_BUFFER_HH__
|
34
include/qpdf/Pl_Count.hh
Normal file
34
include/qpdf/Pl_Count.hh
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __PL_COUNT_HH__
|
||||||
|
#define __PL_COUNT_HH__
|
||||||
|
|
||||||
|
// This pipeline is reusable; i.e., it is safe to call write() after
|
||||||
|
// calling finish().
|
||||||
|
|
||||||
|
#include <qpdf/Pipeline.hh>
|
||||||
|
|
||||||
|
class Pl_Count: public Pipeline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pl_Count(char const* identifier, Pipeline* next);
|
||||||
|
virtual ~Pl_Count();
|
||||||
|
virtual void write(unsigned char*, int);
|
||||||
|
virtual void finish();
|
||||||
|
// Returns the number of bytes written
|
||||||
|
int getCount() const;
|
||||||
|
// Returns the last character written, or '\0' if no characters
|
||||||
|
// have been written (in which case getCount() returns 0)
|
||||||
|
unsigned char getLastChar() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int count;
|
||||||
|
unsigned char last_char;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __PL_COUNT_HH__
|
28
include/qpdf/Pl_Discard.hh
Normal file
28
include/qpdf/Pl_Discard.hh
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __PL_DISCARD_HH__
|
||||||
|
#define __PL_DISCARD_HH__
|
||||||
|
|
||||||
|
// This pipeline discards its output. It is an end-of-line pipeline
|
||||||
|
// (with no next).
|
||||||
|
|
||||||
|
// This pipeline is reusable; i.e., it is safe to call write() after
|
||||||
|
// calling finish().
|
||||||
|
|
||||||
|
#include <qpdf/Pipeline.hh>
|
||||||
|
|
||||||
|
class Pl_Discard: public Pipeline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pl_Discard();
|
||||||
|
virtual ~Pl_Discard();
|
||||||
|
virtual void write(unsigned char*, int);
|
||||||
|
virtual void finish();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __PL_DISCARD_HH__
|
53
include/qpdf/Pl_Flate.hh
Normal file
53
include/qpdf/Pl_Flate.hh
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __PL_FLATE_HH__
|
||||||
|
#define __PL_FLATE_HH__
|
||||||
|
|
||||||
|
#include <qpdf/Pipeline.hh>
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
class Pl_Flate: public Pipeline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class Exception: public Pipeline::Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Exception(std::string const& message) :
|
||||||
|
Pipeline::Exception(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Exception() throw ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int const def_bufsize = 65536;
|
||||||
|
|
||||||
|
enum action_e { a_inflate, a_deflate };
|
||||||
|
|
||||||
|
Pl_Flate(char const* identifier, Pipeline* next,
|
||||||
|
action_e action, int out_bufsize = def_bufsize);
|
||||||
|
virtual ~Pl_Flate();
|
||||||
|
|
||||||
|
virtual void write(unsigned char* data, int len);
|
||||||
|
virtual void finish();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleData(unsigned char* data, int len, int flush);
|
||||||
|
void checkError(char const* prefix, int error_code);
|
||||||
|
|
||||||
|
unsigned char* outbuf;
|
||||||
|
int out_bufsize;
|
||||||
|
action_e action;
|
||||||
|
bool initialized;
|
||||||
|
z_stream zstream;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __PL_FLATE_HH__
|
49
include/qpdf/Pl_StdioFile.hh
Normal file
49
include/qpdf/Pl_StdioFile.hh
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
// End-of-line pipeline that simply writes its data to a stdio FILE* object.
|
||||||
|
|
||||||
|
#ifndef __PL_STDIOFILE_HH__
|
||||||
|
#define __PL_STDIOFILE_HH__
|
||||||
|
|
||||||
|
#include <qpdf/Pipeline.hh>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// This pipeline is reusable.
|
||||||
|
//
|
||||||
|
|
||||||
|
class Pl_StdioFile: public Pipeline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class Exception: public Pipeline::Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Exception(std::string const& message) :
|
||||||
|
Pipeline::Exception(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Exception() throw ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// f is externally maintained; this class just writes to and
|
||||||
|
// flushes it. It does not close it.
|
||||||
|
Pl_StdioFile(char const* identifier, FILE* f);
|
||||||
|
virtual ~Pl_StdioFile();
|
||||||
|
|
||||||
|
virtual void write(unsigned char* buf, int len);
|
||||||
|
virtual void finish();
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE* file;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __PL_STDIOFILE_HH__
|
170
include/qpdf/PointerHolder.hh
Normal file
170
include/qpdf/PointerHolder.hh
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __POINTERHOLDER_HH__
|
||||||
|
#define __POINTERHOLDER_HH__
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// This class is basically boost::shared_pointer but predates that by
|
||||||
|
// several years.
|
||||||
|
|
||||||
|
// This class expects to be initialized with a dynamically allocated
|
||||||
|
// object pointer. It keeps a reference count and deletes this once
|
||||||
|
// the reference count goes to zero. PointerHolder objects are
|
||||||
|
// explicitly safe for use in STL containers.
|
||||||
|
|
||||||
|
// It is very important that a client who pulls the pointer out of
|
||||||
|
// this holder does not let the holder go out of scope until it is
|
||||||
|
// finished with the pointer. It is also important that exactly one
|
||||||
|
// instance of this object ever gets initialized with a given pointer.
|
||||||
|
// Otherwise, the pointer will be deleted twice, and before that, some
|
||||||
|
// objects will be left with a pointer to a deleted object. In other
|
||||||
|
// words, the only legitimate way for two PointerHolder objects to
|
||||||
|
// contain the same pointer is for one to be a copy of the other.
|
||||||
|
// Copy and assignment semantics are well-defined and essentially
|
||||||
|
// allow you to use PointerHolder as a means to get pass-by-reference
|
||||||
|
// semantics in a pass-by-value environment without having to worry
|
||||||
|
// about memory management details.
|
||||||
|
|
||||||
|
// Comparison (== and <) are defined and operate on the internally
|
||||||
|
// stored pointers, not on the data. This makes it possible to store
|
||||||
|
// PointerHolder objects in sorted lists or to find them in STL
|
||||||
|
// containers just as one would be able to store pointers. Comparing
|
||||||
|
// the underlying pointers provides a well-defined, if not
|
||||||
|
// particularly meaningful, ordering.
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class PointerHolder
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
class Data
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Data(T* pointer, bool tracing) :
|
||||||
|
pointer(pointer),
|
||||||
|
tracing(tracing),
|
||||||
|
refcount(0)
|
||||||
|
{
|
||||||
|
static int next_id = 0;
|
||||||
|
this->unique_id = ++next_id;
|
||||||
|
}
|
||||||
|
~Data()
|
||||||
|
{
|
||||||
|
if (this->tracing)
|
||||||
|
{
|
||||||
|
std::cerr << "PointerHolder deleting pointer "
|
||||||
|
<< (void*)pointer
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
delete this->pointer;
|
||||||
|
if (this->tracing)
|
||||||
|
{
|
||||||
|
std::cerr << "PointerHolder done deleting pointer "
|
||||||
|
<< (void*)pointer
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
T* pointer;
|
||||||
|
bool tracing;
|
||||||
|
int refcount;
|
||||||
|
int unique_id;
|
||||||
|
private:
|
||||||
|
Data(Data const&);
|
||||||
|
Data& operator=(Data const&);
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
PointerHolder(T* pointer = 0, bool tracing = false)
|
||||||
|
{
|
||||||
|
this->init(new Data(pointer, tracing));
|
||||||
|
}
|
||||||
|
PointerHolder(PointerHolder const& rhs)
|
||||||
|
{
|
||||||
|
this->copy(rhs);
|
||||||
|
}
|
||||||
|
PointerHolder& operator=(PointerHolder const& rhs)
|
||||||
|
{
|
||||||
|
if (this != &rhs)
|
||||||
|
{
|
||||||
|
this->destroy();
|
||||||
|
this->copy(rhs);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
~PointerHolder()
|
||||||
|
{
|
||||||
|
this->destroy();
|
||||||
|
}
|
||||||
|
bool operator==(PointerHolder const& rhs) const
|
||||||
|
{
|
||||||
|
return this->data->pointer == rhs.data->pointer;
|
||||||
|
}
|
||||||
|
bool operator<(PointerHolder const& rhs) const
|
||||||
|
{
|
||||||
|
return this->data->pointer < rhs.data->pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: The pointer returned by getPointer turns into a pumpkin
|
||||||
|
// when the last PointerHolder that contains it disappears.
|
||||||
|
T* getPointer()
|
||||||
|
{
|
||||||
|
return this->data->pointer;
|
||||||
|
}
|
||||||
|
T const* getPointer() const
|
||||||
|
{
|
||||||
|
return this->data->pointer;
|
||||||
|
}
|
||||||
|
int getRefcount() const
|
||||||
|
{
|
||||||
|
return this->data->refcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init(Data* data)
|
||||||
|
{
|
||||||
|
this->data = data;
|
||||||
|
{
|
||||||
|
++this->data->refcount;
|
||||||
|
if (this->data->tracing)
|
||||||
|
{
|
||||||
|
std::cerr << "PointerHolder " << this->data->unique_id
|
||||||
|
<< " refcount increased to " << this->data->refcount
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void copy(PointerHolder const& rhs)
|
||||||
|
{
|
||||||
|
this->init(rhs.data);
|
||||||
|
}
|
||||||
|
void destroy()
|
||||||
|
{
|
||||||
|
bool gone = false;
|
||||||
|
{
|
||||||
|
if (--this->data->refcount == 0)
|
||||||
|
{
|
||||||
|
gone = true;
|
||||||
|
}
|
||||||
|
if (this->data->tracing)
|
||||||
|
{
|
||||||
|
std::cerr << "PointerHolder " << this->data->unique_id
|
||||||
|
<< " refcount decreased to "
|
||||||
|
<< this->data->refcount
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (gone)
|
||||||
|
{
|
||||||
|
delete this->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Data* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __POINTERHOLDER_HH__
|
119
include/qpdf/QEXC.hh
Normal file
119
include/qpdf/QEXC.hh
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __QEXC_HH__
|
||||||
|
#define __QEXC_HH__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <exception>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
namespace QEXC
|
||||||
|
{
|
||||||
|
// This namespace contains all exception classes used by the
|
||||||
|
// library.
|
||||||
|
|
||||||
|
// The class hierarchy is as follows:
|
||||||
|
|
||||||
|
// std::exception
|
||||||
|
// |
|
||||||
|
// +-> QEXC::Base
|
||||||
|
// |
|
||||||
|
// +-> QEXC::General
|
||||||
|
// |
|
||||||
|
// +-> QEXC::Internal
|
||||||
|
|
||||||
|
// QEXC::General is the base class of all standard user-defined
|
||||||
|
// exceptions and "expected" error conditions raised by QClass.
|
||||||
|
// Applications or libraries using QClass are encouraged to derive
|
||||||
|
// their own exceptions from these classes if they wish. It is
|
||||||
|
// entirely reasonable for code to catch QEXC::General or specific
|
||||||
|
// subclasses of it as part of normal error handling.
|
||||||
|
|
||||||
|
// QEXC::Internal is reserved for internal errors. These should
|
||||||
|
// be used only for situations that indicate a likely bug in the
|
||||||
|
// software itself. This may include improper use of a library
|
||||||
|
// function. Operator errors should not be able to cause Internal
|
||||||
|
// errors. (There may be some exceptions to this such as users
|
||||||
|
// invoking programs that were intended only to be invoked by
|
||||||
|
// other programs.) QEXC::Internal should generally not be
|
||||||
|
// trapped except in terminate handlers or top-level exception
|
||||||
|
// handlers which will want to translate them into error messages
|
||||||
|
// and cause the program to exit. Such top-level handlers may
|
||||||
|
// want to catch std::exception instead.
|
||||||
|
|
||||||
|
// All subclasses of QEXC::Base implement a const unparse() method
|
||||||
|
// which returns a std::string const&. They also override
|
||||||
|
// std::exception::what() to return a char* with the same value.
|
||||||
|
// unparse() should be implemented in such a way that a program
|
||||||
|
// catching QEXC::Base or std::exception can use the text returned
|
||||||
|
// by unparse() (or what()) without any exception-specific
|
||||||
|
// adornment. (The program may prefix the program name or other
|
||||||
|
// general information.) Note that std::exception::what() is a
|
||||||
|
// const method that returns a const char*. For this reason, it
|
||||||
|
// is essential that unparse() return a const reference to a
|
||||||
|
// string so that what() can be implemented by calling unparse().
|
||||||
|
// This means that the string that unparse() returns a reference
|
||||||
|
// to must not be allocated on the stack in the call to unparse().
|
||||||
|
// The recommended way to do this is for derived exception classes
|
||||||
|
// to store their string descriptions by calling the protected
|
||||||
|
// setMessage() method and then to not override unparse().
|
||||||
|
|
||||||
|
class Base: public std::exception
|
||||||
|
{
|
||||||
|
// This is the common base class for all exceptions in qclass.
|
||||||
|
// Application/library code should not generally catch this
|
||||||
|
// directly. See above for caveats.
|
||||||
|
public:
|
||||||
|
Base();
|
||||||
|
Base(std::string const& message);
|
||||||
|
virtual ~Base() throw() {}
|
||||||
|
virtual std::string const& unparse() const;
|
||||||
|
virtual const char* what() const throw();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setMessage(std::string const& message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class General: public Base
|
||||||
|
{
|
||||||
|
// This is the base class for normal user/library-defined
|
||||||
|
// error conditions.
|
||||||
|
public:
|
||||||
|
General();
|
||||||
|
General(std::string const& message);
|
||||||
|
virtual ~General() throw() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note that Internal is not derived from General. Internal
|
||||||
|
// errors are too severe. We don't want internal errors
|
||||||
|
// accidentally trapped as part of QEXC::General. If you are
|
||||||
|
// going to deal with internal errors, you have to do so
|
||||||
|
// explicitly.
|
||||||
|
class Internal: public Base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Internal(std::string const& message);
|
||||||
|
virtual ~Internal() throw() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
class System: public General
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
System(std::string const& prefix, int sys_errno);
|
||||||
|
virtual ~System() throw() {};
|
||||||
|
int getErrno() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int sys_errno;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __QEXC_HH__
|
750
include/qpdf/QPDF.hh
Normal file
750
include/qpdf/QPDF.hh
Normal file
@ -0,0 +1,750 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __QPDF_HH__
|
||||||
|
#define __QPDF_HH__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include <qpdf/QPDFXRefEntry.hh>
|
||||||
|
#include <qpdf/QPDFObjectHandle.hh>
|
||||||
|
#include <qpdf/QPDFTokenizer.hh>
|
||||||
|
#include <qpdf/Buffer.hh>
|
||||||
|
|
||||||
|
class QPDF_Stream;
|
||||||
|
class BitStream;
|
||||||
|
class BitWriter;
|
||||||
|
class QPDFExc;
|
||||||
|
|
||||||
|
class QPDF
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QPDF();
|
||||||
|
~QPDF();
|
||||||
|
|
||||||
|
// Associate a file with a QPDF object and do initial parsing of
|
||||||
|
// the file. PDF objects are not read until they are needed. A
|
||||||
|
// QPDF object may be associated with only on file in its
|
||||||
|
// lifetime. This method must be called before any methods that
|
||||||
|
// potentially ask for information about the PDF file are called.
|
||||||
|
// Prior to calling this, the only methods that are allowed are
|
||||||
|
// those that set parameters.
|
||||||
|
void processFile(char const* filename, char const* password = "");
|
||||||
|
|
||||||
|
// Parameter settings
|
||||||
|
|
||||||
|
// If true, ignore any cross-reference streams in a hybrid file
|
||||||
|
// (one that contains both cross-reference streams and
|
||||||
|
// cross-reference tables). This can be useful for testing to
|
||||||
|
// ensure that a hybrid file would work with an older reader.
|
||||||
|
void setIgnoreXRefStreams(bool);
|
||||||
|
|
||||||
|
// By default, any warnings are issued to stderr as they are
|
||||||
|
// encountered. If this is called with a true value, reporitng of
|
||||||
|
// warnings is suppressed. You may still retrieve warnings by
|
||||||
|
// calling getWarnings.
|
||||||
|
void setSuppressWarnings(bool);
|
||||||
|
|
||||||
|
// By default, QPDF will try to recover if it finds certain types
|
||||||
|
// of errors in PDF files. If turned off, it will throw an
|
||||||
|
// exception on the first such problem it finds without attempting
|
||||||
|
// recovery.
|
||||||
|
void setAttemptRecovery(bool);
|
||||||
|
|
||||||
|
// Other public methods
|
||||||
|
|
||||||
|
// Return the list of warnings that have been issued so far and
|
||||||
|
// clear the list. This method may be called even if processFile
|
||||||
|
// throws an exception. Note that if setSuppressWarnings was not
|
||||||
|
// called or was called with a false value, any warnings retrieved
|
||||||
|
// here will have already been issued to stderr.
|
||||||
|
std::vector<std::string> getWarnings();
|
||||||
|
|
||||||
|
std::string getFilename() const;
|
||||||
|
std::string getPDFVersion() const;
|
||||||
|
QPDFObjectHandle getTrailer();
|
||||||
|
QPDFObjectHandle getRoot();
|
||||||
|
|
||||||
|
// Install this object handle as an indirect object and return an
|
||||||
|
// indirect reference to it.
|
||||||
|
QPDFObjectHandle makeIndirectObject(QPDFObjectHandle);
|
||||||
|
|
||||||
|
// Retrieve an object by object ID and generation. Returns an
|
||||||
|
// indirect reference to it.
|
||||||
|
QPDFObjectHandle getObjectByID(int objid, int generation);
|
||||||
|
|
||||||
|
// Encryption support
|
||||||
|
|
||||||
|
struct EncryptionData
|
||||||
|
{
|
||||||
|
// This class holds data read from the encryption dictionary.
|
||||||
|
EncryptionData(int V, int R, int Length_bytes, long P,
|
||||||
|
std::string const& O, std::string const& U,
|
||||||
|
std::string const& id1) :
|
||||||
|
V(V),
|
||||||
|
R(R),
|
||||||
|
Length_bytes(Length_bytes),
|
||||||
|
P(P),
|
||||||
|
O(O),
|
||||||
|
U(U),
|
||||||
|
id1(id1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int V;
|
||||||
|
int R;
|
||||||
|
int Length_bytes;
|
||||||
|
long P;
|
||||||
|
std::string O;
|
||||||
|
std::string U;
|
||||||
|
std::string id1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void trim_user_password(std::string& user_password);
|
||||||
|
static std::string compute_data_key(
|
||||||
|
std::string const& encryption_key, int objid, int generation);
|
||||||
|
static std::string compute_encryption_key(
|
||||||
|
std::string const& password, EncryptionData const& data);
|
||||||
|
|
||||||
|
static void compute_encryption_O_U(
|
||||||
|
char const* user_password, char const* owner_password,
|
||||||
|
int V, int R, int key_len, unsigned long P,
|
||||||
|
std::string const& id1,
|
||||||
|
std::string& O, std::string& U);
|
||||||
|
std::string const& getUserPassword() const;
|
||||||
|
|
||||||
|
// Linearization support
|
||||||
|
|
||||||
|
// Returns true iff the file starts with a linearization parameter
|
||||||
|
// dictionary. Does no additional validation.
|
||||||
|
bool isLinearized();
|
||||||
|
|
||||||
|
// Performs various sanity checks on a linearized file. Return
|
||||||
|
// true if no errors or warnings. Otherwise, return false and
|
||||||
|
// output errors and warnings to stdout.
|
||||||
|
bool checkLinearization();
|
||||||
|
|
||||||
|
// Calls checkLinearization() and, if possible, prints normalized
|
||||||
|
// contents of some of the hints tables to stdout. Normalization
|
||||||
|
// includes adding min values to delta values and adjusting
|
||||||
|
// offsets based on the location and size of the primary hint
|
||||||
|
// stream.
|
||||||
|
void showLinearizationData();
|
||||||
|
|
||||||
|
// Shows the contents of the cross-reference table
|
||||||
|
void showXRefTable();
|
||||||
|
|
||||||
|
// Optimization support -- see doc/optimization. Implemented in
|
||||||
|
// QPDF_optimization.cc
|
||||||
|
|
||||||
|
// The object_stream_data map maps from a "compressed" object to
|
||||||
|
// the object stream that contains it. This enables optimize to
|
||||||
|
// populate the object <-> user maps with only uncompressed
|
||||||
|
// objects. If allow_changes is false, an exception will be
|
||||||
|
// thrown if any changes are made during the optimization process.
|
||||||
|
// This is available so that the test suite can make sure that a
|
||||||
|
// linearized file is already optimized. When called in this way,
|
||||||
|
// optimize() still populates the object <-> user maps
|
||||||
|
void optimize(std::map<int, int> const& object_stream_data,
|
||||||
|
bool allow_changes = true);
|
||||||
|
|
||||||
|
// Replace all references to indirect objects that are "scalars"
|
||||||
|
// (i.e., things that don't have children: not arrays, streams, or
|
||||||
|
// dictionaries) with direct objects.
|
||||||
|
void flattenScalarReferences();
|
||||||
|
|
||||||
|
// For QPDFWriter:
|
||||||
|
|
||||||
|
// Remove /ID, /Encrypt, and /Prev keys from the trailer
|
||||||
|
// dictionary since these are regenerated during write.
|
||||||
|
void trimTrailerForWrite();
|
||||||
|
|
||||||
|
// Get lists of all objects in order according to the part of a
|
||||||
|
// linearized file that they belong to.
|
||||||
|
void getLinearizedParts(
|
||||||
|
std::map<int, int> const& object_stream_data,
|
||||||
|
std::vector<QPDFObjectHandle>& part4,
|
||||||
|
std::vector<QPDFObjectHandle>& part6,
|
||||||
|
std::vector<QPDFObjectHandle>& part7,
|
||||||
|
std::vector<QPDFObjectHandle>& part8,
|
||||||
|
std::vector<QPDFObjectHandle>& part9);
|
||||||
|
|
||||||
|
void generateHintStream(std::map<int, QPDFXRefEntry> const& xref,
|
||||||
|
std::map<int, size_t> const& lengths,
|
||||||
|
std::map<int, int> const& obj_renumber,
|
||||||
|
PointerHolder<Buffer>& hint_stream,
|
||||||
|
int& S, int& O);
|
||||||
|
|
||||||
|
// Map object to object stream that contains it
|
||||||
|
void getObjectStreamData(std::map<int, int>&);
|
||||||
|
// Get a list of objects that would be permitted in an object
|
||||||
|
// stream
|
||||||
|
std::vector<int> getCompressibleObjects();
|
||||||
|
|
||||||
|
// Convenience routines for common functions. See also
|
||||||
|
// QPDFObjectHandle.hh for additional convenience routines.
|
||||||
|
|
||||||
|
// Traverse page tree return all /Page objects.
|
||||||
|
std::vector<QPDFObjectHandle> const& getAllPages();
|
||||||
|
|
||||||
|
// Resolver class is restricted to QPDFObjectHandle so that only
|
||||||
|
// it can resolve indirect references.
|
||||||
|
class Resolver
|
||||||
|
{
|
||||||
|
friend class QPDFObjectHandle;
|
||||||
|
private:
|
||||||
|
static PointerHolder<QPDFObject> resolve(
|
||||||
|
QPDF* qpdf, int objid, int generation)
|
||||||
|
{
|
||||||
|
return qpdf->resolve(objid, generation);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
friend class Resolver;
|
||||||
|
|
||||||
|
// Pipe class is restricted to QPDF_Stream
|
||||||
|
class Pipe
|
||||||
|
{
|
||||||
|
friend class QPDF_Stream;
|
||||||
|
private:
|
||||||
|
static void pipeStreamData(QPDF* qpdf, int objid, int generation,
|
||||||
|
off_t offset, size_t length,
|
||||||
|
QPDFObjectHandle dict,
|
||||||
|
Pipeline* pipeline)
|
||||||
|
{
|
||||||
|
qpdf->pipeStreamData(
|
||||||
|
objid, generation, offset, length, dict, pipeline);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
friend class Pipe;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class InputSource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InputSource() :
|
||||||
|
last_offset(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual ~InputSource()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLastOffset(off_t);
|
||||||
|
off_t getLastOffset() const;
|
||||||
|
std::string readLine();
|
||||||
|
|
||||||
|
virtual std::string const& getName() const = 0;
|
||||||
|
virtual off_t tell() = 0;
|
||||||
|
virtual void seek(off_t offset, int whence) = 0;
|
||||||
|
virtual void rewind() = 0;
|
||||||
|
virtual size_t read(char* buffer, int length) = 0;
|
||||||
|
virtual void unreadCh(char ch) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
off_t last_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileInputSource: public InputSource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FileInputSource();
|
||||||
|
void setFilename(char const* filename);
|
||||||
|
virtual ~FileInputSource();
|
||||||
|
virtual std::string const& getName() const;
|
||||||
|
virtual off_t tell();
|
||||||
|
virtual void seek(off_t offset, int whence);
|
||||||
|
virtual void rewind();
|
||||||
|
virtual size_t read(char* buffer, int length);
|
||||||
|
virtual void unreadCh(char ch);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FileInputSource(FileInputSource const&);
|
||||||
|
FileInputSource& operator=(FileInputSource const&);
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
std::string filename;
|
||||||
|
FILE* file;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BufferInputSource: public InputSource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BufferInputSource(std::string const& description, Buffer* buf);
|
||||||
|
virtual ~BufferInputSource();
|
||||||
|
virtual std::string const& getName() const;
|
||||||
|
virtual off_t tell();
|
||||||
|
virtual void seek(off_t offset, int whence);
|
||||||
|
virtual void rewind();
|
||||||
|
virtual size_t read(char* buffer, int length);
|
||||||
|
virtual void unreadCh(char ch);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string description;
|
||||||
|
Buffer* buf;
|
||||||
|
off_t cur_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ObjGen
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ObjGen();
|
||||||
|
ObjGen(int obj, int gen);
|
||||||
|
bool operator<(ObjGen const&) const;
|
||||||
|
|
||||||
|
int obj;
|
||||||
|
int gen;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ObjCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ObjCache() :
|
||||||
|
end_before_space(0),
|
||||||
|
end_after_space(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
ObjCache(PointerHolder<QPDFObject> object,
|
||||||
|
off_t end_before_space,
|
||||||
|
off_t end_after_space) :
|
||||||
|
object(object),
|
||||||
|
end_before_space(end_before_space),
|
||||||
|
end_after_space(end_after_space)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PointerHolder<QPDFObject> object;
|
||||||
|
off_t end_before_space;
|
||||||
|
off_t end_after_space;
|
||||||
|
};
|
||||||
|
|
||||||
|
void parse();
|
||||||
|
void warn(QPDFExc const& e);
|
||||||
|
void setTrailer(QPDFObjectHandle obj);
|
||||||
|
void read_xref(off_t offset);
|
||||||
|
void reconstruct_xref(QPDFExc& e);
|
||||||
|
int read_xrefTable(off_t offset);
|
||||||
|
int read_xrefStream(off_t offset);
|
||||||
|
int processXRefStream(off_t offset, QPDFObjectHandle& xref_stream);
|
||||||
|
void insertXrefEntry(int obj, int f0, int f1, int f2);
|
||||||
|
QPDFObjectHandle readObject(
|
||||||
|
InputSource*, int objid, int generation,
|
||||||
|
bool in_object_stream);
|
||||||
|
QPDFObjectHandle readObjectInternal(
|
||||||
|
InputSource* input, int objid, int generation,
|
||||||
|
bool in_object_stream,
|
||||||
|
bool in_array, bool in_dictionary);
|
||||||
|
int recoverStreamLength(
|
||||||
|
InputSource* input, int objid, int generation, off_t stream_offset);
|
||||||
|
QPDFTokenizer::Token readToken(InputSource*);
|
||||||
|
|
||||||
|
QPDFObjectHandle readObjectAtOffset(
|
||||||
|
off_t offset,
|
||||||
|
int exp_objid, int exp_generation,
|
||||||
|
int& act_objid, int& act_generation);
|
||||||
|
PointerHolder<QPDFObject> resolve(int objid, int generation);
|
||||||
|
void resolveObjectsInStream(int obj_stream_number);
|
||||||
|
|
||||||
|
// Calls finish() on the pipeline when done but does not delete it
|
||||||
|
void pipeStreamData(int objid, int generation,
|
||||||
|
off_t offset, size_t length,
|
||||||
|
QPDFObjectHandle dict,
|
||||||
|
Pipeline* pipeline);
|
||||||
|
void getAllPagesInternal(QPDFObjectHandle cur_pages,
|
||||||
|
std::vector<QPDFObjectHandle>& result);
|
||||||
|
|
||||||
|
// methods to support encryption -- implemented in QPDF_encryption.cc
|
||||||
|
void initializeEncryption();
|
||||||
|
std::string getKeyForObject(int objid, int generation);
|
||||||
|
void decryptString(std::string&, int objid, int generation);
|
||||||
|
void decryptStream(Pipeline*& pipeline, int objid, int generation,
|
||||||
|
std::vector<PointerHolder<Pipeline> >& heap);
|
||||||
|
|
||||||
|
// Linearization Hint table structures.
|
||||||
|
// Naming conventions:
|
||||||
|
|
||||||
|
// HSomething is the Something Hint Table or table header
|
||||||
|
// HSomethingEntry is an entry in the Something table
|
||||||
|
|
||||||
|
// delta_something + min_something = something
|
||||||
|
// nbits_something = number of bits required for something
|
||||||
|
|
||||||
|
// something_offset is the pre-adjusted offset in the file. If >=
|
||||||
|
// H0_offset, H0_length must be added to get an actual file
|
||||||
|
// offset.
|
||||||
|
|
||||||
|
// PDF 1.4: Table F.4
|
||||||
|
struct HPageOffsetEntry
|
||||||
|
{
|
||||||
|
HPageOffsetEntry() :
|
||||||
|
delta_nobjects(0),
|
||||||
|
delta_page_length(0),
|
||||||
|
nshared_objects(0),
|
||||||
|
delta_content_offset(0),
|
||||||
|
delta_content_length(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int delta_nobjects; // 1
|
||||||
|
int delta_page_length; // 2
|
||||||
|
int nshared_objects; // 3
|
||||||
|
// vectors' sizes = nshared_objects
|
||||||
|
std::vector<int> shared_identifiers; // 4
|
||||||
|
std::vector<int> shared_numerators; // 5
|
||||||
|
int delta_content_offset; // 6
|
||||||
|
int delta_content_length; // 7
|
||||||
|
};
|
||||||
|
|
||||||
|
// PDF 1.4: Table F.3
|
||||||
|
struct HPageOffset
|
||||||
|
{
|
||||||
|
HPageOffset() :
|
||||||
|
min_nobjects(0),
|
||||||
|
first_page_offset(0),
|
||||||
|
nbits_delta_nobjects(0),
|
||||||
|
min_page_length(0),
|
||||||
|
nbits_delta_page_length(0),
|
||||||
|
min_content_offset(0),
|
||||||
|
nbits_delta_content_offset(0),
|
||||||
|
min_content_length(0),
|
||||||
|
nbits_delta_content_length(0),
|
||||||
|
nbits_nshared_objects(0),
|
||||||
|
nbits_shared_identifier(0),
|
||||||
|
nbits_shared_numerator(0),
|
||||||
|
shared_denominator(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int min_nobjects; // 1
|
||||||
|
int first_page_offset; // 2
|
||||||
|
int nbits_delta_nobjects; // 3
|
||||||
|
int min_page_length; // 4
|
||||||
|
int nbits_delta_page_length; // 5
|
||||||
|
int min_content_offset; // 6
|
||||||
|
int nbits_delta_content_offset; // 7
|
||||||
|
int min_content_length; // 8
|
||||||
|
int nbits_delta_content_length; // 9
|
||||||
|
int nbits_nshared_objects; // 10
|
||||||
|
int nbits_shared_identifier; // 11
|
||||||
|
int nbits_shared_numerator; // 12
|
||||||
|
int shared_denominator; // 13
|
||||||
|
// vector size is npages
|
||||||
|
std::vector<HPageOffsetEntry> entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
// PDF 1.4: Table F.6
|
||||||
|
struct HSharedObjectEntry
|
||||||
|
{
|
||||||
|
HSharedObjectEntry() :
|
||||||
|
delta_group_length(0),
|
||||||
|
signature_present(0),
|
||||||
|
nobjects_minus_one(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item 3 is a 128-bit signature (unsupported by Acrobat)
|
||||||
|
int delta_group_length; // 1
|
||||||
|
int signature_present; // 2 -- always 0
|
||||||
|
int nobjects_minus_one; // 4 -- always 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// PDF 1.4: Table F.5
|
||||||
|
struct HSharedObject
|
||||||
|
{
|
||||||
|
HSharedObject() :
|
||||||
|
first_shared_obj(0),
|
||||||
|
first_shared_offset(0),
|
||||||
|
nshared_first_page(0),
|
||||||
|
nshared_total(0),
|
||||||
|
nbits_nobjects(0),
|
||||||
|
min_group_length(0),
|
||||||
|
nbits_delta_group_length(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int first_shared_obj; // 1
|
||||||
|
int first_shared_offset; // 2
|
||||||
|
int nshared_first_page; // 3
|
||||||
|
int nshared_total; // 4
|
||||||
|
int nbits_nobjects; // 5
|
||||||
|
int min_group_length; // 6
|
||||||
|
int nbits_delta_group_length; // 7
|
||||||
|
// vector size is nshared_total
|
||||||
|
std::vector<HSharedObjectEntry> entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
// PDF 1.4: Table F.9
|
||||||
|
struct HGeneric
|
||||||
|
{
|
||||||
|
HGeneric() :
|
||||||
|
first_object(0),
|
||||||
|
first_object_offset(0),
|
||||||
|
nobjects(0),
|
||||||
|
group_length(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int first_object; // 1
|
||||||
|
int first_object_offset; // 2
|
||||||
|
int nobjects; // 3
|
||||||
|
int group_length; // 4
|
||||||
|
};
|
||||||
|
|
||||||
|
// Other linearization data structures
|
||||||
|
|
||||||
|
// Initialized from Linearization Parameter dictionary
|
||||||
|
struct LinParameters
|
||||||
|
{
|
||||||
|
LinParameters() :
|
||||||
|
file_size(0),
|
||||||
|
first_page_object(0),
|
||||||
|
first_page_end(0),
|
||||||
|
npages(0),
|
||||||
|
xref_zero_offset(0),
|
||||||
|
first_page(0),
|
||||||
|
H_offset(0),
|
||||||
|
H_length(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_size; // /L
|
||||||
|
int first_page_object; // /O
|
||||||
|
int first_page_end; // /E
|
||||||
|
int npages; // /N
|
||||||
|
int xref_zero_offset; // /T
|
||||||
|
int first_page; // /P
|
||||||
|
int H_offset; // offset of primary hint stream
|
||||||
|
int H_length; // length of primary hint stream
|
||||||
|
};
|
||||||
|
|
||||||
|
// Computed hint table value data structures. These tables
|
||||||
|
// contain the computed values on which the hint table values are
|
||||||
|
// based. They exclude things like number of bits and store
|
||||||
|
// actual values instead of mins and deltas. File offsets are
|
||||||
|
// also absolute rather than being offset by the size of the
|
||||||
|
// primary hint table. We populate the hint table structures from
|
||||||
|
// these during writing and compare the hint table values with
|
||||||
|
// these during validation. We ignore some values for various
|
||||||
|
// reasons described in the code. Those values are omitted from
|
||||||
|
// these structures. Note also that object numbers are object
|
||||||
|
// numbers from the input file, not the output file.
|
||||||
|
|
||||||
|
// Naming convention: CHSomething is analogous to HSomething
|
||||||
|
// above. "CH" is computed hint.
|
||||||
|
|
||||||
|
struct CHPageOffsetEntry
|
||||||
|
{
|
||||||
|
CHPageOffsetEntry() :
|
||||||
|
nobjects(0),
|
||||||
|
nshared_objects(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int nobjects;
|
||||||
|
int nshared_objects;
|
||||||
|
// vectors' sizes = nshared_objects
|
||||||
|
std::vector<int> shared_identifiers;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CHPageOffset
|
||||||
|
{
|
||||||
|
// vector size is npages
|
||||||
|
std::vector<CHPageOffsetEntry> entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CHSharedObjectEntry
|
||||||
|
{
|
||||||
|
CHSharedObjectEntry(int object) :
|
||||||
|
object(object)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int object;
|
||||||
|
};
|
||||||
|
|
||||||
|
// PDF 1.4: Table F.5
|
||||||
|
struct CHSharedObject
|
||||||
|
{
|
||||||
|
CHSharedObject() :
|
||||||
|
first_shared_obj(0),
|
||||||
|
nshared_first_page(0),
|
||||||
|
nshared_total(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int first_shared_obj;
|
||||||
|
int nshared_first_page;
|
||||||
|
int nshared_total;
|
||||||
|
// vector size is nshared_total
|
||||||
|
std::vector<CHSharedObjectEntry> entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
// No need for CHGeneric -- HGeneric is fine as is.
|
||||||
|
|
||||||
|
|
||||||
|
// Data structures to support optimization -- implemented in
|
||||||
|
// QPDF_optimization.cc
|
||||||
|
|
||||||
|
class ObjUser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum user_e
|
||||||
|
{
|
||||||
|
ou_bad,
|
||||||
|
ou_page,
|
||||||
|
ou_thumb,
|
||||||
|
ou_trailer_key,
|
||||||
|
ou_root_key,
|
||||||
|
ou_root
|
||||||
|
};
|
||||||
|
|
||||||
|
// type is set to ou_bad
|
||||||
|
ObjUser();
|
||||||
|
|
||||||
|
// type must be ou_root
|
||||||
|
ObjUser(user_e type);
|
||||||
|
|
||||||
|
// type must be one of ou_page or ou_thumb
|
||||||
|
ObjUser(user_e type, int pageno);
|
||||||
|
|
||||||
|
// type must be one of ou_trailer_key or ou_root_key
|
||||||
|
ObjUser(user_e type, std::string const& key);
|
||||||
|
|
||||||
|
bool operator<(ObjUser const&) const;
|
||||||
|
|
||||||
|
user_e ou_type;
|
||||||
|
int pageno; // if ou_page;
|
||||||
|
std::string key; // if ou_trailer_key or ou_root_key
|
||||||
|
};
|
||||||
|
|
||||||
|
// methods to support linearization checking -- implemented in
|
||||||
|
// QPDF_linearization.cc
|
||||||
|
void readLinearizationData();
|
||||||
|
bool checkLinearizationInternal();
|
||||||
|
void dumpLinearizationDataInternal();
|
||||||
|
QPDFObjectHandle readHintStream(Pipeline&, off_t offset, size_t length);
|
||||||
|
void readHPageOffset(BitStream);
|
||||||
|
void readHSharedObject(BitStream);
|
||||||
|
void readHGeneric(BitStream, HGeneric&);
|
||||||
|
int maxEnd(ObjUser const& ou);
|
||||||
|
int getLinearizationOffset(ObjGen const&);
|
||||||
|
QPDFObjectHandle getUncompressedObject(
|
||||||
|
QPDFObjectHandle&, std::map<int, int> const& object_stream_data);
|
||||||
|
int lengthNextN(int first_object, int n,
|
||||||
|
std::list<std::string>& errors);
|
||||||
|
void checkHPageOffset(std::list<std::string>& errors,
|
||||||
|
std::list<std::string>& warnings,
|
||||||
|
std::vector<QPDFObjectHandle> const& pages,
|
||||||
|
std::map<int, int>& idx_to_obj);
|
||||||
|
void checkHSharedObject(std::list<std::string>& warnings,
|
||||||
|
std::list<std::string>& errors,
|
||||||
|
std::vector<QPDFObjectHandle> const& pages,
|
||||||
|
std::map<int, int>& idx_to_obj);
|
||||||
|
void checkHOutlines(std::list<std::string>& warnings);
|
||||||
|
void dumpHPageOffset();
|
||||||
|
void dumpHSharedObject();
|
||||||
|
void dumpHGeneric(HGeneric&);
|
||||||
|
int adjusted_offset(int offset);
|
||||||
|
QPDFObjectHandle objGenToIndirect(ObjGen const&);
|
||||||
|
void calculateLinearizationData(
|
||||||
|
std::map<int, int> const& object_stream_data);
|
||||||
|
void pushOutlinesToPart(
|
||||||
|
std::vector<QPDFObjectHandle>& part,
|
||||||
|
std::set<ObjGen>& lc_outlines,
|
||||||
|
std::map<int, int> const& object_stream_data);
|
||||||
|
int outputLengthNextN(
|
||||||
|
int in_object, int n,
|
||||||
|
std::map<int, size_t> const& lengths,
|
||||||
|
std::map<int, int> const& obj_renumber);
|
||||||
|
void calculateHPageOffset(
|
||||||
|
std::map<int, QPDFXRefEntry> const& xref,
|
||||||
|
std::map<int, size_t> const& lengths,
|
||||||
|
std::map<int, int> const& obj_renumber);
|
||||||
|
void calculateHSharedObject(
|
||||||
|
std::map<int, QPDFXRefEntry> const& xref,
|
||||||
|
std::map<int, size_t> const& lengths,
|
||||||
|
std::map<int, int> const& obj_renumber);
|
||||||
|
void calculateHOutline(
|
||||||
|
std::map<int, QPDFXRefEntry> const& xref,
|
||||||
|
std::map<int, size_t> const& lengths,
|
||||||
|
std::map<int, int> const& obj_renumber);
|
||||||
|
void writeHPageOffset(BitWriter&);
|
||||||
|
void writeHSharedObject(BitWriter&);
|
||||||
|
void writeHGeneric(BitWriter&, HGeneric&);
|
||||||
|
|
||||||
|
|
||||||
|
// Methods to support optimization
|
||||||
|
|
||||||
|
void optimizePagesTree(
|
||||||
|
QPDFObjectHandle,
|
||||||
|
std::map<std::string, std::vector<QPDFObjectHandle> >&,
|
||||||
|
int& pageno, bool allow_changes);
|
||||||
|
void updateObjectMaps(ObjUser const& ou, QPDFObjectHandle oh);
|
||||||
|
void updateObjectMapsInternal(ObjUser const& ou, QPDFObjectHandle oh,
|
||||||
|
std::set<ObjGen>& visited, bool top);
|
||||||
|
void filterCompressedObjects(std::map<int, int> const& object_stream_data);
|
||||||
|
|
||||||
|
|
||||||
|
QPDFTokenizer tokenizer;
|
||||||
|
FileInputSource file;
|
||||||
|
bool encrypted;
|
||||||
|
bool encryption_initialized;
|
||||||
|
bool ignore_xref_streams;
|
||||||
|
bool suppress_warnings;
|
||||||
|
bool attempt_recovery;
|
||||||
|
std::string provided_password;
|
||||||
|
std::string user_password;
|
||||||
|
std::string encryption_key;
|
||||||
|
std::string cached_object_encryption_key;
|
||||||
|
int cached_key_objid;
|
||||||
|
int cached_key_generation;
|
||||||
|
std::string pdf_version;
|
||||||
|
std::map<ObjGen, QPDFXRefEntry> xref_table;
|
||||||
|
std::set<int> deleted_objects;
|
||||||
|
std::map<ObjGen, ObjCache> obj_cache;
|
||||||
|
QPDFObjectHandle trailer;
|
||||||
|
std::vector<QPDFObjectHandle> all_pages;
|
||||||
|
std::vector<std::string> warnings;
|
||||||
|
|
||||||
|
// Linearization data
|
||||||
|
int first_xref_item_offset; // actual value from file
|
||||||
|
bool uncompressed_after_compressed;
|
||||||
|
|
||||||
|
// Linearization parameter dictionary and hint table data: may be
|
||||||
|
// read from file or computed prior to writing a linearized file
|
||||||
|
QPDFObjectHandle lindict;
|
||||||
|
LinParameters linp;
|
||||||
|
HPageOffset page_offset_hints;
|
||||||
|
HSharedObject shared_object_hints;
|
||||||
|
HGeneric outline_hints;
|
||||||
|
|
||||||
|
// Computed linearization data: used to populate above tables
|
||||||
|
// during writing and to compare with them during validation. c_
|
||||||
|
// means computed.
|
||||||
|
LinParameters c_linp;
|
||||||
|
CHPageOffset c_page_offset_data;
|
||||||
|
CHSharedObject c_shared_object_data;
|
||||||
|
HGeneric c_outline_data;
|
||||||
|
|
||||||
|
// Object ordering data for linearized files: initialized by
|
||||||
|
// calculateLinearizationData(). Part numbers refer to the PDF
|
||||||
|
// 1.4 specification.
|
||||||
|
std::vector<QPDFObjectHandle> part4;
|
||||||
|
std::vector<QPDFObjectHandle> part6;
|
||||||
|
std::vector<QPDFObjectHandle> part7;
|
||||||
|
std::vector<QPDFObjectHandle> part8;
|
||||||
|
std::vector<QPDFObjectHandle> part9;
|
||||||
|
|
||||||
|
// Optimization data
|
||||||
|
std::map<ObjUser, std::set<ObjGen> > obj_user_to_objects;
|
||||||
|
std::map<ObjGen, std::set<ObjUser> > object_to_obj_users;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __QPDF_HH__
|
22
include/qpdf/QPDFExc.hh
Normal file
22
include/qpdf/QPDFExc.hh
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __QPDFEXC_HH__
|
||||||
|
#define __QPDFEXC_HH__
|
||||||
|
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
|
||||||
|
class QPDFExc: public QEXC::General
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QPDFExc(std::string const& message);
|
||||||
|
QPDFExc(std::string const& filename, int offset,
|
||||||
|
std::string const& message);
|
||||||
|
virtual ~QPDFExc() throw ();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __QPDFEXC_HH__
|
20
include/qpdf/QPDFObject.hh
Normal file
20
include/qpdf/QPDFObject.hh
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __QPDFOBJECT_HH__
|
||||||
|
#define __QPDFOBJECT_HH__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class QPDFObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~QPDFObject() {}
|
||||||
|
virtual std::string unparse() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __QPDFOBJECT_HH__
|
221
include/qpdf/QPDFObjectHandle.hh
Normal file
221
include/qpdf/QPDFObjectHandle.hh
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __QPDFOBJECTHANDLE_HH__
|
||||||
|
#define __QPDFOBJECTHANDLE_HH__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <qpdf/PointerHolder.hh>
|
||||||
|
#include <qpdf/Buffer.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QPDFObject.hh>
|
||||||
|
|
||||||
|
class Pipeline;
|
||||||
|
class QPDF;
|
||||||
|
|
||||||
|
class QPDFObjectHandle
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QPDFObjectHandle();
|
||||||
|
bool isInitialized() const;
|
||||||
|
|
||||||
|
// Exactly one of these will return true for any object.
|
||||||
|
bool isBool();
|
||||||
|
bool isNull();
|
||||||
|
bool isInteger();
|
||||||
|
bool isReal();
|
||||||
|
bool isName();
|
||||||
|
bool isString();
|
||||||
|
bool isArray();
|
||||||
|
bool isDictionary();
|
||||||
|
bool isStream();
|
||||||
|
|
||||||
|
// This returns true in addition to the query for the specific
|
||||||
|
// type for indirect objects.
|
||||||
|
bool isIndirect();
|
||||||
|
|
||||||
|
// True for everything except array, dictionary, and stream
|
||||||
|
bool isScalar();
|
||||||
|
|
||||||
|
// Public factory methods
|
||||||
|
|
||||||
|
static QPDFObjectHandle newNull();
|
||||||
|
static QPDFObjectHandle newBool(bool value);
|
||||||
|
static QPDFObjectHandle newInteger(int value);
|
||||||
|
static QPDFObjectHandle newReal(std::string const& value);
|
||||||
|
static QPDFObjectHandle newName(std::string const& name);
|
||||||
|
static QPDFObjectHandle newString(std::string const& str);
|
||||||
|
static QPDFObjectHandle newArray(
|
||||||
|
std::vector<QPDFObjectHandle> const& items);
|
||||||
|
static QPDFObjectHandle newDictionary(
|
||||||
|
std::map<std::string, QPDFObjectHandle> const& items);
|
||||||
|
|
||||||
|
// Accessor methods. If an accessor method that is valid for only
|
||||||
|
// a particular object type is called on an object of the wrong
|
||||||
|
// type, an exception is thrown.
|
||||||
|
|
||||||
|
// Methods for bool objects
|
||||||
|
bool getBoolValue();
|
||||||
|
|
||||||
|
// Methods for integer objects
|
||||||
|
int getIntValue();
|
||||||
|
|
||||||
|
// Methods for real objects
|
||||||
|
std::string getRealValue();
|
||||||
|
|
||||||
|
// Methods that work for both integer and real objects
|
||||||
|
bool isNumber();
|
||||||
|
double getNumericValue();
|
||||||
|
|
||||||
|
// Methods for name objects
|
||||||
|
std::string getName();
|
||||||
|
|
||||||
|
// Methods for string objects
|
||||||
|
std::string getStringValue();
|
||||||
|
std::string getUTF8Value();
|
||||||
|
|
||||||
|
// Methods for array objects
|
||||||
|
int getArrayNItems();
|
||||||
|
QPDFObjectHandle getArrayItem(int n);
|
||||||
|
|
||||||
|
// Methods for dictionary objects
|
||||||
|
bool hasKey(std::string const&);
|
||||||
|
QPDFObjectHandle getKey(std::string const&);
|
||||||
|
std::set<std::string> getKeys();
|
||||||
|
|
||||||
|
// Mutator methods. Use with caution.
|
||||||
|
|
||||||
|
// Recursively copy this object, making it direct. Throws an
|
||||||
|
// exception if a loop is detected or any sub-object is a stream.
|
||||||
|
void makeDirect();
|
||||||
|
|
||||||
|
// Mutator methods for array objects
|
||||||
|
void setArrayItem(int, QPDFObjectHandle const&);
|
||||||
|
|
||||||
|
// Mutator methods for dictionary objects
|
||||||
|
|
||||||
|
// Replace value of key, adding it if it does not exist
|
||||||
|
void replaceKey(std::string const& key, QPDFObjectHandle const&);
|
||||||
|
// Remove key, doing nothing if key does not exist
|
||||||
|
void removeKey(std::string const& key);
|
||||||
|
|
||||||
|
// Methods for stream objects
|
||||||
|
QPDFObjectHandle getDict();
|
||||||
|
|
||||||
|
// Returns filtered (uncompressed) stream data. Throws an
|
||||||
|
// exception if the stream is filtered and we can't decode it.
|
||||||
|
PointerHolder<Buffer> getStreamData();
|
||||||
|
|
||||||
|
// Write stream data through the given pipeline. A null pipeline
|
||||||
|
// value may be used if all you want to do is determine whether a
|
||||||
|
// stream is filterable. If filter is false, write raw stream
|
||||||
|
// data and return false. If filter is true, then attempt to
|
||||||
|
// apply all the decoding filters to the stream data. If we are
|
||||||
|
// successful, return true. Otherwise, return false and write raw
|
||||||
|
// data. If filtering is requested and successfully performed,
|
||||||
|
// then the normalize and compress flags are used to determine
|
||||||
|
// whether stream data should be normalized and compressed. In
|
||||||
|
// all cases, if this function returns false, raw data has been
|
||||||
|
// written. If it returns true, then any requested filtering has
|
||||||
|
// been performed. Note that if the original stream data has no
|
||||||
|
// filters applied to it, the return value will be equal to the
|
||||||
|
// value of the filter parameter. Callers may use the return
|
||||||
|
// value of this function to determine whether or not the /Filter
|
||||||
|
// and /DecodeParms keys in the stream dictionary should be
|
||||||
|
// replaced if writing a new stream object.
|
||||||
|
bool pipeStreamData(Pipeline*, bool filter,
|
||||||
|
bool normalize, bool compress);
|
||||||
|
|
||||||
|
// return 0 for direct objects
|
||||||
|
int getObjectID() const;
|
||||||
|
int getGeneration() const;
|
||||||
|
|
||||||
|
std::string unparse();
|
||||||
|
std::string unparseResolved();
|
||||||
|
|
||||||
|
// Convenience routines for commonly performed functions
|
||||||
|
|
||||||
|
// Throws an exception if this is not a Page object. Returns an
|
||||||
|
// empty map if there are no images or no resources. This
|
||||||
|
// function does not presently support inherited resources. See
|
||||||
|
// comment in the source for details. Return value is a map from
|
||||||
|
// XObject name to the image object, which is always a stream.
|
||||||
|
std::map<std::string, QPDFObjectHandle> getPageImages();
|
||||||
|
|
||||||
|
// Throws an exception if this is not a Page object. Returns a
|
||||||
|
// vector of stream objects representing the content streams for
|
||||||
|
// the given page. This routine allows the caller to not care
|
||||||
|
// whether there are one or more than one content streams for a
|
||||||
|
// page.
|
||||||
|
std::vector<QPDFObjectHandle> getPageContents();
|
||||||
|
|
||||||
|
// Initializers for objects. This Factory class gives the QPDF
|
||||||
|
// class specific permission to call factory methods without
|
||||||
|
// making it a friend of the whole QPDFObjectHandle class.
|
||||||
|
class Factory
|
||||||
|
{
|
||||||
|
friend class QPDF;
|
||||||
|
private:
|
||||||
|
static QPDFObjectHandle newIndirect(QPDF* qpdf,
|
||||||
|
int objid, int generation)
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle::newIndirect(qpdf, objid, generation);
|
||||||
|
}
|
||||||
|
// object must be dictionary object
|
||||||
|
static QPDFObjectHandle newStream(
|
||||||
|
QPDF* qpdf, int objid, int generation,
|
||||||
|
QPDFObjectHandle stream_dict, off_t offset, int length)
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle::newStream(
|
||||||
|
qpdf, objid, generation, stream_dict, offset, length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
friend class Factory;
|
||||||
|
|
||||||
|
// Accessor for raw underlying object -- only QPDF is allowed to
|
||||||
|
// call this.
|
||||||
|
class ObjAccessor
|
||||||
|
{
|
||||||
|
friend class QPDF;
|
||||||
|
private:
|
||||||
|
static PointerHolder<QPDFObject> getObject(QPDFObjectHandle& o)
|
||||||
|
{
|
||||||
|
o.dereference();
|
||||||
|
return o.obj;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
friend class ObjAccessor;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPDFObjectHandle(QPDF*, int objid, int generation);
|
||||||
|
QPDFObjectHandle(QPDFObject*);
|
||||||
|
|
||||||
|
// Private object factory methods
|
||||||
|
static QPDFObjectHandle newIndirect(QPDF*, int objid, int generation);
|
||||||
|
static QPDFObjectHandle newStream(
|
||||||
|
QPDF* qpdf, int objid, int generation,
|
||||||
|
QPDFObjectHandle stream_dict, off_t offset, int length);
|
||||||
|
|
||||||
|
void assertInitialized() const;
|
||||||
|
void assertType(char const* type_name, bool istype);
|
||||||
|
void assertPageObject();
|
||||||
|
void dereference();
|
||||||
|
void makeDirectInternal(std::set<int>& visited);
|
||||||
|
|
||||||
|
bool initialized;
|
||||||
|
|
||||||
|
QPDF* qpdf; // 0 for direct object
|
||||||
|
int objid; // 0 for direct object
|
||||||
|
int generation;
|
||||||
|
PointerHolder<QPDFObject> obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __QPDFOBJECTHANDLE_HH__
|
141
include/qpdf/QPDFTokenizer.hh
Normal file
141
include/qpdf/QPDFTokenizer.hh
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __QPDFTOKENIZER_HH__
|
||||||
|
#define __QPDFTOKENIZER_HH__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
class QPDFTokenizer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum token_type_e
|
||||||
|
{
|
||||||
|
tt_bad,
|
||||||
|
tt_array_close,
|
||||||
|
tt_array_open,
|
||||||
|
tt_brace_close,
|
||||||
|
tt_brace_open,
|
||||||
|
tt_dict_close,
|
||||||
|
tt_dict_open,
|
||||||
|
tt_integer,
|
||||||
|
tt_name,
|
||||||
|
tt_real,
|
||||||
|
tt_string,
|
||||||
|
tt_null,
|
||||||
|
tt_bool,
|
||||||
|
tt_word,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Token
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Token() : type(tt_bad) {}
|
||||||
|
|
||||||
|
Token(token_type_e type, std::string const& value) :
|
||||||
|
type(type),
|
||||||
|
value(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Token(token_type_e type, std::string const& value,
|
||||||
|
std::string raw_value, std::string error_message) :
|
||||||
|
type(type),
|
||||||
|
value(value),
|
||||||
|
raw_value(raw_value),
|
||||||
|
error_message(error_message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
token_type_e getType() const
|
||||||
|
{
|
||||||
|
return this->type;
|
||||||
|
}
|
||||||
|
std::string const& getValue() const
|
||||||
|
{
|
||||||
|
return this->value;
|
||||||
|
}
|
||||||
|
std::string const& getRawValue() const
|
||||||
|
{
|
||||||
|
return this->raw_value;
|
||||||
|
}
|
||||||
|
std::string const& getErrorMessage() const
|
||||||
|
{
|
||||||
|
return this->error_message;
|
||||||
|
}
|
||||||
|
bool operator==(Token const& rhs)
|
||||||
|
{
|
||||||
|
// Ignore fields other than type and value
|
||||||
|
return ((this->type != tt_bad) &&
|
||||||
|
(this->type == rhs.type) &&
|
||||||
|
(this->value == rhs.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
token_type_e type;
|
||||||
|
std::string value;
|
||||||
|
std::string raw_value;
|
||||||
|
std::string error_message;
|
||||||
|
};
|
||||||
|
|
||||||
|
QPDFTokenizer();
|
||||||
|
|
||||||
|
// PDF files with version < 1.2 allowed the pound character
|
||||||
|
// anywhere in a name. Starting with version 1.2, the pound
|
||||||
|
// character was allowed only when followed by two hexadecimal
|
||||||
|
// digits. This method should be called when parsing a PDF file
|
||||||
|
// whose version is older than 1.2.
|
||||||
|
void allowPoundAnywhereInName();
|
||||||
|
|
||||||
|
// Mode of operation:
|
||||||
|
|
||||||
|
// Keep presenting characters and calling getToken() until
|
||||||
|
// getToken() returns true. When it does, be sure to check
|
||||||
|
// unread_ch and to unread ch if it is true.
|
||||||
|
|
||||||
|
// It these are called when a token is available, an exception
|
||||||
|
// will be thrown.
|
||||||
|
void presentCharacter(char ch);
|
||||||
|
void presentEOF();
|
||||||
|
|
||||||
|
// If a token is available, return true and initialize token with
|
||||||
|
// the token, unread_char with whether or not we have to unread
|
||||||
|
// the last character, and if unread_char, ch with the character
|
||||||
|
// to unread.
|
||||||
|
bool getToken(Token& token, bool& unread_char, char& ch);
|
||||||
|
|
||||||
|
// This function returns true of the current character is between
|
||||||
|
// tokens (i.e., white space that is not part of a string) or is
|
||||||
|
// part of a comment. A tokenizing filter can call this to
|
||||||
|
// determine whether to output the character.
|
||||||
|
bool betweenTokens();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
// Lexer state
|
||||||
|
enum { st_top, st_in_comment, st_in_string, st_lt, st_gt,
|
||||||
|
st_literal, st_in_hexstring, st_token_ready } state;
|
||||||
|
|
||||||
|
bool pound_special_in_name;
|
||||||
|
|
||||||
|
// Current token accumulation
|
||||||
|
token_type_e type;
|
||||||
|
std::string val;
|
||||||
|
std::string raw_val;
|
||||||
|
std::string error_message;
|
||||||
|
bool unread_char;
|
||||||
|
char char_to_unread;
|
||||||
|
|
||||||
|
// State for strings
|
||||||
|
int string_depth;
|
||||||
|
bool string_ignoring_newline;
|
||||||
|
char bs_num_register[4];
|
||||||
|
bool last_char_was_bs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __QPDFTOKENIZER_HH__
|
243
include/qpdf/QPDFWriter.hh
Normal file
243
include/qpdf/QPDFWriter.hh
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
// This class implements a simple writer for saving QPDF objects to
|
||||||
|
// new PDF files. See comments through the header file for additional
|
||||||
|
// details.
|
||||||
|
|
||||||
|
#ifndef __QPDFWRITER_HH__
|
||||||
|
#define __QPDFWRITER_HH__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <qpdf/QPDFXRefEntry.hh>
|
||||||
|
|
||||||
|
#include <qpdf/PointerHolder.hh>
|
||||||
|
#include <qpdf/Pipeline.hh>
|
||||||
|
#include <qpdf/Buffer.hh>
|
||||||
|
|
||||||
|
class QPDF;
|
||||||
|
class QPDFObjectHandle;
|
||||||
|
class Pl_Count;
|
||||||
|
|
||||||
|
class QPDFWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Passing null as filename means write to stdout
|
||||||
|
QPDFWriter(QPDF& pdf, char const* filename);
|
||||||
|
~QPDFWriter();
|
||||||
|
|
||||||
|
// Set the value of object stream mode. In disable mode, we never
|
||||||
|
// generate any object streams. In preserve mode, we preserve
|
||||||
|
// object stream structure from the original file. In generate
|
||||||
|
// mode, we generate our own object streams. In all cases, we
|
||||||
|
// generate a conventional cross-reference table if there are no
|
||||||
|
// object streams and a cross-reference stream if there are object
|
||||||
|
// streams. The default is o_preserve.
|
||||||
|
enum object_stream_e { o_disable, o_preserve, o_generate };
|
||||||
|
void setObjectStreamMode(object_stream_e);
|
||||||
|
|
||||||
|
// Set value of stream data mode. In uncompress mode, we attempt
|
||||||
|
// to uncompress any stream that we can. In preserve mode, we
|
||||||
|
// preserve any filtering applied to streams. In compress mode,
|
||||||
|
// if we can apply all filters and the stream is not already
|
||||||
|
// optimally compressed, recompress the stream.
|
||||||
|
enum stream_data_e { s_uncompress, s_preserve, s_compress };
|
||||||
|
void setStreamDataMode(stream_data_e);
|
||||||
|
|
||||||
|
// Set value of content stream normalization. The default is
|
||||||
|
// "false". If true, we attempt to normalize newlines inside of
|
||||||
|
// content streams. Some constructs such as inline images may
|
||||||
|
// thwart our efforts. There may be some cases where this can
|
||||||
|
// damage the content stream. This flag should be used only for
|
||||||
|
// debugging and experimenting with PDF content streams. Never
|
||||||
|
// use it for production files.
|
||||||
|
void setContentNormalization(bool);
|
||||||
|
|
||||||
|
// Set QDF mode. QDF mode causes special "pretty printing" of
|
||||||
|
// PDF objects, adds comments for easier perusing of files.
|
||||||
|
// Resulting PDF files can be edited in a text editor and then run
|
||||||
|
// through fix-qdf to update cross reference tables and stream
|
||||||
|
// lengths.
|
||||||
|
void setQDFMode(bool);
|
||||||
|
|
||||||
|
// Cause a static /ID value to be generated. Use only in test
|
||||||
|
// suites.
|
||||||
|
void setStaticID(bool);
|
||||||
|
|
||||||
|
// Preserve encryption. The default is true unless prefilering,
|
||||||
|
// content normalization, or qdf mode has been selected in which
|
||||||
|
// case encryption is never preserved. Encryption is also not
|
||||||
|
// preserved if we explicitly set encryption parameters.
|
||||||
|
void setPreserveEncryption(bool);
|
||||||
|
|
||||||
|
// Set up for encrypted output. Disables stream prefiltering and
|
||||||
|
// content normalization. Note that setting R2 encryption
|
||||||
|
// parameters sets the PDF version to at least 1.3, and setting R3
|
||||||
|
// encryption parameters pushes the PDF version number to at least
|
||||||
|
// 1.4.
|
||||||
|
void setR2EncryptionParameters(
|
||||||
|
char const* user_password, char const* owner_password,
|
||||||
|
bool allow_print, bool allow_modify,
|
||||||
|
bool allow_extract, bool allow_annotate);
|
||||||
|
enum r3_print_e
|
||||||
|
{
|
||||||
|
r3p_full, // allow all printing
|
||||||
|
r3p_low, // allow only low-resolution printing
|
||||||
|
r3p_none // allow no printing
|
||||||
|
};
|
||||||
|
enum r3_modify_e
|
||||||
|
{
|
||||||
|
r3m_all, // allow all modification
|
||||||
|
r3m_annotate, // allow comment authoring and form operations
|
||||||
|
r3m_form, // allow form field fill-in or signing
|
||||||
|
r3m_assembly, // allow only document assembly
|
||||||
|
r3m_none // allow no modification
|
||||||
|
};
|
||||||
|
void setR3EncryptionParameters(
|
||||||
|
char const* user_password, char const* owner_password,
|
||||||
|
bool allow_accessibility, bool allow_extract,
|
||||||
|
r3_print_e print, r3_modify_e modify);
|
||||||
|
|
||||||
|
// Create linearized output. Disables qdf mode, content
|
||||||
|
// normalization, and stream prefiltering.
|
||||||
|
void setLinearization(bool);
|
||||||
|
|
||||||
|
void write();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// flags used by unparseObject
|
||||||
|
static int const f_stream = 1 << 0;
|
||||||
|
static int const f_filtered = 1 << 1;
|
||||||
|
static int const f_in_ostream = 1 << 2;
|
||||||
|
|
||||||
|
enum trailer_e { t_normal, t_lin_first, t_lin_second };
|
||||||
|
|
||||||
|
int bytesNeeded(unsigned long n);
|
||||||
|
void writeBinary(unsigned long val, unsigned int bytes);
|
||||||
|
void writeString(std::string const& str);
|
||||||
|
void writeBuffer(PointerHolder<Buffer>&);
|
||||||
|
void writeStringQDF(std::string const& str);
|
||||||
|
void writeStringNoQDF(std::string const& str);
|
||||||
|
void assignCompressedObjectNumbers(int objid);
|
||||||
|
void enqueueObject(QPDFObjectHandle object);
|
||||||
|
void writeObjectStreamOffsets(std::vector<int>& offsets, int first_obj);
|
||||||
|
void writeObjectStream(QPDFObjectHandle object);
|
||||||
|
void writeObject(QPDFObjectHandle object, int object_stream_index = -1);
|
||||||
|
void writeTrailer(trailer_e which, int size,
|
||||||
|
bool xref_stream, int prev = 0);
|
||||||
|
void unparseObject(QPDFObjectHandle object, int level,
|
||||||
|
unsigned int flags);
|
||||||
|
void unparseObject(QPDFObjectHandle object, int level,
|
||||||
|
unsigned int flags,
|
||||||
|
// for stream dictionaries
|
||||||
|
int stream_length, bool compress);
|
||||||
|
void unparseChild(QPDFObjectHandle child, int level, int flags);
|
||||||
|
void initializeSpecialStreams();
|
||||||
|
void preserveObjectStreams();
|
||||||
|
void generateObjectStreams();
|
||||||
|
void generateID();
|
||||||
|
void setEncryptionParameters(
|
||||||
|
char const* user_password, char const* owner_password,
|
||||||
|
int V, int R, int key_len, std::set<int>& bits_to_clear);
|
||||||
|
void setEncryptionParametersInternal(
|
||||||
|
int V, int R, int key_len, long P,
|
||||||
|
std::string const& O, std::string const& U,
|
||||||
|
std::string const& id1, std::string const& user_password);
|
||||||
|
void copyEncryptionParameters();
|
||||||
|
void setDataKey(int objid);
|
||||||
|
int openObject(int objid = 0);
|
||||||
|
void closeObject(int objid);
|
||||||
|
void writeStandard();
|
||||||
|
void writeLinearized();
|
||||||
|
void enqueuePart(std::vector<QPDFObjectHandle>& part);
|
||||||
|
void writeEncryptionDictionary();
|
||||||
|
void writeHeader();
|
||||||
|
void writeHintStream(int hint_id);
|
||||||
|
int writeXRefTable(trailer_e which, int first, int last, int size);
|
||||||
|
int writeXRefTable(trailer_e which, int first, int last, int size,
|
||||||
|
// for linearization
|
||||||
|
int prev,
|
||||||
|
bool suppress_offsets,
|
||||||
|
int hint_id,
|
||||||
|
int hint_offset,
|
||||||
|
int hint_length);
|
||||||
|
int writeXRefStream(int objid, int max_id, int max_offset,
|
||||||
|
trailer_e which, int first, int last, int size);
|
||||||
|
int writeXRefStream(int objid, int max_id, int max_offset,
|
||||||
|
trailer_e which, int first, int last, int size,
|
||||||
|
// for linearization
|
||||||
|
int prev,
|
||||||
|
int hint_id,
|
||||||
|
int hint_offset,
|
||||||
|
int hint_length);
|
||||||
|
|
||||||
|
// When filtering subsections, push additional pipelines to the
|
||||||
|
// stack. When ready to switch, activate the pipeline stack.
|
||||||
|
// Pipelines passed to pushPipeline are deleted when
|
||||||
|
// clearPipelineStack is called.
|
||||||
|
Pipeline* pushPipeline(Pipeline*);
|
||||||
|
void activatePipelineStack();
|
||||||
|
|
||||||
|
// Calls finish on the current pipeline and pops the pipeline
|
||||||
|
// stack until the top of stack is a previous active top of stack,
|
||||||
|
// and restores the pipeline to that point. Deletes any piplines
|
||||||
|
// that it pops. If the bp argument is non-null and any of the
|
||||||
|
// stack items are of type Pl_Buffer, the buffer is retrieved.
|
||||||
|
void popPipelineStack(PointerHolder<Buffer>* bp = 0);
|
||||||
|
|
||||||
|
void pushEncryptionFilter();
|
||||||
|
void pushDiscardFilter();
|
||||||
|
|
||||||
|
QPDF& pdf;
|
||||||
|
char const* filename;
|
||||||
|
FILE* file;
|
||||||
|
bool close_file;
|
||||||
|
bool normalize_content_set;
|
||||||
|
bool normalize_content;
|
||||||
|
bool stream_data_mode_set;
|
||||||
|
stream_data_e stream_data_mode;
|
||||||
|
bool qdf_mode;
|
||||||
|
bool static_id;
|
||||||
|
bool direct_stream_lengths;
|
||||||
|
bool encrypted;
|
||||||
|
bool preserve_encryption;
|
||||||
|
bool linearized;
|
||||||
|
object_stream_e object_stream_mode;
|
||||||
|
std::string encryption_key;
|
||||||
|
std::map<std::string, std::string> encryption_dictionary;
|
||||||
|
|
||||||
|
std::string id1; // for /ID key of
|
||||||
|
std::string id2; // trailer dictionary
|
||||||
|
std::string min_pdf_version;
|
||||||
|
int encryption_dict_objid;
|
||||||
|
std::string cur_data_key;
|
||||||
|
std::list<PointerHolder<Pipeline> > to_delete;
|
||||||
|
Pl_Count* pipeline;
|
||||||
|
std::list<QPDFObjectHandle> object_queue;
|
||||||
|
std::map<int, int> obj_renumber;
|
||||||
|
std::map<int, QPDFXRefEntry> xref;
|
||||||
|
std::map<int, size_t> lengths;
|
||||||
|
int next_objid;
|
||||||
|
int cur_stream_length_id;
|
||||||
|
int cur_stream_length;
|
||||||
|
bool added_newline;
|
||||||
|
int max_ostream_index;
|
||||||
|
std::set<int> normalized_streams;
|
||||||
|
std::map<int, int> page_object_to_seq;
|
||||||
|
std::map<int, int> contents_to_page_seq;
|
||||||
|
std::map<int, int> object_to_object_stream;
|
||||||
|
std::map<int, std::set<int> > object_stream_to_objects;
|
||||||
|
std::list<Pipeline*> pipeline_stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __QPDFWRITER_HH__
|
34
include/qpdf/QPDFXRefEntry.hh
Normal file
34
include/qpdf/QPDFXRefEntry.hh
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __QPDFXREFENTRY_HH__
|
||||||
|
#define __QPDFXREFENTRY_HH__
|
||||||
|
|
||||||
|
class QPDFXRefEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Type constants are from the PDF spec section
|
||||||
|
// "Cross-Reference Streams":
|
||||||
|
// 0 = free entry; not used
|
||||||
|
// 1 = "uncompressed"; field 1 = offset
|
||||||
|
// 2 = "compressed"; field 1 = object stream number, field 2 = index
|
||||||
|
|
||||||
|
QPDFXRefEntry();
|
||||||
|
QPDFXRefEntry(int type, int field1, int field2);
|
||||||
|
|
||||||
|
int getType() const;
|
||||||
|
int getOffset() const; // only for type 1
|
||||||
|
int getObjStreamNumber() const; // only for type 2
|
||||||
|
int getObjStreamIndex() const; // only for type 2
|
||||||
|
|
||||||
|
private:
|
||||||
|
int type;
|
||||||
|
int field1;
|
||||||
|
int field2;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __QPDFXREFENTRY_HH__
|
16
include/qpdf/QTC.hh
Normal file
16
include/qpdf/QTC.hh
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __QTC_HH__
|
||||||
|
#define __QTC_HH__
|
||||||
|
|
||||||
|
namespace QTC
|
||||||
|
{
|
||||||
|
void TC(char const* const scope, char const* const ccase, int n = 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __QTC_HH__
|
45
include/qpdf/QUtil.hh
Normal file
45
include/qpdf/QUtil.hh
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) 2005-2008 Jay Berkenbilt
|
||||||
|
//
|
||||||
|
// This file is part of qpdf. This software may be distributed under
|
||||||
|
// the terms of version 2 of the Artistic License which may be found
|
||||||
|
// in the source distribution. It is provided "as is" without express
|
||||||
|
// or implied warranty.
|
||||||
|
|
||||||
|
#ifndef __QUTIL_HH__
|
||||||
|
#define __QUTIL_HH__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
|
||||||
|
namespace QUtil
|
||||||
|
{
|
||||||
|
// This is a collection of useful utility functions that don't
|
||||||
|
// really go anywhere else.
|
||||||
|
std::string int_to_string(int, int length = 0);
|
||||||
|
std::string double_to_string(double, int decimal_places = 0);
|
||||||
|
|
||||||
|
// If status is -1, convert the current value of errno to a
|
||||||
|
// QEXC::System exception. Otherwise, return status.
|
||||||
|
int os_wrapper(std::string const& description, int status)
|
||||||
|
throw (QEXC::System);
|
||||||
|
|
||||||
|
FILE* fopen_wrapper(std::string const&, FILE*)
|
||||||
|
throw (QEXC::System);
|
||||||
|
|
||||||
|
char* copy_string(std::string const&);
|
||||||
|
|
||||||
|
// Get the value of an environment variable in a portable fashion.
|
||||||
|
// Returns true iff the variable is defined. If `value' is
|
||||||
|
// non-null, initializes it with the value of the variable.
|
||||||
|
bool get_env(std::string const& var, std::string* value = 0);
|
||||||
|
|
||||||
|
// Return a string containing the byte representation of the UTF-8
|
||||||
|
// encoding for the unicode value passed in.
|
||||||
|
std::string toUTF8(unsigned long uval);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __QUTIL_HH__
|
519
install-sh
Executable file
519
install-sh
Executable file
@ -0,0 +1,519 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# install - install a program, script, or datafile
|
||||||
|
|
||||||
|
scriptversion=2006-12-25.00
|
||||||
|
|
||||||
|
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||||
|
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||||
|
# following copyright and license.
|
||||||
|
#
|
||||||
|
# Copyright (C) 1994 X Consortium
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to
|
||||||
|
# deal in the Software without restriction, including without limitation the
|
||||||
|
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
# sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||||
|
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
#
|
||||||
|
# Except as contained in this notice, the name of the X Consortium shall not
|
||||||
|
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||||
|
# ings in this Software without prior written authorization from the X Consor-
|
||||||
|
# tium.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# FSF changes to this file are in the public domain.
|
||||||
|
#
|
||||||
|
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||||
|
# `make' implicit rules from creating a file called install from it
|
||||||
|
# when there is no Makefile.
|
||||||
|
#
|
||||||
|
# This script is compatible with the BSD install script, but was written
|
||||||
|
# from scratch.
|
||||||
|
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
IFS=" "" $nl"
|
||||||
|
|
||||||
|
# set DOITPROG to echo to test this script
|
||||||
|
|
||||||
|
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||||
|
doit=${DOITPROG-}
|
||||||
|
if test -z "$doit"; then
|
||||||
|
doit_exec=exec
|
||||||
|
else
|
||||||
|
doit_exec=$doit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Put in absolute file names if you don't have them in your path;
|
||||||
|
# or use environment vars.
|
||||||
|
|
||||||
|
chgrpprog=${CHGRPPROG-chgrp}
|
||||||
|
chmodprog=${CHMODPROG-chmod}
|
||||||
|
chownprog=${CHOWNPROG-chown}
|
||||||
|
cmpprog=${CMPPROG-cmp}
|
||||||
|
cpprog=${CPPROG-cp}
|
||||||
|
mkdirprog=${MKDIRPROG-mkdir}
|
||||||
|
mvprog=${MVPROG-mv}
|
||||||
|
rmprog=${RMPROG-rm}
|
||||||
|
stripprog=${STRIPPROG-strip}
|
||||||
|
|
||||||
|
posix_glob='?'
|
||||||
|
initialize_posix_glob='
|
||||||
|
test "$posix_glob" != "?" || {
|
||||||
|
if (set -f) 2>/dev/null; then
|
||||||
|
posix_glob=
|
||||||
|
else
|
||||||
|
posix_glob=:
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
'
|
||||||
|
|
||||||
|
posix_mkdir=
|
||||||
|
|
||||||
|
# Desired mode of installed file.
|
||||||
|
mode=0755
|
||||||
|
|
||||||
|
chgrpcmd=
|
||||||
|
chmodcmd=$chmodprog
|
||||||
|
chowncmd=
|
||||||
|
mvcmd=$mvprog
|
||||||
|
rmcmd="$rmprog -f"
|
||||||
|
stripcmd=
|
||||||
|
|
||||||
|
src=
|
||||||
|
dst=
|
||||||
|
dir_arg=
|
||||||
|
dst_arg=
|
||||||
|
|
||||||
|
copy_on_change=false
|
||||||
|
no_target_directory=
|
||||||
|
|
||||||
|
usage="\
|
||||||
|
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||||
|
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||||
|
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||||
|
or: $0 [OPTION]... -d DIRECTORIES...
|
||||||
|
|
||||||
|
In the 1st form, copy SRCFILE to DSTFILE.
|
||||||
|
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||||
|
In the 4th, create DIRECTORIES.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--help display this help and exit.
|
||||||
|
--version display version info and exit.
|
||||||
|
|
||||||
|
-c (ignored)
|
||||||
|
-C install only if different (preserve the last data modification time)
|
||||||
|
-d create directories instead of installing files.
|
||||||
|
-g GROUP $chgrpprog installed files to GROUP.
|
||||||
|
-m MODE $chmodprog installed files to MODE.
|
||||||
|
-o USER $chownprog installed files to USER.
|
||||||
|
-s $stripprog installed files.
|
||||||
|
-t DIRECTORY install into DIRECTORY.
|
||||||
|
-T report an error if DSTFILE is a directory.
|
||||||
|
|
||||||
|
Environment variables override the default commands:
|
||||||
|
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||||
|
RMPROG STRIPPROG
|
||||||
|
"
|
||||||
|
|
||||||
|
while test $# -ne 0; do
|
||||||
|
case $1 in
|
||||||
|
-c) ;;
|
||||||
|
|
||||||
|
-C) copy_on_change=true;;
|
||||||
|
|
||||||
|
-d) dir_arg=true;;
|
||||||
|
|
||||||
|
-g) chgrpcmd="$chgrpprog $2"
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
--help) echo "$usage"; exit $?;;
|
||||||
|
|
||||||
|
-m) mode=$2
|
||||||
|
case $mode in
|
||||||
|
*' '* | *' '* | *'
|
||||||
|
'* | *'*'* | *'?'* | *'['*)
|
||||||
|
echo "$0: invalid mode: $mode" >&2
|
||||||
|
exit 1;;
|
||||||
|
esac
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-o) chowncmd="$chownprog $2"
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-s) stripcmd=$stripprog;;
|
||||||
|
|
||||||
|
-t) dst_arg=$2
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-T) no_target_directory=true;;
|
||||||
|
|
||||||
|
--version) echo "$0 $scriptversion"; exit $?;;
|
||||||
|
|
||||||
|
--) shift
|
||||||
|
break;;
|
||||||
|
|
||||||
|
-*) echo "$0: invalid option: $1" >&2
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||||
|
# When -d is used, all remaining arguments are directories to create.
|
||||||
|
# When -t is used, the destination is already specified.
|
||||||
|
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
if test -n "$dst_arg"; then
|
||||||
|
# $@ is not empty: it contains at least $arg.
|
||||||
|
set fnord "$@" "$dst_arg"
|
||||||
|
shift # fnord
|
||||||
|
fi
|
||||||
|
shift # arg
|
||||||
|
dst_arg=$arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $# -eq 0; then
|
||||||
|
if test -z "$dir_arg"; then
|
||||||
|
echo "$0: no input file specified." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# It's OK to call `install-sh -d' without argument.
|
||||||
|
# This can happen when creating conditional directories.
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$dir_arg"; then
|
||||||
|
trap '(exit $?); exit' 1 2 13 15
|
||||||
|
|
||||||
|
# Set umask so as not to create temps with too-generous modes.
|
||||||
|
# However, 'strip' requires both read and write access to temps.
|
||||||
|
case $mode in
|
||||||
|
# Optimize common cases.
|
||||||
|
*644) cp_umask=133;;
|
||||||
|
*755) cp_umask=22;;
|
||||||
|
|
||||||
|
*[0-7])
|
||||||
|
if test -z "$stripcmd"; then
|
||||||
|
u_plus_rw=
|
||||||
|
else
|
||||||
|
u_plus_rw='% 200'
|
||||||
|
fi
|
||||||
|
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||||
|
*)
|
||||||
|
if test -z "$stripcmd"; then
|
||||||
|
u_plus_rw=
|
||||||
|
else
|
||||||
|
u_plus_rw=,u+rw
|
||||||
|
fi
|
||||||
|
cp_umask=$mode$u_plus_rw;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
for src
|
||||||
|
do
|
||||||
|
# Protect names starting with `-'.
|
||||||
|
case $src in
|
||||||
|
-*) src=./$src;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
dst=$src
|
||||||
|
dstdir=$dst
|
||||||
|
test -d "$dstdir"
|
||||||
|
dstdir_status=$?
|
||||||
|
else
|
||||||
|
|
||||||
|
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||||
|
# might cause directories to be created, which would be especially bad
|
||||||
|
# if $src (and thus $dsttmp) contains '*'.
|
||||||
|
if test ! -f "$src" && test ! -d "$src"; then
|
||||||
|
echo "$0: $src does not exist." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$dst_arg"; then
|
||||||
|
echo "$0: no destination specified." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
dst=$dst_arg
|
||||||
|
# Protect names starting with `-'.
|
||||||
|
case $dst in
|
||||||
|
-*) dst=./$dst;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# If destination is a directory, append the input filename; won't work
|
||||||
|
# if double slashes aren't ignored.
|
||||||
|
if test -d "$dst"; then
|
||||||
|
if test -n "$no_target_directory"; then
|
||||||
|
echo "$0: $dst_arg: Is a directory" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
dstdir=$dst
|
||||||
|
dst=$dstdir/`basename "$src"`
|
||||||
|
dstdir_status=0
|
||||||
|
else
|
||||||
|
# Prefer dirname, but fall back on a substitute if dirname fails.
|
||||||
|
dstdir=`
|
||||||
|
(dirname "$dst") 2>/dev/null ||
|
||||||
|
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
||||||
|
X"$dst" : 'X\(//\)[^/]' \| \
|
||||||
|
X"$dst" : 'X\(//\)$' \| \
|
||||||
|
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
|
||||||
|
echo X"$dst" |
|
||||||
|
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
||||||
|
s//\1/
|
||||||
|
q
|
||||||
|
}
|
||||||
|
/^X\(\/\/\)[^/].*/{
|
||||||
|
s//\1/
|
||||||
|
q
|
||||||
|
}
|
||||||
|
/^X\(\/\/\)$/{
|
||||||
|
s//\1/
|
||||||
|
q
|
||||||
|
}
|
||||||
|
/^X\(\/\).*/{
|
||||||
|
s//\1/
|
||||||
|
q
|
||||||
|
}
|
||||||
|
s/.*/./; q'
|
||||||
|
`
|
||||||
|
|
||||||
|
test -d "$dstdir"
|
||||||
|
dstdir_status=$?
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
obsolete_mkdir_used=false
|
||||||
|
|
||||||
|
if test $dstdir_status != 0; then
|
||||||
|
case $posix_mkdir in
|
||||||
|
'')
|
||||||
|
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||||
|
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||||
|
umask=`umask`
|
||||||
|
case $stripcmd.$umask in
|
||||||
|
# Optimize common cases.
|
||||||
|
*[2367][2367]) mkdir_umask=$umask;;
|
||||||
|
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||||
|
|
||||||
|
*[0-7])
|
||||||
|
mkdir_umask=`expr $umask + 22 \
|
||||||
|
- $umask % 100 % 40 + $umask % 20 \
|
||||||
|
- $umask % 10 % 4 + $umask % 2
|
||||||
|
`;;
|
||||||
|
*) mkdir_umask=$umask,go-w;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# With -d, create the new directory with the user-specified mode.
|
||||||
|
# Otherwise, rely on $mkdir_umask.
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
mkdir_mode=-m$mode
|
||||||
|
else
|
||||||
|
mkdir_mode=
|
||||||
|
fi
|
||||||
|
|
||||||
|
posix_mkdir=false
|
||||||
|
case $umask in
|
||||||
|
*[123567][0-7][0-7])
|
||||||
|
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||||
|
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||||
|
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||||
|
|
||||||
|
if (umask $mkdir_umask &&
|
||||||
|
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
if test -z "$dir_arg" || {
|
||||||
|
# Check for POSIX incompatibilities with -m.
|
||||||
|
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||||
|
# other-writeable bit of parent directory when it shouldn't.
|
||||||
|
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||||
|
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||||
|
case $ls_ld_tmpdir in
|
||||||
|
d????-?r-*) different_mode=700;;
|
||||||
|
d????-?--*) different_mode=755;;
|
||||||
|
*) false;;
|
||||||
|
esac &&
|
||||||
|
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||||
|
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||||
|
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
then posix_mkdir=:
|
||||||
|
fi
|
||||||
|
rmdir "$tmpdir/d" "$tmpdir"
|
||||||
|
else
|
||||||
|
# Remove any dirs left behind by ancient mkdir implementations.
|
||||||
|
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||||
|
fi
|
||||||
|
trap '' 0;;
|
||||||
|
esac;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if
|
||||||
|
$posix_mkdir && (
|
||||||
|
umask $mkdir_umask &&
|
||||||
|
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||||
|
)
|
||||||
|
then :
|
||||||
|
else
|
||||||
|
|
||||||
|
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
||||||
|
# or it failed possibly due to a race condition. Create the
|
||||||
|
# directory the slow way, step by step, checking for races as we go.
|
||||||
|
|
||||||
|
case $dstdir in
|
||||||
|
/*) prefix='/';;
|
||||||
|
-*) prefix='./';;
|
||||||
|
*) prefix='';;
|
||||||
|
esac
|
||||||
|
|
||||||
|
eval "$initialize_posix_glob"
|
||||||
|
|
||||||
|
oIFS=$IFS
|
||||||
|
IFS=/
|
||||||
|
$posix_glob set -f
|
||||||
|
set fnord $dstdir
|
||||||
|
shift
|
||||||
|
$posix_glob set +f
|
||||||
|
IFS=$oIFS
|
||||||
|
|
||||||
|
prefixes=
|
||||||
|
|
||||||
|
for d
|
||||||
|
do
|
||||||
|
test -z "$d" && continue
|
||||||
|
|
||||||
|
prefix=$prefix$d
|
||||||
|
if test -d "$prefix"; then
|
||||||
|
prefixes=
|
||||||
|
else
|
||||||
|
if $posix_mkdir; then
|
||||||
|
(umask=$mkdir_umask &&
|
||||||
|
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||||
|
# Don't fail if two instances are running concurrently.
|
||||||
|
test -d "$prefix" || exit 1
|
||||||
|
else
|
||||||
|
case $prefix in
|
||||||
|
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||||
|
*) qprefix=$prefix;;
|
||||||
|
esac
|
||||||
|
prefixes="$prefixes '$qprefix'"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
prefix=$prefix/
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -n "$prefixes"; then
|
||||||
|
# Don't fail if two instances are running concurrently.
|
||||||
|
(umask $mkdir_umask &&
|
||||||
|
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||||
|
test -d "$dstdir" || exit 1
|
||||||
|
obsolete_mkdir_used=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||||
|
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||||
|
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||||
|
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||||
|
else
|
||||||
|
|
||||||
|
# Make a couple of temp file names in the proper directory.
|
||||||
|
dsttmp=$dstdir/_inst.$$_
|
||||||
|
rmtmp=$dstdir/_rm.$$_
|
||||||
|
|
||||||
|
# Trap to clean up those temp files at exit.
|
||||||
|
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||||
|
|
||||||
|
# Copy the file name to the temp name.
|
||||||
|
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
||||||
|
|
||||||
|
# and set any options; do chmod last to preserve setuid bits.
|
||||||
|
#
|
||||||
|
# If any of these fail, we abort the whole thing. If we want to
|
||||||
|
# ignore errors from any of these, just make sure not to ignore
|
||||||
|
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||||
|
#
|
||||||
|
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||||
|
|
||||||
|
# If -C, don't bother to copy if it wouldn't change the file.
|
||||||
|
if $copy_on_change &&
|
||||||
|
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||||
|
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||||
|
|
||||||
|
eval "$initialize_posix_glob" &&
|
||||||
|
$posix_glob set -f &&
|
||||||
|
set X $old && old=:$2:$4:$5:$6 &&
|
||||||
|
set X $new && new=:$2:$4:$5:$6 &&
|
||||||
|
$posix_glob set +f &&
|
||||||
|
|
||||||
|
test "$old" = "$new" &&
|
||||||
|
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
rm -f "$dsttmp"
|
||||||
|
else
|
||||||
|
# Rename the file to the real destination.
|
||||||
|
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||||
|
|
||||||
|
# The rename failed, perhaps because mv can't rename something else
|
||||||
|
# to itself, or perhaps because mv is so ancient that it does not
|
||||||
|
# support -f.
|
||||||
|
{
|
||||||
|
# Now remove or move aside any old file at destination location.
|
||||||
|
# We try this two ways since rm can't unlink itself on some
|
||||||
|
# systems and the destination file might be busy for other
|
||||||
|
# reasons. In this case, the final cleanup might fail but the new
|
||||||
|
# file should still install successfully.
|
||||||
|
{
|
||||||
|
test ! -f "$dst" ||
|
||||||
|
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||||
|
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||||
|
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||||
|
} ||
|
||||||
|
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||||
|
(exit 1); exit 1
|
||||||
|
}
|
||||||
|
} &&
|
||||||
|
|
||||||
|
# Now rename the file to the real destination.
|
||||||
|
$doit $mvcmd "$dsttmp" "$dst"
|
||||||
|
}
|
||||||
|
fi || exit 1
|
||||||
|
|
||||||
|
trap '' 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Local variables:
|
||||||
|
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-end: "$"
|
||||||
|
# End:
|
45
libqpdf/BitStream.cc
Normal file
45
libqpdf/BitStream.cc
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include <qpdf/BitStream.hh>
|
||||||
|
|
||||||
|
// See comments in bits.cc
|
||||||
|
#define BITS_READ 1
|
||||||
|
#include "bits.icc"
|
||||||
|
|
||||||
|
BitStream::BitStream(unsigned char const* p, int nbytes) :
|
||||||
|
start(p),
|
||||||
|
nbytes(nbytes)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BitStream::reset()
|
||||||
|
{
|
||||||
|
p = start;
|
||||||
|
bit_offset = 7;
|
||||||
|
bits_available = 8 * nbytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
BitStream::getBits(int nbits)
|
||||||
|
{
|
||||||
|
return read_bits(this->p, this->bit_offset,
|
||||||
|
this->bits_available, nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BitStream::skipToNextByte()
|
||||||
|
{
|
||||||
|
if (bit_offset != 7)
|
||||||
|
{
|
||||||
|
unsigned int bits_to_skip = bit_offset + 1;
|
||||||
|
if (bits_available < bits_to_skip)
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("overflow skipping to next byte in bitstream");
|
||||||
|
}
|
||||||
|
bit_offset = 7;
|
||||||
|
++p;
|
||||||
|
bits_available -= bits_to_skip;
|
||||||
|
}
|
||||||
|
}
|
30
libqpdf/BitWriter.cc
Normal file
30
libqpdf/BitWriter.cc
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include <qpdf/BitWriter.hh>
|
||||||
|
|
||||||
|
// See comments in bits.cc
|
||||||
|
#define BITS_WRITE 1
|
||||||
|
#include "bits.icc"
|
||||||
|
|
||||||
|
BitWriter::BitWriter(Pipeline* pl) :
|
||||||
|
pl(pl),
|
||||||
|
ch(0),
|
||||||
|
bit_offset(7)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BitWriter::writeBits(unsigned long val, int bits)
|
||||||
|
{
|
||||||
|
write_bits(this->ch, this->bit_offset, val, bits, this->pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BitWriter::flush()
|
||||||
|
{
|
||||||
|
if (bit_offset < 7)
|
||||||
|
{
|
||||||
|
int bits_to_write = bit_offset + 1;
|
||||||
|
write_bits(this->ch, this->bit_offset, 0, bits_to_write, this->pl);
|
||||||
|
}
|
||||||
|
}
|
79
libqpdf/Buffer.cc
Normal file
79
libqpdf/Buffer.cc
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
|
||||||
|
#include <qpdf/Buffer.hh>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
Buffer::Buffer()
|
||||||
|
{
|
||||||
|
init(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::Buffer(unsigned long size)
|
||||||
|
{
|
||||||
|
init(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::Buffer(Buffer const& rhs)
|
||||||
|
{
|
||||||
|
init(0);
|
||||||
|
copy(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer&
|
||||||
|
Buffer::operator=(Buffer const& rhs)
|
||||||
|
{
|
||||||
|
copy(rhs);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::~Buffer()
|
||||||
|
{
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Buffer::init(unsigned long size)
|
||||||
|
{
|
||||||
|
this->size = size;
|
||||||
|
this->buf = (size ? new unsigned char[size] : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Buffer::copy(Buffer const& rhs)
|
||||||
|
{
|
||||||
|
if (this != &rhs)
|
||||||
|
{
|
||||||
|
this->destroy();
|
||||||
|
this->init(rhs.size);
|
||||||
|
if (this->size)
|
||||||
|
{
|
||||||
|
memcpy(this->buf, rhs.buf, this->size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Buffer::destroy()
|
||||||
|
{
|
||||||
|
delete [] this->buf;
|
||||||
|
this->size = 0;
|
||||||
|
this->buf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
Buffer::getSize() const
|
||||||
|
{
|
||||||
|
return this->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char const*
|
||||||
|
Buffer::getBuffer() const
|
||||||
|
{
|
||||||
|
return this->buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char*
|
||||||
|
Buffer::getBuffer()
|
||||||
|
{
|
||||||
|
return this->buf;
|
||||||
|
}
|
441
libqpdf/MD5.cc
Normal file
441
libqpdf/MD5.cc
Normal file
@ -0,0 +1,441 @@
|
|||||||
|
// This file implements a class for computation of MD5 checksums.
|
||||||
|
// It is derived from the reference algorithm for MD5 as given in
|
||||||
|
// RFC 1321. The original copyright notice is as follows:
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||||
|
// rights reserved.
|
||||||
|
//
|
||||||
|
// License to copy and use this software is granted provided that it
|
||||||
|
// is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||||
|
// Algorithm" in all material mentioning or referencing this software
|
||||||
|
// or this function.
|
||||||
|
//
|
||||||
|
// License is also granted to make and use derivative works provided
|
||||||
|
// that such works are identified as "derived from the RSA Data
|
||||||
|
// Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||||
|
// mentioning or referencing the derived work.
|
||||||
|
//
|
||||||
|
// RSA Data Security, Inc. makes no representations concerning either
|
||||||
|
// the merchantability of this software or the suitability of this
|
||||||
|
// software for any particular purpose. It is provided "as is"
|
||||||
|
// without express or implied warranty of any kind.
|
||||||
|
//
|
||||||
|
// These notices must be retained in any copies of any part of this
|
||||||
|
// documentation and/or software.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <qpdf/MD5.hh>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
int const S11 = 7;
|
||||||
|
int const S12 = 12;
|
||||||
|
int const S13 = 17;
|
||||||
|
int const S14 = 22;
|
||||||
|
int const S21 = 5;
|
||||||
|
int const S22 = 9;
|
||||||
|
int const S23 = 14;
|
||||||
|
int const S24 = 20;
|
||||||
|
int const S31 = 4;
|
||||||
|
int const S32 = 11;
|
||||||
|
int const S33 = 16;
|
||||||
|
int const S34 = 23;
|
||||||
|
int const S41 = 6;
|
||||||
|
int const S42 = 10;
|
||||||
|
int const S43 = 15;
|
||||||
|
int const S44 = 21;
|
||||||
|
|
||||||
|
static unsigned char PADDING[64] = {
|
||||||
|
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// F, G, H and I are basic MD5 functions.
|
||||||
|
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
|
||||||
|
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
|
||||||
|
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||||
|
#define I(x, y, z) ((y) ^ ((x) | (~z)))
|
||||||
|
|
||||||
|
// ROTATE_LEFT rotates x left n bits.
|
||||||
|
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
|
||||||
|
|
||||||
|
// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
||||||
|
// Rotation is separate from addition to prevent recomputation.
|
||||||
|
#define FF(a, b, c, d, x, s, ac) { \
|
||||||
|
(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||||
|
(a) = ROTATE_LEFT ((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
#define GG(a, b, c, d, x, s, ac) { \
|
||||||
|
(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||||
|
(a) = ROTATE_LEFT ((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
#define HH(a, b, c, d, x, s, ac) { \
|
||||||
|
(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||||
|
(a) = ROTATE_LEFT ((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
#define II(a, b, c, d, x, s, ac) { \
|
||||||
|
(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||||
|
(a) = ROTATE_LEFT ((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// MD5 initialization. Begins an MD5 operation, writing a new context.
|
||||||
|
void MD5::init()
|
||||||
|
{
|
||||||
|
count[0] = count[1] = 0;
|
||||||
|
// Load magic initialization constants.
|
||||||
|
state[0] = 0x67452301;
|
||||||
|
state[1] = 0xefcdab89;
|
||||||
|
state[2] = 0x98badcfe;
|
||||||
|
state[3] = 0x10325476;
|
||||||
|
|
||||||
|
finalized = false;
|
||||||
|
memset(digest_val, 0, sizeof(digest_val));
|
||||||
|
}
|
||||||
|
|
||||||
|
// MD5 block update operation. Continues an MD5 message-digest
|
||||||
|
// operation, processing another message block, and updating the
|
||||||
|
// context.
|
||||||
|
|
||||||
|
void MD5::update(unsigned char *input,
|
||||||
|
unsigned int inputLen)
|
||||||
|
{
|
||||||
|
unsigned int i, index, partLen;
|
||||||
|
|
||||||
|
// Compute number of bytes mod 64
|
||||||
|
index = (unsigned int)((count[0] >> 3) & 0x3F);
|
||||||
|
|
||||||
|
// Update number of bits
|
||||||
|
if ((count[0] += ((UINT4)inputLen << 3))
|
||||||
|
< ((UINT4)inputLen << 3))
|
||||||
|
count[1]++;
|
||||||
|
count[1] += ((UINT4)inputLen >> 29);
|
||||||
|
|
||||||
|
partLen = 64 - index;
|
||||||
|
|
||||||
|
// Transform as many times as possible.
|
||||||
|
|
||||||
|
if (inputLen >= partLen) {
|
||||||
|
memcpy
|
||||||
|
((POINTER)&buffer[index], (POINTER)input, partLen);
|
||||||
|
transform(state, buffer);
|
||||||
|
|
||||||
|
for (i = partLen; i + 63 < inputLen; i += 64)
|
||||||
|
transform(state, &input[i]);
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
// Buffer remaining input
|
||||||
|
memcpy
|
||||||
|
((POINTER)&buffer[index], (POINTER)&input[i],
|
||||||
|
inputLen-i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MD5 finalization. Ends an MD5 message-digest operation, writing the
|
||||||
|
// the message digest and zeroizing the context.
|
||||||
|
void MD5::final()
|
||||||
|
{
|
||||||
|
if (finalized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char bits[8];
|
||||||
|
unsigned int index, padLen;
|
||||||
|
|
||||||
|
// Save number of bits
|
||||||
|
encode(bits, count, 8);
|
||||||
|
|
||||||
|
// Pad out to 56 mod 64.
|
||||||
|
|
||||||
|
index = (unsigned int)((count[0] >> 3) & 0x3f);
|
||||||
|
padLen = (index < 56) ? (56 - index) : (120 - index);
|
||||||
|
update(PADDING, padLen);
|
||||||
|
|
||||||
|
// Append length (before padding)
|
||||||
|
update(bits, 8);
|
||||||
|
// Store state in digest_val
|
||||||
|
encode(digest_val, state, 16);
|
||||||
|
|
||||||
|
// Zeroize sensitive information.
|
||||||
|
memset(state, 0, sizeof(state));
|
||||||
|
memset(count, 0, sizeof(count));
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
|
||||||
|
finalized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MD5 basic transformation. Transforms state based on block.
|
||||||
|
void MD5::transform(UINT4 state[4], unsigned char block[64])
|
||||||
|
{
|
||||||
|
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
||||||
|
|
||||||
|
decode(x, block, 64);
|
||||||
|
|
||||||
|
// Round 1
|
||||||
|
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); // 1
|
||||||
|
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); // 2
|
||||||
|
FF (c, d, a, b, x[ 2], S13, 0x242070db); // 3
|
||||||
|
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); // 4
|
||||||
|
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); // 5
|
||||||
|
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); // 6
|
||||||
|
FF (c, d, a, b, x[ 6], S13, 0xa8304613); // 7
|
||||||
|
FF (b, c, d, a, x[ 7], S14, 0xfd469501); // 8
|
||||||
|
FF (a, b, c, d, x[ 8], S11, 0x698098d8); // 9
|
||||||
|
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); // 10
|
||||||
|
FF (c, d, a, b, x[10], S13, 0xffff5bb1); // 11
|
||||||
|
FF (b, c, d, a, x[11], S14, 0x895cd7be); // 12
|
||||||
|
FF (a, b, c, d, x[12], S11, 0x6b901122); // 13
|
||||||
|
FF (d, a, b, c, x[13], S12, 0xfd987193); // 14
|
||||||
|
FF (c, d, a, b, x[14], S13, 0xa679438e); // 15
|
||||||
|
FF (b, c, d, a, x[15], S14, 0x49b40821); // 16
|
||||||
|
|
||||||
|
// Round 2
|
||||||
|
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); // 17
|
||||||
|
GG (d, a, b, c, x[ 6], S22, 0xc040b340); // 18
|
||||||
|
GG (c, d, a, b, x[11], S23, 0x265e5a51); // 19
|
||||||
|
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); // 20
|
||||||
|
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); // 21
|
||||||
|
GG (d, a, b, c, x[10], S22, 0x2441453); // 22
|
||||||
|
GG (c, d, a, b, x[15], S23, 0xd8a1e681); // 23
|
||||||
|
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); // 24
|
||||||
|
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); // 25
|
||||||
|
GG (d, a, b, c, x[14], S22, 0xc33707d6); // 26
|
||||||
|
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); // 27
|
||||||
|
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); // 28
|
||||||
|
GG (a, b, c, d, x[13], S21, 0xa9e3e905); // 29
|
||||||
|
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); // 30
|
||||||
|
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); // 31
|
||||||
|
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); // 32
|
||||||
|
|
||||||
|
// Round 3
|
||||||
|
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); // 33
|
||||||
|
HH (d, a, b, c, x[ 8], S32, 0x8771f681); // 34
|
||||||
|
HH (c, d, a, b, x[11], S33, 0x6d9d6122); // 35
|
||||||
|
HH (b, c, d, a, x[14], S34, 0xfde5380c); // 36
|
||||||
|
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); // 37
|
||||||
|
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); // 38
|
||||||
|
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); // 39
|
||||||
|
HH (b, c, d, a, x[10], S34, 0xbebfbc70); // 40
|
||||||
|
HH (a, b, c, d, x[13], S31, 0x289b7ec6); // 41
|
||||||
|
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); // 42
|
||||||
|
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); // 43
|
||||||
|
HH (b, c, d, a, x[ 6], S34, 0x4881d05); // 44
|
||||||
|
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); // 45
|
||||||
|
HH (d, a, b, c, x[12], S32, 0xe6db99e5); // 46
|
||||||
|
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); // 47
|
||||||
|
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); // 48
|
||||||
|
|
||||||
|
// Round 4
|
||||||
|
II (a, b, c, d, x[ 0], S41, 0xf4292244); // 49
|
||||||
|
II (d, a, b, c, x[ 7], S42, 0x432aff97); // 50
|
||||||
|
II (c, d, a, b, x[14], S43, 0xab9423a7); // 51
|
||||||
|
II (b, c, d, a, x[ 5], S44, 0xfc93a039); // 52
|
||||||
|
II (a, b, c, d, x[12], S41, 0x655b59c3); // 53
|
||||||
|
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); // 54
|
||||||
|
II (c, d, a, b, x[10], S43, 0xffeff47d); // 55
|
||||||
|
II (b, c, d, a, x[ 1], S44, 0x85845dd1); // 56
|
||||||
|
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); // 57
|
||||||
|
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); // 58
|
||||||
|
II (c, d, a, b, x[ 6], S43, 0xa3014314); // 59
|
||||||
|
II (b, c, d, a, x[13], S44, 0x4e0811a1); // 60
|
||||||
|
II (a, b, c, d, x[ 4], S41, 0xf7537e82); // 61
|
||||||
|
II (d, a, b, c, x[11], S42, 0xbd3af235); // 62
|
||||||
|
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); // 63
|
||||||
|
II (b, c, d, a, x[ 9], S44, 0xeb86d391); // 64
|
||||||
|
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
|
||||||
|
// Zeroize sensitive information.
|
||||||
|
|
||||||
|
memset ((POINTER)x, 0, sizeof (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encodes input (UINT4) into output (unsigned char). Assumes len is a
|
||||||
|
// multiple of 4.
|
||||||
|
void MD5::encode(unsigned char *output, UINT4 *input, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
for (i = 0, j = 0; j < len; i++, j += 4) {
|
||||||
|
output[j] = (unsigned char)(input[i] & 0xff);
|
||||||
|
output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
|
||||||
|
output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
|
||||||
|
output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decodes input (unsigned char) into output (UINT4). Assumes len is a
|
||||||
|
// multiple of 4.
|
||||||
|
void MD5::decode(UINT4 *output, unsigned char *input, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
for (i = 0, j = 0; j < len; i++, j += 4)
|
||||||
|
output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
|
||||||
|
(((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public functions
|
||||||
|
|
||||||
|
MD5::MD5()
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5::reset()
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5::encodeString(char const* str)
|
||||||
|
{
|
||||||
|
unsigned int len = strlen(str);
|
||||||
|
|
||||||
|
update((unsigned char *)str, len);
|
||||||
|
final();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5::appendString(char const* input_string)
|
||||||
|
{
|
||||||
|
update((unsigned char *)input_string, strlen(input_string));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5::encodeDataIncrementally(char const* data, int len)
|
||||||
|
{
|
||||||
|
update((unsigned char *)data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5::encodeFile(char const *filename, int up_to_size)
|
||||||
|
throw (QEXC::System)
|
||||||
|
{
|
||||||
|
FILE *file;
|
||||||
|
unsigned char buffer[1024];
|
||||||
|
|
||||||
|
if ((file = fopen (filename, "rb")) == NULL)
|
||||||
|
{
|
||||||
|
throw QEXC::System(std::string("MD5: can't open ") + filename, errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
int len;
|
||||||
|
int so_far = 0;
|
||||||
|
int to_try = 1024;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if ((up_to_size >= 0) && ((so_far + to_try) > up_to_size))
|
||||||
|
{
|
||||||
|
to_try = up_to_size - so_far;
|
||||||
|
}
|
||||||
|
len = fread(buffer, 1, to_try, file);
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
update(buffer, len);
|
||||||
|
so_far += len;
|
||||||
|
if ((up_to_size >= 0) && (so_far >= up_to_size))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (len > 0);
|
||||||
|
if (ferror(file))
|
||||||
|
{
|
||||||
|
// Assume, perhaps incorrectly, that errno was set by the
|
||||||
|
// underlying call to read....
|
||||||
|
(void) fclose(file);
|
||||||
|
throw QEXC::System(std::string("MD5: read error on ") + filename, errno);
|
||||||
|
}
|
||||||
|
(void) fclose(file);
|
||||||
|
|
||||||
|
final();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5::digest(Digest result)
|
||||||
|
{
|
||||||
|
final();
|
||||||
|
memcpy(result, digest_val, sizeof(digest_val));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5::print()
|
||||||
|
{
|
||||||
|
final();
|
||||||
|
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
printf("%02x", digest_val[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MD5::unparse()
|
||||||
|
{
|
||||||
|
final();
|
||||||
|
|
||||||
|
char result[33];
|
||||||
|
char* p = result;
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
sprintf(p, "%02x", digest_val[i]);
|
||||||
|
p += 2;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
MD5::getDataChecksum(char const* buf, int len)
|
||||||
|
{
|
||||||
|
MD5 m;
|
||||||
|
m.encodeDataIncrementally(buf, len);
|
||||||
|
return m.unparse();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
MD5::getFileChecksum(char const* filename, int up_to_size)
|
||||||
|
{
|
||||||
|
MD5 m;
|
||||||
|
m.encodeFile(filename, up_to_size);
|
||||||
|
return m.unparse();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MD5::checkDataChecksum(char const* const checksum,
|
||||||
|
char const* buf, int len)
|
||||||
|
{
|
||||||
|
std::string actual_checksum = getDataChecksum(buf, len);
|
||||||
|
return (checksum == actual_checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MD5::checkFileChecksum(char const* const checksum,
|
||||||
|
char const* filename, int up_to_size)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::string actual_checksum = getFileChecksum(filename, up_to_size);
|
||||||
|
result = (checksum == actual_checksum);
|
||||||
|
}
|
||||||
|
catch (QEXC::System)
|
||||||
|
{
|
||||||
|
// Ignore -- return false
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
1
libqpdf/Makefile
Normal file
1
libqpdf/Makefile
Normal file
@ -0,0 +1 @@
|
|||||||
|
include ../make/proxy.mk
|
365
libqpdf/PCRE.cc
Normal file
365
libqpdf/PCRE.cc
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include <qpdf/PCRE.hh>
|
||||||
|
#include <qpdf/QUtil.hh>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
PCRE::Exception::Exception(std::string const& message)
|
||||||
|
{
|
||||||
|
this->setMessage("PCRE error: " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
PCRE::NoBackref::NoBackref() :
|
||||||
|
Exception("no match")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PCRE::Match::Match(int nbackrefs, char const* subject)
|
||||||
|
{
|
||||||
|
this->init(-1, nbackrefs, subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
PCRE::Match::~Match()
|
||||||
|
{
|
||||||
|
this->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
PCRE::Match::Match(Match const& rhs)
|
||||||
|
{
|
||||||
|
this->copy(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
PCRE::Match&
|
||||||
|
PCRE::Match::operator=(Match const& rhs)
|
||||||
|
{
|
||||||
|
if (this != &rhs)
|
||||||
|
{
|
||||||
|
this->destroy();
|
||||||
|
this->copy(rhs);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PCRE::Match::init(int nmatches, int nbackrefs, char const* subject)
|
||||||
|
{
|
||||||
|
this->nmatches = nmatches;
|
||||||
|
this->nbackrefs = nbackrefs;
|
||||||
|
this->subject = subject;
|
||||||
|
this->ovecsize = 3 * (1 + nbackrefs);
|
||||||
|
this->ovector = 0;
|
||||||
|
if (this->ovecsize)
|
||||||
|
{
|
||||||
|
this->ovector = new int[this->ovecsize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PCRE::Match::copy(Match const& rhs)
|
||||||
|
{
|
||||||
|
this->init(rhs.nmatches, rhs.nbackrefs, rhs.subject);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < this->ovecsize; ++i)
|
||||||
|
{
|
||||||
|
this->ovector[i] = rhs.ovector[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PCRE::Match::destroy()
|
||||||
|
{
|
||||||
|
delete [] this->ovector;
|
||||||
|
}
|
||||||
|
|
||||||
|
PCRE::Match::operator bool()
|
||||||
|
{
|
||||||
|
return (this->nmatches >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
PCRE::Match::getMatch(int n, int flags)
|
||||||
|
throw(QEXC::General, Exception)
|
||||||
|
{
|
||||||
|
// This method used to be implemented in terms of
|
||||||
|
// pcre_get_substring, but that function gives you an empty string
|
||||||
|
// for an unmatched backreference that is in range.
|
||||||
|
|
||||||
|
int offset;
|
||||||
|
int length;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getOffsetLength(n, offset, length);
|
||||||
|
}
|
||||||
|
catch (NoBackref&)
|
||||||
|
{
|
||||||
|
if (flags & gm_no_substring_returns_empty)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string(this->subject).substr(offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PCRE::Match::getOffsetLength(int n, int& offset, int& length) throw(Exception)
|
||||||
|
{
|
||||||
|
if ((this->nmatches < 0) ||
|
||||||
|
(n > this->nmatches - 1) ||
|
||||||
|
(this->ovector[n * 2] == -1))
|
||||||
|
{
|
||||||
|
throw NoBackref();
|
||||||
|
}
|
||||||
|
offset = this->ovector[n * 2];
|
||||||
|
length = this->ovector[n * 2 + 1] - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PCRE::Match::getOffset(int n) throw(Exception)
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
int length;
|
||||||
|
this->getOffsetLength(n, offset, length);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PCRE::Match::getLength(int n) throw(Exception)
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
int length;
|
||||||
|
this->getOffsetLength(n, offset, length);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
PCRE::Match::nMatches() const
|
||||||
|
{
|
||||||
|
return this->nmatches;
|
||||||
|
}
|
||||||
|
|
||||||
|
PCRE::PCRE(char const* pattern, int options) throw (Exception)
|
||||||
|
{
|
||||||
|
char const *errptr;
|
||||||
|
int erroffset;
|
||||||
|
this->code = pcre_compile(pattern, options, &errptr, &erroffset, 0);
|
||||||
|
if (this->code)
|
||||||
|
{
|
||||||
|
this->nbackrefs = pcre_info(this->code, 0, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string message = (std::string("compilation of ") + pattern +
|
||||||
|
" failed at offset " +
|
||||||
|
QUtil::int_to_string(erroffset) + ": " +
|
||||||
|
errptr);
|
||||||
|
throw Exception(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PCRE::~PCRE()
|
||||||
|
{
|
||||||
|
pcre_free(this->code);
|
||||||
|
}
|
||||||
|
|
||||||
|
PCRE::Match
|
||||||
|
PCRE::match(char const* subject, int options, int startoffset, int size)
|
||||||
|
throw (QEXC::General, Exception)
|
||||||
|
{
|
||||||
|
if (size == -1)
|
||||||
|
{
|
||||||
|
size = strlen(subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
Match result(this->nbackrefs, subject);
|
||||||
|
int status = pcre_exec(this->code, 0, subject, size,
|
||||||
|
startoffset, options,
|
||||||
|
result.ovector, result.ovecsize);
|
||||||
|
if (status >= 0)
|
||||||
|
{
|
||||||
|
result.nmatches = status;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string message;
|
||||||
|
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case PCRE_ERROR_NOMATCH:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCRE_ERROR_BADOPTION:
|
||||||
|
message = "bad option passed to PCRE::match()";
|
||||||
|
throw Exception(message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCRE_ERROR_NOMEMORY:
|
||||||
|
message = "insufficient memory";
|
||||||
|
throw Exception(message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PCRE_ERROR_NULL:
|
||||||
|
case PCRE_ERROR_BADMAGIC:
|
||||||
|
case PCRE_ERROR_UNKNOWN_NODE:
|
||||||
|
default:
|
||||||
|
message = "pcre_exec returned " + QUtil::int_to_string(status);
|
||||||
|
throw QEXC::Internal(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PCRE::test(int n)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (n == 1)
|
||||||
|
{
|
||||||
|
static char const* utf8 = "abπdefq";
|
||||||
|
PCRE u1("^([[:alpha:]]+)");
|
||||||
|
PCRE u2("^([\\p{L}]+)", PCRE_UTF8);
|
||||||
|
PCRE::Match m1 = u1.match(utf8);
|
||||||
|
if (m1)
|
||||||
|
{
|
||||||
|
std::cout << "no utf8: " << m1.getMatch(1) << std::endl;
|
||||||
|
}
|
||||||
|
PCRE::Match m2 = u2.match(utf8);
|
||||||
|
if (m2)
|
||||||
|
{
|
||||||
|
std::cout << "utf8: " << m2.getMatch(1) << std::endl;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PCRE pcre1("a**");
|
||||||
|
}
|
||||||
|
catch (Exception& e)
|
||||||
|
{
|
||||||
|
std::cout << e.unparse() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
PCRE pcre2("^([^\\s:]*)\\s*:\\s*(.*?)\\s*$");
|
||||||
|
PCRE::Match m2 = pcre2.match("key: value one two three ");
|
||||||
|
if (m2)
|
||||||
|
{
|
||||||
|
std::cout << m2.nMatches() << std::endl;
|
||||||
|
std::cout << m2.getMatch(0) << std::endl;
|
||||||
|
std::cout << m2.getOffset(0) << std::endl;
|
||||||
|
std::cout << m2.getLength(0) << std::endl;
|
||||||
|
std::cout << m2.getMatch(1) << std::endl;
|
||||||
|
std::cout << m2.getOffset(1) << std::endl;
|
||||||
|
std::cout << m2.getLength(1) << std::endl;
|
||||||
|
std::cout << m2.getMatch(2) << std::endl;
|
||||||
|
std::cout << m2.getOffset(2) << std::endl;
|
||||||
|
std::cout << m2.getLength(2) << std::endl;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::cout << m2.getMatch(3) << std::endl;
|
||||||
|
}
|
||||||
|
catch (Exception& e)
|
||||||
|
{
|
||||||
|
std::cout << e.unparse() << std::endl;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::cout << m2.getOffset(3) << std::endl;
|
||||||
|
}
|
||||||
|
catch (Exception& e)
|
||||||
|
{
|
||||||
|
std::cout << e.unparse() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PCRE pcre3("^(a+)(b+)?$");
|
||||||
|
PCRE::Match m3 = pcre3.match("aaa");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (m3)
|
||||||
|
{
|
||||||
|
std::cout << m3.nMatches() << std::endl;
|
||||||
|
std::cout << m3.getMatch(0) << std::endl;
|
||||||
|
std::cout << m3.getMatch(1) << std::endl;
|
||||||
|
std::cout << "-"
|
||||||
|
<< m3.getMatch(
|
||||||
|
2, Match::gm_no_substring_returns_empty)
|
||||||
|
<< "-" << std::endl;
|
||||||
|
std::cout << "hello" << std::endl;
|
||||||
|
std::cout << m3.getMatch(2) << std::endl;
|
||||||
|
std::cout << "can't see this" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception& e)
|
||||||
|
{
|
||||||
|
std::cout << e.unparse() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// backref: 1 2 3 4 5
|
||||||
|
PCRE pcre4("^((?:(a(b)?)(?:,(c))?)|(c))?$");
|
||||||
|
static char const* candidates[] = {
|
||||||
|
"qqqcqqq", // no match
|
||||||
|
"ab,c", // backrefs: 0, 1, 2, 3, 4
|
||||||
|
"ab", // backrefs: 0, 1, 2, 3
|
||||||
|
"a", // backrefs: 0, 1, 2
|
||||||
|
"a,c", // backrefs: 0, 1, 2, 4
|
||||||
|
"c", // backrefs: 0, 1, 5
|
||||||
|
"", // backrefs: 0
|
||||||
|
0
|
||||||
|
};
|
||||||
|
for (char const** p = candidates; *p; ++p)
|
||||||
|
{
|
||||||
|
PCRE::Match m(pcre4.match(*p));
|
||||||
|
if (m)
|
||||||
|
{
|
||||||
|
int nmatches = m.nMatches();
|
||||||
|
for (int i = 0; i < nmatches; ++i)
|
||||||
|
{
|
||||||
|
std::cout << *p << ": " << i << ": ";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::string match = m.getMatch(i);
|
||||||
|
std::cout << match;
|
||||||
|
}
|
||||||
|
catch (NoBackref&)
|
||||||
|
{
|
||||||
|
std::cout << "no backref (getMatch)";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << *p << ": " << i << ": ";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
int length;
|
||||||
|
m.getOffsetLength(i, offset, length);
|
||||||
|
std::cout << offset << ", " << length;
|
||||||
|
}
|
||||||
|
catch (NoBackref&)
|
||||||
|
{
|
||||||
|
std::cout << "no backref (getOffsetLength)";
|
||||||
|
}
|
||||||
|
std:: cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << *p << ": no match" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (QEXC::General& e)
|
||||||
|
{
|
||||||
|
std::cout << "unexpected exception: " << e.unparse() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
25
libqpdf/Pipeline.cc
Normal file
25
libqpdf/Pipeline.cc
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include <qpdf/Pipeline.hh>
|
||||||
|
|
||||||
|
Pipeline::Pipeline(char const* identifier, Pipeline* next) :
|
||||||
|
identifier(identifier),
|
||||||
|
next(next)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline::~Pipeline()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline*
|
||||||
|
Pipeline::getNext(bool allow_null)
|
||||||
|
{
|
||||||
|
if ((next == 0) && (! allow_null))
|
||||||
|
{
|
||||||
|
throw Exception(
|
||||||
|
this->identifier +
|
||||||
|
": Pipeline::getNext() called on pipeline with no next");
|
||||||
|
}
|
||||||
|
return this->next;
|
||||||
|
}
|
131
libqpdf/Pl_ASCII85Decoder.cc
Normal file
131
libqpdf/Pl_ASCII85Decoder.cc
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#include <qpdf/Pl_ASCII85Decoder.hh>
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
#include <qpdf/QTC.hh>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
Pl_ASCII85Decoder::Pl_ASCII85Decoder(char const* identifier, Pipeline* next) :
|
||||||
|
Pipeline(identifier, next),
|
||||||
|
pos(0),
|
||||||
|
eod(0)
|
||||||
|
{
|
||||||
|
memset(this->inbuf, 117, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pl_ASCII85Decoder::~Pl_ASCII85Decoder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_ASCII85Decoder::write(unsigned char* buf, int len)
|
||||||
|
{
|
||||||
|
if (eod > 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
if (eod > 1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (eod == 1)
|
||||||
|
{
|
||||||
|
if (buf[i] == '>')
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
eod = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw QEXC::General(
|
||||||
|
"broken end-of-data sequence in base 85 data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (buf[i])
|
||||||
|
{
|
||||||
|
case ' ':
|
||||||
|
case '\f':
|
||||||
|
case '\v':
|
||||||
|
case '\t':
|
||||||
|
case '\r':
|
||||||
|
case '\n':
|
||||||
|
QTC::TC("libtests", "Pl_ASCII85Decoder ignore space");
|
||||||
|
// ignore whitespace
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '~':
|
||||||
|
eod = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'z':
|
||||||
|
if (pos != 0)
|
||||||
|
{
|
||||||
|
throw QEXC::General(
|
||||||
|
"unexpected z during base 85 decode");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QTC::TC("libtests", "Pl_ASCII85Decoder read z");
|
||||||
|
getNext()->write((unsigned char*)"\000\000\000\000", 4);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if ((buf[i] < 33) || (buf[i] > 117))
|
||||||
|
{
|
||||||
|
throw QEXC::General
|
||||||
|
("character out of range during base 85 decode");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->inbuf[this->pos++] = buf[i];
|
||||||
|
if (pos == 5)
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_ASCII85Decoder::flush()
|
||||||
|
{
|
||||||
|
if (this->pos == 0)
|
||||||
|
{
|
||||||
|
QTC::TC("libtests", "Pl_ASCII85Decoder no-op flush");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned long lval = 0;
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
lval *= 85;
|
||||||
|
lval += (this->inbuf[i] - 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char outbuf[4];
|
||||||
|
memset(outbuf, 0, 4);
|
||||||
|
for (int i = 3; i >= 0; --i)
|
||||||
|
{
|
||||||
|
outbuf[i] = lval & 0xff;
|
||||||
|
lval >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTC::TC("libtests", "Pl_ASCII85Decoder partial flush",
|
||||||
|
(this->pos == 5) ? 0 : 1);
|
||||||
|
getNext()->write(outbuf, this->pos - 1);
|
||||||
|
|
||||||
|
this->pos = 0;
|
||||||
|
memset(this->inbuf, 117, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_ASCII85Decoder::finish()
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
getNext()->finish();
|
||||||
|
}
|
108
libqpdf/Pl_ASCIIHexDecoder.cc
Normal file
108
libqpdf/Pl_ASCIIHexDecoder.cc
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include <qpdf/Pl_ASCIIHexDecoder.hh>
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
#include <qpdf/QTC.hh>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
Pl_ASCIIHexDecoder::Pl_ASCIIHexDecoder(char const* identifier, Pipeline* next) :
|
||||||
|
Pipeline(identifier, next),
|
||||||
|
pos(0),
|
||||||
|
eod(false)
|
||||||
|
{
|
||||||
|
strcpy(this->inbuf, "00");
|
||||||
|
}
|
||||||
|
|
||||||
|
Pl_ASCIIHexDecoder::~Pl_ASCIIHexDecoder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_ASCIIHexDecoder::write(unsigned char* buf, int len)
|
||||||
|
{
|
||||||
|
if (this->eod)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
char ch = toupper(buf[i]);
|
||||||
|
switch (ch)
|
||||||
|
{
|
||||||
|
case ' ':
|
||||||
|
case '\f':
|
||||||
|
case '\v':
|
||||||
|
case '\t':
|
||||||
|
case '\r':
|
||||||
|
case '\n':
|
||||||
|
QTC::TC("libtests", "Pl_ASCIIHexDecoder ignore space");
|
||||||
|
// ignore whitespace
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '>':
|
||||||
|
this->eod = true;
|
||||||
|
flush();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (((ch >= '0') && (ch <= '9')) ||
|
||||||
|
((ch >= 'A') && (ch <= 'F')))
|
||||||
|
{
|
||||||
|
this->inbuf[this->pos++] = ch;
|
||||||
|
if (this->pos == 2)
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char t[2];
|
||||||
|
t[0] = ch;
|
||||||
|
t[1] = 0;
|
||||||
|
throw QEXC::General(
|
||||||
|
std::string("character out of range during base Hex decode: ") + t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (this->eod)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_ASCIIHexDecoder::flush()
|
||||||
|
{
|
||||||
|
if (this->pos == 0)
|
||||||
|
{
|
||||||
|
QTC::TC("libtests", "Pl_ASCIIHexDecoder no-op flush");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int b[2];
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
if (this->inbuf[i] >= 'A')
|
||||||
|
{
|
||||||
|
b[i] = this->inbuf[i] - 'A' + 10;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
b[i] = this->inbuf[i] - '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsigned char ch = (unsigned char)((b[0] << 4) + b[1]);
|
||||||
|
|
||||||
|
QTC::TC("libtests", "Pl_ASCIIHexDecoder partial flush",
|
||||||
|
(this->pos == 2) ? 0 : 1);
|
||||||
|
getNext()->write(&ch, 1);
|
||||||
|
|
||||||
|
this->pos = 0;
|
||||||
|
strcpy(this->inbuf, "00");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_ASCIIHexDecoder::finish()
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
getNext()->finish();
|
||||||
|
}
|
67
libqpdf/Pl_Buffer.cc
Normal file
67
libqpdf/Pl_Buffer.cc
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
#include <qpdf/Pl_Buffer.hh>
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
Pl_Buffer::Pl_Buffer(char const* identifier, Pipeline* next) :
|
||||||
|
Pipeline(identifier, next),
|
||||||
|
ready(false),
|
||||||
|
total_size(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Pl_Buffer::~Pl_Buffer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_Buffer::write(unsigned char* buf, int len)
|
||||||
|
{
|
||||||
|
Buffer* b = new Buffer(len);
|
||||||
|
memcpy(b->getBuffer(), buf, len);
|
||||||
|
this->data.push_back(b);
|
||||||
|
this->ready = false;
|
||||||
|
this->total_size += len;
|
||||||
|
|
||||||
|
if (getNext(true))
|
||||||
|
{
|
||||||
|
getNext()->write(buf, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_Buffer::finish()
|
||||||
|
{
|
||||||
|
this->ready = true;
|
||||||
|
if (getNext(true))
|
||||||
|
{
|
||||||
|
getNext()->finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer*
|
||||||
|
Pl_Buffer::getBuffer()
|
||||||
|
{
|
||||||
|
if (! this->ready)
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("Pl_Buffer::getBuffer() called when not ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer* b = new Buffer(this->total_size);
|
||||||
|
unsigned char* p = b->getBuffer();
|
||||||
|
while (! this->data.empty())
|
||||||
|
{
|
||||||
|
PointerHolder<Buffer> bph = this->data.front();
|
||||||
|
this->data.pop_front();
|
||||||
|
Buffer* bp = bph.getPointer();
|
||||||
|
size_t bytes = bp->getSize();
|
||||||
|
memcpy(p, bp->getBuffer(), bytes);
|
||||||
|
p += bytes;
|
||||||
|
this->total_size -= bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(this->total_size == 0);
|
||||||
|
this->ready = false;
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
42
libqpdf/Pl_Count.cc
Normal file
42
libqpdf/Pl_Count.cc
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
#include <qpdf/Pl_Count.hh>
|
||||||
|
|
||||||
|
Pl_Count::Pl_Count(char const* identifier, Pipeline* next) :
|
||||||
|
Pipeline(identifier, next),
|
||||||
|
count(0),
|
||||||
|
last_char('\0')
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Pl_Count::~Pl_Count()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_Count::write(unsigned char* buf, int len)
|
||||||
|
{
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
this->count += len;
|
||||||
|
getNext()->write(buf, len);
|
||||||
|
this->last_char = buf[len - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_Count::finish()
|
||||||
|
{
|
||||||
|
getNext()->finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Pl_Count::getCount() const
|
||||||
|
{
|
||||||
|
return this->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char
|
||||||
|
Pl_Count::getLastChar() const
|
||||||
|
{
|
||||||
|
return this->last_char;
|
||||||
|
}
|
23
libqpdf/Pl_Discard.cc
Normal file
23
libqpdf/Pl_Discard.cc
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
#include <qpdf/Pl_Discard.hh>
|
||||||
|
|
||||||
|
// Exercised in md5 test suite
|
||||||
|
|
||||||
|
Pl_Discard::Pl_Discard() :
|
||||||
|
Pipeline("discard", 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Pl_Discard::~Pl_Discard()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_Discard::write(unsigned char* buf, int len)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_Discard::finish()
|
||||||
|
{
|
||||||
|
}
|
198
libqpdf/Pl_Flate.cc
Normal file
198
libqpdf/Pl_Flate.cc
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
|
||||||
|
#include <qpdf/Pl_Flate.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QUtil.hh>
|
||||||
|
|
||||||
|
Pl_Flate::Pl_Flate(char const* identifier, Pipeline* next,
|
||||||
|
action_e action, int out_bufsize) :
|
||||||
|
Pipeline(identifier, next),
|
||||||
|
out_bufsize(out_bufsize),
|
||||||
|
action(action),
|
||||||
|
initialized(false)
|
||||||
|
{
|
||||||
|
this->outbuf = new unsigned char[out_bufsize];
|
||||||
|
|
||||||
|
zstream.zalloc = (alloc_func)0;
|
||||||
|
zstream.zfree = (free_func)0;
|
||||||
|
zstream.opaque = (voidpf)0;
|
||||||
|
zstream.next_in = 0;
|
||||||
|
zstream.avail_in = 0;
|
||||||
|
zstream.next_out = this->outbuf;
|
||||||
|
zstream.avail_out = out_bufsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pl_Flate::~Pl_Flate()
|
||||||
|
{
|
||||||
|
if (this->outbuf)
|
||||||
|
{
|
||||||
|
delete [] this->outbuf;
|
||||||
|
this->outbuf = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_Flate::write(unsigned char* data, int len)
|
||||||
|
{
|
||||||
|
if (this->outbuf == 0)
|
||||||
|
{
|
||||||
|
throw Exception(
|
||||||
|
this->identifier +
|
||||||
|
": Pl_Flate: write() called after finish() called");
|
||||||
|
}
|
||||||
|
handleData(data, len, Z_NO_FLUSH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_Flate::handleData(unsigned char* data, int len, int flush)
|
||||||
|
{
|
||||||
|
this->zstream.next_in = data;
|
||||||
|
this->zstream.avail_in = len;
|
||||||
|
|
||||||
|
if (! this->initialized)
|
||||||
|
{
|
||||||
|
int err = Z_OK;
|
||||||
|
if (this->action == a_deflate)
|
||||||
|
{
|
||||||
|
err = deflateInit(&this->zstream, Z_DEFAULT_COMPRESSION);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = inflateInit(&this->zstream);
|
||||||
|
}
|
||||||
|
checkError("Init", err);
|
||||||
|
this->initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = Z_OK;
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
while (! done)
|
||||||
|
{
|
||||||
|
if (action == a_deflate)
|
||||||
|
{
|
||||||
|
err = deflate(&this->zstream, flush);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = inflate(&this->zstream, flush);
|
||||||
|
}
|
||||||
|
switch (err)
|
||||||
|
{
|
||||||
|
case Z_BUF_ERROR:
|
||||||
|
// Probably shouldn't be able to happen, but possible as a
|
||||||
|
// boundary condition: if the last call to inflate exactly
|
||||||
|
// filled the output buffer, it's possible that the next
|
||||||
|
// call to inflate could have nothing to do.
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Z_STREAM_END:
|
||||||
|
done = true;
|
||||||
|
// fall through
|
||||||
|
|
||||||
|
case Z_OK:
|
||||||
|
{
|
||||||
|
if ((this->zstream.avail_in == 0) &&
|
||||||
|
(this->zstream.avail_out > 0))
|
||||||
|
{
|
||||||
|
// There is nothing left to read, and there was
|
||||||
|
// sufficient buffer space to write everything we
|
||||||
|
// needed, so we're done for now.
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
uLong ready = (this->out_bufsize - this->zstream.avail_out);
|
||||||
|
if (ready > 0)
|
||||||
|
{
|
||||||
|
this->getNext()->write(this->outbuf, ready);
|
||||||
|
this->zstream.next_out = this->outbuf;
|
||||||
|
this->zstream.avail_out = this->out_bufsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this->checkError("data", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_Flate::finish()
|
||||||
|
{
|
||||||
|
if (this->outbuf)
|
||||||
|
{
|
||||||
|
if (this->initialized)
|
||||||
|
{
|
||||||
|
unsigned char buf[1];
|
||||||
|
buf[0] = '\0';
|
||||||
|
handleData(buf, 0, Z_FINISH);
|
||||||
|
int err = Z_OK;
|
||||||
|
if (action == a_deflate)
|
||||||
|
{
|
||||||
|
err = deflateEnd(&this->zstream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = inflateEnd(&this->zstream);
|
||||||
|
}
|
||||||
|
checkError("End", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete [] this->outbuf;
|
||||||
|
this->outbuf = 0;
|
||||||
|
}
|
||||||
|
this->getNext()->finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_Flate::checkError(char const* prefix, int error_code)
|
||||||
|
{
|
||||||
|
if (error_code != Z_OK)
|
||||||
|
{
|
||||||
|
char const* action_str = (action == a_deflate ? "deflate" : "inflate");
|
||||||
|
std::string msg =
|
||||||
|
this->identifier + ": " + action_str + ": " + prefix + ": ";
|
||||||
|
|
||||||
|
if (this->zstream.msg)
|
||||||
|
{
|
||||||
|
msg += this->zstream.msg;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (error_code)
|
||||||
|
{
|
||||||
|
case Z_ERRNO:
|
||||||
|
msg += "zlib system error";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Z_STREAM_ERROR:
|
||||||
|
msg += "zlib stream error";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Z_DATA_ERROR:
|
||||||
|
msg += "zlib data error";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Z_MEM_ERROR:
|
||||||
|
msg += "zlib memory error";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Z_BUF_ERROR:
|
||||||
|
msg += "zlib buffer error";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Z_VERSION_ERROR:
|
||||||
|
msg += "zlib version error";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
msg += std::string("zlib unknown error (") +
|
||||||
|
QUtil::int_to_string(error_code) + ")";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception(msg);
|
||||||
|
}
|
||||||
|
}
|
229
libqpdf/Pl_LZWDecoder.cc
Normal file
229
libqpdf/Pl_LZWDecoder.cc
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
#include <qpdf/Pl_LZWDecoder.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
#include <qpdf/QTC.hh>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
Pl_LZWDecoder::Pl_LZWDecoder(char const* identifier, Pipeline* next,
|
||||||
|
bool early_code_change) :
|
||||||
|
Pipeline(identifier, next),
|
||||||
|
code_size(9),
|
||||||
|
next(0),
|
||||||
|
byte_pos(0),
|
||||||
|
bit_pos(0),
|
||||||
|
bits_available(0),
|
||||||
|
code_change_delta(early_code_change ? 1 : 0),
|
||||||
|
eod(false),
|
||||||
|
last_code(256)
|
||||||
|
{
|
||||||
|
memset(buf, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pl_LZWDecoder::~Pl_LZWDecoder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_LZWDecoder::write(unsigned char* bytes, int len)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
this->buf[next++] = bytes[i];
|
||||||
|
if (this->next == 3)
|
||||||
|
{
|
||||||
|
this->next = 0;
|
||||||
|
}
|
||||||
|
this->bits_available += 8;
|
||||||
|
if (this->bits_available >= this->code_size)
|
||||||
|
{
|
||||||
|
sendNextCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_LZWDecoder::finish()
|
||||||
|
{
|
||||||
|
getNext()->finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_LZWDecoder::sendNextCode()
|
||||||
|
{
|
||||||
|
int high = this->byte_pos;
|
||||||
|
int med = (this->byte_pos + 1) % 3;
|
||||||
|
int low = (this->byte_pos + 2) % 3;
|
||||||
|
|
||||||
|
int bits_from_high = 8 - this->bit_pos;
|
||||||
|
int bits_from_med = this->code_size - bits_from_high;
|
||||||
|
int bits_from_low = 0;
|
||||||
|
if (bits_from_med > 8)
|
||||||
|
{
|
||||||
|
bits_from_low = bits_from_med - 8;
|
||||||
|
bits_from_med = 8;
|
||||||
|
}
|
||||||
|
int high_mask = (1 << bits_from_high) - 1;
|
||||||
|
int med_mask = 0xff - ((1 << (8 - bits_from_med)) - 1);
|
||||||
|
int low_mask = 0xff - ((1 << (8 - bits_from_low)) - 1);
|
||||||
|
int code = 0;
|
||||||
|
code += (this->buf[high] & high_mask) << bits_from_med;
|
||||||
|
code += ((this->buf[med] & med_mask) >> (8 - bits_from_med));
|
||||||
|
if (bits_from_low)
|
||||||
|
{
|
||||||
|
code <<= bits_from_low;
|
||||||
|
code += ((this->buf[low] & low_mask) >> (8 - bits_from_low));
|
||||||
|
this->byte_pos = low;
|
||||||
|
this->bit_pos = bits_from_low;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->byte_pos = med;
|
||||||
|
this->bit_pos = bits_from_med;
|
||||||
|
}
|
||||||
|
if (this->bit_pos == 8)
|
||||||
|
{
|
||||||
|
this->bit_pos = 0;
|
||||||
|
++this->byte_pos;
|
||||||
|
this->byte_pos %= 3;
|
||||||
|
}
|
||||||
|
this->bits_available -= this->code_size;
|
||||||
|
|
||||||
|
handleCode(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char
|
||||||
|
Pl_LZWDecoder::getFirstChar(int code)
|
||||||
|
{
|
||||||
|
unsigned char result = '\0';
|
||||||
|
if (code < 256)
|
||||||
|
{
|
||||||
|
result = (unsigned char) code;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(code > 257);
|
||||||
|
unsigned int idx = code - 258;
|
||||||
|
assert(idx < table.size());
|
||||||
|
Buffer& b = table[idx];
|
||||||
|
result = b.getBuffer()[0];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_LZWDecoder::addToTable(unsigned char next)
|
||||||
|
{
|
||||||
|
unsigned int last_size = 0;
|
||||||
|
unsigned char const* last_data = 0;
|
||||||
|
unsigned char tmp[1];
|
||||||
|
|
||||||
|
if (this->last_code < 256)
|
||||||
|
{
|
||||||
|
tmp[0] = this->last_code;
|
||||||
|
last_data = tmp;
|
||||||
|
last_size = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(this->last_code > 257);
|
||||||
|
unsigned int idx = this->last_code - 258;
|
||||||
|
assert(idx < table.size());
|
||||||
|
Buffer& b = table[idx];
|
||||||
|
last_data = b.getBuffer();
|
||||||
|
last_size = b.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer entry(1 + last_size);
|
||||||
|
unsigned char* new_data = entry.getBuffer();
|
||||||
|
memcpy(new_data, last_data, last_size);
|
||||||
|
new_data[last_size] = next;
|
||||||
|
this->table.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_LZWDecoder::handleCode(int code)
|
||||||
|
{
|
||||||
|
if (this->eod)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == 256)
|
||||||
|
{
|
||||||
|
if (! this->table.empty())
|
||||||
|
{
|
||||||
|
QTC::TC("libtests", "Pl_LZWDecoder intermediate reset");
|
||||||
|
}
|
||||||
|
this->table.clear();
|
||||||
|
this->code_size = 9;
|
||||||
|
}
|
||||||
|
else if (code == 257)
|
||||||
|
{
|
||||||
|
this->eod = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (this->last_code != 256)
|
||||||
|
{
|
||||||
|
// Add to the table from last time. New table entry would
|
||||||
|
// be what we read last plus the first character of what
|
||||||
|
// we're reading now.
|
||||||
|
unsigned char next = '\0';
|
||||||
|
unsigned int table_size = table.size();
|
||||||
|
if (code < 256)
|
||||||
|
{
|
||||||
|
// just read < 256; last time's next was code
|
||||||
|
next = code;
|
||||||
|
}
|
||||||
|
else if (code > 257)
|
||||||
|
{
|
||||||
|
unsigned int idx = code - 258;
|
||||||
|
if (idx > table_size)
|
||||||
|
{
|
||||||
|
throw QEXC::General("LZWDecoder: bad code received");
|
||||||
|
}
|
||||||
|
else if (idx == table_size)
|
||||||
|
{
|
||||||
|
// The encoder would have just created this entry,
|
||||||
|
// so the first character of this entry would have
|
||||||
|
// been the same as the first character of the
|
||||||
|
// last entry.
|
||||||
|
QTC::TC("libtests", "Pl_LZWDecoder last was table size");
|
||||||
|
next = getFirstChar(this->last_code);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next = getFirstChar(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsigned int last_idx = 258 + table_size;
|
||||||
|
if (last_idx == 4095)
|
||||||
|
{
|
||||||
|
throw QEXC::General("LZWDecoder: table full");
|
||||||
|
}
|
||||||
|
addToTable(next);
|
||||||
|
unsigned int change_idx = last_idx + code_change_delta;
|
||||||
|
if ((change_idx == 511) ||
|
||||||
|
(change_idx == 1023) ||
|
||||||
|
(change_idx == 2047))
|
||||||
|
{
|
||||||
|
++this->code_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code < 256)
|
||||||
|
{
|
||||||
|
unsigned char ch = (unsigned char) code;
|
||||||
|
getNext()->write(&ch, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Buffer& b = table[code - 258];
|
||||||
|
getNext()->write(b.getBuffer(), b.getSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->last_code = code;
|
||||||
|
}
|
43
libqpdf/Pl_MD5.cc
Normal file
43
libqpdf/Pl_MD5.cc
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
#include <qpdf/Pl_MD5.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
|
||||||
|
Pl_MD5::Pl_MD5(char const* identifier, Pipeline* next) :
|
||||||
|
Pipeline(identifier, next),
|
||||||
|
in_progress(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Pl_MD5::~Pl_MD5()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_MD5::write(unsigned char* buf, int len)
|
||||||
|
{
|
||||||
|
if (! this->in_progress)
|
||||||
|
{
|
||||||
|
this->md5.reset();
|
||||||
|
this->in_progress = true;
|
||||||
|
}
|
||||||
|
this->md5.encodeDataIncrementally((char*) buf, len);
|
||||||
|
this->getNext()->write(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_MD5::finish()
|
||||||
|
{
|
||||||
|
this->getNext()->finish();
|
||||||
|
this->in_progress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Pl_MD5::getHexDigest()
|
||||||
|
{
|
||||||
|
if (this->in_progress)
|
||||||
|
{
|
||||||
|
throw QEXC::General("digest requested for in-progress MD5 Pipeline");
|
||||||
|
}
|
||||||
|
return this->md5.unparse();
|
||||||
|
}
|
146
libqpdf/Pl_PNGFilter.cc
Normal file
146
libqpdf/Pl_PNGFilter.cc
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
|
||||||
|
#include <qpdf/Pl_PNGFilter.hh>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
Pl_PNGFilter::Pl_PNGFilter(char const* identifier, Pipeline* next,
|
||||||
|
action_e action, unsigned int columns,
|
||||||
|
unsigned int bytes_per_pixel) :
|
||||||
|
Pipeline(identifier, next),
|
||||||
|
action(action),
|
||||||
|
columns(columns),
|
||||||
|
cur_row(0),
|
||||||
|
prev_row(0),
|
||||||
|
buf1(0),
|
||||||
|
buf2(0),
|
||||||
|
pos(0)
|
||||||
|
{
|
||||||
|
this->buf1 = new unsigned char[columns + 1];
|
||||||
|
this->buf2 = new unsigned char[columns + 1];
|
||||||
|
this->cur_row = buf1;
|
||||||
|
|
||||||
|
// number of bytes per incoming row
|
||||||
|
this->incoming = (action == a_encode ? columns : columns + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pl_PNGFilter::~Pl_PNGFilter()
|
||||||
|
{
|
||||||
|
delete [] buf1;
|
||||||
|
delete [] buf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_PNGFilter::write(unsigned char* data, int len)
|
||||||
|
{
|
||||||
|
int left = this->incoming - this->pos;
|
||||||
|
unsigned int offset = 0;
|
||||||
|
while (len >= left)
|
||||||
|
{
|
||||||
|
// finish off current row
|
||||||
|
memcpy(this->cur_row + this->pos, data + offset, left);
|
||||||
|
offset += left;
|
||||||
|
len -= left;
|
||||||
|
|
||||||
|
processRow();
|
||||||
|
|
||||||
|
// Swap rows
|
||||||
|
unsigned char* t = this->prev_row;
|
||||||
|
this->prev_row = this->cur_row;
|
||||||
|
this->cur_row = t ? t : this->buf2;
|
||||||
|
memset(this->cur_row, 0, this->columns + 1);
|
||||||
|
left = this->incoming;
|
||||||
|
this->pos = 0;
|
||||||
|
}
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
memcpy(this->cur_row + this->pos, data + offset, len);
|
||||||
|
}
|
||||||
|
this->pos += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_PNGFilter::processRow()
|
||||||
|
{
|
||||||
|
if (this->action == a_encode)
|
||||||
|
{
|
||||||
|
encodeRow();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
decodeRow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_PNGFilter::decodeRow()
|
||||||
|
{
|
||||||
|
int filter = (int) this->cur_row[0];
|
||||||
|
if (this->prev_row)
|
||||||
|
{
|
||||||
|
switch (filter)
|
||||||
|
{
|
||||||
|
case 0: // none
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // sub
|
||||||
|
throw Exception("sub filter not implemented");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // up
|
||||||
|
for (unsigned int i = 1; i <= this->columns; ++i)
|
||||||
|
{
|
||||||
|
this->cur_row[i] += this->prev_row[i];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: // average
|
||||||
|
throw Exception("average filter not implemented");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: // Paeth
|
||||||
|
throw Exception("Paeth filter not implemented");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// ignore
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getNext()->write(this->cur_row + 1, this->columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_PNGFilter::encodeRow()
|
||||||
|
{
|
||||||
|
// For now, hard-code to using UP filter.
|
||||||
|
unsigned char ch = 2;
|
||||||
|
getNext()->write(&ch, 1);
|
||||||
|
if (this->prev_row)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < this->columns; ++i)
|
||||||
|
{
|
||||||
|
ch = this->cur_row[i] - this->prev_row[i];
|
||||||
|
getNext()->write(&ch, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
getNext()->write(this->cur_row, this->columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_PNGFilter::finish()
|
||||||
|
{
|
||||||
|
if (this->pos)
|
||||||
|
{
|
||||||
|
// write partial row
|
||||||
|
processRow();
|
||||||
|
}
|
||||||
|
this->prev_row = 0;
|
||||||
|
this->cur_row = buf1;
|
||||||
|
this->pos = 0;
|
||||||
|
memset(this->cur_row, 0, this->columns + 1);
|
||||||
|
|
||||||
|
getNext()->finish();
|
||||||
|
}
|
179
libqpdf/Pl_QPDFTokenizer.cc
Normal file
179
libqpdf/Pl_QPDFTokenizer.cc
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
|
||||||
|
#include <qpdf/Pl_QPDFTokenizer.hh>
|
||||||
|
#include <qpdf/QPDF_String.hh>
|
||||||
|
#include <qpdf/QPDF_Name.hh>
|
||||||
|
|
||||||
|
Pl_QPDFTokenizer::Pl_QPDFTokenizer(char const* identifier, Pipeline* next) :
|
||||||
|
Pipeline(identifier, next),
|
||||||
|
newline_after_next_token(false),
|
||||||
|
just_wrote_nl(false),
|
||||||
|
last_char_was_cr(false),
|
||||||
|
unread_char(false),
|
||||||
|
char_to_unread('\0'),
|
||||||
|
pass_through(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Pl_QPDFTokenizer::~Pl_QPDFTokenizer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_QPDFTokenizer::writeNext(char const* buf, int len)
|
||||||
|
{
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
unsigned char* t = new unsigned char[len];
|
||||||
|
memcpy(t, buf, len);
|
||||||
|
getNext()->write(t, len);
|
||||||
|
delete [] t;
|
||||||
|
this->just_wrote_nl = (buf[len-1] == '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_QPDFTokenizer::writeToken(QPDFTokenizer::Token& token)
|
||||||
|
{
|
||||||
|
std::string value = token.getRawValue();
|
||||||
|
|
||||||
|
switch (token.getType())
|
||||||
|
{
|
||||||
|
case QPDFTokenizer::tt_string:
|
||||||
|
value = QPDF_String(token.getValue()).unparse();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QPDFTokenizer::tt_name:
|
||||||
|
value = QPDF_Name(token.getValue()).unparse();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
writeNext(value.c_str(), value.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_QPDFTokenizer::processChar(char ch)
|
||||||
|
{
|
||||||
|
if (this->pass_through)
|
||||||
|
{
|
||||||
|
// We're not noramlizing anymore -- just write this without
|
||||||
|
// looking at it.
|
||||||
|
writeNext(&ch, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenizer.presentCharacter(ch);
|
||||||
|
QPDFTokenizer::Token token;
|
||||||
|
if (tokenizer.getToken(token, this->unread_char, this->char_to_unread))
|
||||||
|
{
|
||||||
|
writeToken(token);
|
||||||
|
if (this->newline_after_next_token)
|
||||||
|
{
|
||||||
|
writeNext("\n", 1);
|
||||||
|
this->newline_after_next_token = false;
|
||||||
|
}
|
||||||
|
if ((token.getType() == QPDFTokenizer::tt_word) &&
|
||||||
|
(token.getValue() == "BI"))
|
||||||
|
{
|
||||||
|
// Uh oh.... we're not sophisticated enough to handle
|
||||||
|
// inline images safely. We'd have to to set up all the
|
||||||
|
// filters and pipe the iamge data through it until the
|
||||||
|
// filtered output was the right size for an image of the
|
||||||
|
// specified dimensions. Then we'd either have to write
|
||||||
|
// out raw image data or continue to write filtered data,
|
||||||
|
// resuming normalization when we get to the end.
|
||||||
|
// Insetad, for now, we'll just turn off noramlization for
|
||||||
|
// the remainder of this stream.
|
||||||
|
this->pass_through = true;
|
||||||
|
if (this->unread_char)
|
||||||
|
{
|
||||||
|
writeNext(&this->char_to_unread, 1);
|
||||||
|
this->unread_char = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool suppress = false;
|
||||||
|
if ((ch == '\n') && (this->last_char_was_cr))
|
||||||
|
{
|
||||||
|
// Always ignore \n following \r
|
||||||
|
suppress = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((this->last_char_was_cr = (ch == '\r')))
|
||||||
|
{
|
||||||
|
ch = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->tokenizer.betweenTokens())
|
||||||
|
{
|
||||||
|
if (! suppress)
|
||||||
|
{
|
||||||
|
writeNext(&ch, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ch == '\n')
|
||||||
|
{
|
||||||
|
this->newline_after_next_token = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_QPDFTokenizer::checkUnread()
|
||||||
|
{
|
||||||
|
if (this->unread_char)
|
||||||
|
{
|
||||||
|
processChar(this->char_to_unread);
|
||||||
|
if (this->unread_char)
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("unread_char still true after processing "
|
||||||
|
"unread character");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_QPDFTokenizer::write(unsigned char* buf, int len)
|
||||||
|
{
|
||||||
|
checkUnread();
|
||||||
|
for (int i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
processChar(buf[i]);
|
||||||
|
checkUnread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_QPDFTokenizer::finish()
|
||||||
|
{
|
||||||
|
this->tokenizer.presentEOF();
|
||||||
|
if (! this->pass_through)
|
||||||
|
{
|
||||||
|
QPDFTokenizer::Token token;
|
||||||
|
if (tokenizer.getToken(token, this->unread_char, this->char_to_unread))
|
||||||
|
{
|
||||||
|
writeToken(token);
|
||||||
|
if (unread_char)
|
||||||
|
{
|
||||||
|
if (this->char_to_unread == '\r')
|
||||||
|
{
|
||||||
|
this->char_to_unread = '\n';
|
||||||
|
}
|
||||||
|
writeNext(&this->char_to_unread, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! this->just_wrote_nl)
|
||||||
|
{
|
||||||
|
writeNext("\n", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNext()->finish();
|
||||||
|
}
|
57
libqpdf/Pl_RC4.cc
Normal file
57
libqpdf/Pl_RC4.cc
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
#include <qpdf/Pl_RC4.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QUtil.hh>
|
||||||
|
|
||||||
|
Pl_RC4::Pl_RC4(char const* identifier, Pipeline* next,
|
||||||
|
unsigned char const* key_data, int key_len,
|
||||||
|
int out_bufsize) :
|
||||||
|
Pipeline(identifier, next),
|
||||||
|
out_bufsize(out_bufsize),
|
||||||
|
rc4(key_data, key_len)
|
||||||
|
{
|
||||||
|
this->outbuf = new unsigned char[out_bufsize];
|
||||||
|
}
|
||||||
|
|
||||||
|
Pl_RC4::~Pl_RC4()
|
||||||
|
{
|
||||||
|
if (this->outbuf)
|
||||||
|
{
|
||||||
|
delete [] this->outbuf;
|
||||||
|
this->outbuf = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_RC4::write(unsigned char* data, int len)
|
||||||
|
{
|
||||||
|
if (this->outbuf == 0)
|
||||||
|
{
|
||||||
|
throw Exception(
|
||||||
|
this->identifier +
|
||||||
|
": Pl_RC4: write() called after finish() called");
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytes_left = len;
|
||||||
|
unsigned char* p = data;
|
||||||
|
|
||||||
|
while (bytes_left > 0)
|
||||||
|
{
|
||||||
|
int bytes = (bytes_left < this->out_bufsize ? bytes_left : out_bufsize);
|
||||||
|
bytes_left -= bytes;
|
||||||
|
rc4.process(p, bytes, outbuf);
|
||||||
|
p += bytes;
|
||||||
|
getNext()->write(outbuf, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_RC4::finish()
|
||||||
|
{
|
||||||
|
if (this->outbuf)
|
||||||
|
{
|
||||||
|
delete [] this->outbuf;
|
||||||
|
this->outbuf = 0;
|
||||||
|
}
|
||||||
|
this->getNext()->finish();
|
||||||
|
}
|
48
libqpdf/Pl_StdioFile.cc
Normal file
48
libqpdf/Pl_StdioFile.cc
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
#include <qpdf/Pl_StdioFile.hh>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
Pl_StdioFile::Pl_StdioFile(char const* identifier, FILE* f) :
|
||||||
|
Pipeline(identifier, 0),
|
||||||
|
file(f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Pl_StdioFile::~Pl_StdioFile()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_StdioFile::write(unsigned char* buf, int len)
|
||||||
|
{
|
||||||
|
size_t so_far = 0;
|
||||||
|
while (len > 0)
|
||||||
|
{
|
||||||
|
so_far = fwrite(buf, 1, len, this->file);
|
||||||
|
if (so_far == 0)
|
||||||
|
{
|
||||||
|
throw QEXC::System(this->identifier + ": Pl_StdioFile::write",
|
||||||
|
errno);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buf += so_far;
|
||||||
|
len -= so_far;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Pl_StdioFile::finish()
|
||||||
|
{
|
||||||
|
if (fileno(this->file) != -1)
|
||||||
|
{
|
||||||
|
fflush(this->file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw QEXC::Internal(this->identifier +
|
||||||
|
": Pl_StdioFile::finish: stream already closed");
|
||||||
|
}
|
||||||
|
}
|
67
libqpdf/QEXC.cc
Normal file
67
libqpdf/QEXC.cc
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
QEXC::Base::Base()
|
||||||
|
{
|
||||||
|
// nothing needed
|
||||||
|
}
|
||||||
|
|
||||||
|
QEXC::Base::Base(std::string const& message) :
|
||||||
|
message(message)
|
||||||
|
{
|
||||||
|
// nothing needed
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
QEXC::Base::unparse() const
|
||||||
|
{
|
||||||
|
return this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QEXC::Base::setMessage(std::string const& message)
|
||||||
|
{
|
||||||
|
this->message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char*
|
||||||
|
QEXC::Base::what() const throw()
|
||||||
|
{
|
||||||
|
// Since unparse() returns a const string reference, its
|
||||||
|
// implementors must arrange to have it return a reference to a
|
||||||
|
// string that is not going to disappear. It is therefore safe
|
||||||
|
// for us to return it's c_str() pointer.
|
||||||
|
return this->unparse().c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
QEXC::General::General()
|
||||||
|
{
|
||||||
|
// nothing needed
|
||||||
|
}
|
||||||
|
|
||||||
|
QEXC::General::General(std::string const& message) :
|
||||||
|
Base(message)
|
||||||
|
{
|
||||||
|
// nothing needed
|
||||||
|
}
|
||||||
|
|
||||||
|
QEXC::System::System(std::string const& prefix, int sys_errno)
|
||||||
|
{
|
||||||
|
// Note: using sys_errno in case errno is a macro.
|
||||||
|
this->sys_errno = sys_errno;
|
||||||
|
this->setMessage(prefix + ": " + strerror(sys_errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
QEXC::System::getErrno() const
|
||||||
|
{
|
||||||
|
return this->sys_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
QEXC::Internal::Internal(std::string const& message) :
|
||||||
|
Base("INTERNAL ERROR: " + message)
|
||||||
|
{
|
||||||
|
// nothing needed
|
||||||
|
}
|
1851
libqpdf/QPDF.cc
Normal file
1851
libqpdf/QPDF.cc
Normal file
File diff suppressed because it is too large
Load Diff
20
libqpdf/QPDFExc.cc
Normal file
20
libqpdf/QPDFExc.cc
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDFExc.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QUtil.hh>
|
||||||
|
|
||||||
|
QPDFExc::QPDFExc(std::string const& message) :
|
||||||
|
QEXC::General(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFExc::QPDFExc(std::string const& filename, int offset,
|
||||||
|
std::string const& message) :
|
||||||
|
QEXC::General(filename + ": offset " + QUtil::int_to_string(offset) +
|
||||||
|
": " + message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFExc::~QPDFExc() throw ()
|
||||||
|
{
|
||||||
|
}
|
2
libqpdf/QPDFObject.cc
Normal file
2
libqpdf/QPDFObject.cc
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDFObject.hh>
|
637
libqpdf/QPDFObjectHandle.cc
Normal file
637
libqpdf/QPDFObjectHandle.cc
Normal file
@ -0,0 +1,637 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDFObjectHandle.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QPDF.hh>
|
||||||
|
#include <qpdf/QPDF_Bool.hh>
|
||||||
|
#include <qpdf/QPDF_Null.hh>
|
||||||
|
#include <qpdf/QPDF_Integer.hh>
|
||||||
|
#include <qpdf/QPDF_Real.hh>
|
||||||
|
#include <qpdf/QPDF_Name.hh>
|
||||||
|
#include <qpdf/QPDF_String.hh>
|
||||||
|
#include <qpdf/QPDF_Array.hh>
|
||||||
|
#include <qpdf/QPDF_Dictionary.hh>
|
||||||
|
#include <qpdf/QPDF_Stream.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QTC.hh>
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
#include <qpdf/QUtil.hh>
|
||||||
|
|
||||||
|
QPDFObjectHandle::QPDFObjectHandle() :
|
||||||
|
initialized(false),
|
||||||
|
objid(0),
|
||||||
|
generation(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle::QPDFObjectHandle(QPDF* qpdf, int objid, int generation) :
|
||||||
|
initialized(true),
|
||||||
|
qpdf(qpdf),
|
||||||
|
objid(objid),
|
||||||
|
generation(generation)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle::QPDFObjectHandle(QPDFObject* data) :
|
||||||
|
initialized(true),
|
||||||
|
qpdf(0),
|
||||||
|
objid(0),
|
||||||
|
generation(0),
|
||||||
|
obj(data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isInitialized() const
|
||||||
|
{
|
||||||
|
return this->initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class QPDFObjectTypeAccessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static bool check(QPDFObject* o)
|
||||||
|
{
|
||||||
|
return (o && dynamic_cast<T*>(o));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isBool()
|
||||||
|
{
|
||||||
|
dereference();
|
||||||
|
return QPDFObjectTypeAccessor<QPDF_Bool>::check(obj.getPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isNull()
|
||||||
|
{
|
||||||
|
dereference();
|
||||||
|
return QPDFObjectTypeAccessor<QPDF_Null>::check(obj.getPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isInteger()
|
||||||
|
{
|
||||||
|
dereference();
|
||||||
|
return QPDFObjectTypeAccessor<QPDF_Integer>::check(obj.getPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isReal()
|
||||||
|
{
|
||||||
|
dereference();
|
||||||
|
return QPDFObjectTypeAccessor<QPDF_Real>::check(obj.getPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isNumber()
|
||||||
|
{
|
||||||
|
return (isInteger() || isReal());
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
QPDFObjectHandle::getNumericValue()
|
||||||
|
{
|
||||||
|
double result = 0.0;
|
||||||
|
if (isInteger())
|
||||||
|
{
|
||||||
|
result = getIntValue();
|
||||||
|
}
|
||||||
|
else if (isReal())
|
||||||
|
{
|
||||||
|
result = atof(getRealValue().c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("getNumericValue called for non-numeric object");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isName()
|
||||||
|
{
|
||||||
|
dereference();
|
||||||
|
return QPDFObjectTypeAccessor<QPDF_Name>::check(obj.getPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isString()
|
||||||
|
{
|
||||||
|
dereference();
|
||||||
|
return QPDFObjectTypeAccessor<QPDF_String>::check(obj.getPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isArray()
|
||||||
|
{
|
||||||
|
dereference();
|
||||||
|
return QPDFObjectTypeAccessor<QPDF_Array>::check(obj.getPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isDictionary()
|
||||||
|
{
|
||||||
|
dereference();
|
||||||
|
return QPDFObjectTypeAccessor<QPDF_Dictionary>::check(obj.getPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isStream()
|
||||||
|
{
|
||||||
|
dereference();
|
||||||
|
return QPDFObjectTypeAccessor<QPDF_Stream>::check(obj.getPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isIndirect()
|
||||||
|
{
|
||||||
|
assertInitialized();
|
||||||
|
return (this->objid != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::isScalar()
|
||||||
|
{
|
||||||
|
return (! (isArray() || isDictionary() || isStream()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool accessors
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::getBoolValue()
|
||||||
|
{
|
||||||
|
assertType("Boolean", isBool());
|
||||||
|
return dynamic_cast<QPDF_Bool*>(obj.getPointer())->getVal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer accessors
|
||||||
|
|
||||||
|
int
|
||||||
|
QPDFObjectHandle::getIntValue()
|
||||||
|
{
|
||||||
|
assertType("Integer", isInteger());
|
||||||
|
return dynamic_cast<QPDF_Integer*>(obj.getPointer())->getVal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real accessors
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDFObjectHandle::getRealValue()
|
||||||
|
{
|
||||||
|
assertType("Real", isReal());
|
||||||
|
return dynamic_cast<QPDF_Real*>(obj.getPointer())->getVal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name acessors
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDFObjectHandle::getName()
|
||||||
|
{
|
||||||
|
assertType("Name", isName());
|
||||||
|
return dynamic_cast<QPDF_Name*>(obj.getPointer())->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// String accessors
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDFObjectHandle::getStringValue()
|
||||||
|
{
|
||||||
|
assertType("String", isString());
|
||||||
|
return dynamic_cast<QPDF_String*>(obj.getPointer())->getVal();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDFObjectHandle::getUTF8Value()
|
||||||
|
{
|
||||||
|
assertType("String", isString());
|
||||||
|
return dynamic_cast<QPDF_String*>(obj.getPointer())->getUTF8Val();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array acessors
|
||||||
|
|
||||||
|
int
|
||||||
|
QPDFObjectHandle::getArrayNItems()
|
||||||
|
{
|
||||||
|
assertType("Array", isArray());
|
||||||
|
return dynamic_cast<QPDF_Array*>(obj.getPointer())->getNItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::getArrayItem(int n)
|
||||||
|
{
|
||||||
|
assertType("Array", isArray());
|
||||||
|
return dynamic_cast<QPDF_Array*>(obj.getPointer())->getItem(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array mutators
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
|
||||||
|
{
|
||||||
|
assertType("Array", isArray());
|
||||||
|
return dynamic_cast<QPDF_Array*>(obj.getPointer())->setItem(n, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dictionary accesors
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::hasKey(std::string const& key)
|
||||||
|
{
|
||||||
|
assertType("Dictionary", isDictionary());
|
||||||
|
return dynamic_cast<QPDF_Dictionary*>(obj.getPointer())->hasKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::getKey(std::string const& key)
|
||||||
|
{
|
||||||
|
assertType("Dictionary", isDictionary());
|
||||||
|
return dynamic_cast<QPDF_Dictionary*>(obj.getPointer())->getKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::string>
|
||||||
|
QPDFObjectHandle::getKeys()
|
||||||
|
{
|
||||||
|
assertType("Dictionary", isDictionary());
|
||||||
|
return dynamic_cast<QPDF_Dictionary*>(obj.getPointer())->getKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dictionary mutators
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFObjectHandle::replaceKey(std::string const& key,
|
||||||
|
QPDFObjectHandle const& value)
|
||||||
|
{
|
||||||
|
assertType("Dictionary", isDictionary());
|
||||||
|
return dynamic_cast<QPDF_Dictionary*>(
|
||||||
|
obj.getPointer())->replaceKey(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFObjectHandle::removeKey(std::string const& key)
|
||||||
|
{
|
||||||
|
assertType("Dictionary", isDictionary());
|
||||||
|
return dynamic_cast<QPDF_Dictionary*>(obj.getPointer())->removeKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream accessors
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::getDict()
|
||||||
|
{
|
||||||
|
assertType("Stream", isStream());
|
||||||
|
return dynamic_cast<QPDF_Stream*>(obj.getPointer())->getDict();
|
||||||
|
}
|
||||||
|
|
||||||
|
PointerHolder<Buffer>
|
||||||
|
QPDFObjectHandle::getStreamData()
|
||||||
|
{
|
||||||
|
assertType("Stream", isStream());
|
||||||
|
return dynamic_cast<QPDF_Stream*>(obj.getPointer())->getStreamData();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::pipeStreamData(Pipeline* p, bool filter,
|
||||||
|
bool normalize, bool compress)
|
||||||
|
{
|
||||||
|
assertType("Stream", isStream());
|
||||||
|
return dynamic_cast<QPDF_Stream*>(obj.getPointer())->pipeStreamData(
|
||||||
|
p, filter, normalize, compress);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
QPDFObjectHandle::getObjectID() const
|
||||||
|
{
|
||||||
|
return this->objid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
QPDFObjectHandle::getGeneration() const
|
||||||
|
{
|
||||||
|
return this->generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, QPDFObjectHandle>
|
||||||
|
QPDFObjectHandle::getPageImages()
|
||||||
|
{
|
||||||
|
assertPageObject();
|
||||||
|
|
||||||
|
// Note: this code doesn't handle inherited resources. If this
|
||||||
|
// page dictionary doesn't have a /Resources key or has one whose
|
||||||
|
// value is null or an empty dictionary, you are supposed to walk
|
||||||
|
// up the page tree until you find a /Resources dictionary. As of
|
||||||
|
// this writing, I don't have any test files that use inherited
|
||||||
|
// resources, and hand-generating one won't be a good test beacuse
|
||||||
|
// any mistakes in my understanding would be present in both the
|
||||||
|
// code and the test file.
|
||||||
|
|
||||||
|
// NOTE: If support of inherited resources (see above comment) is
|
||||||
|
// implemented, edit comment in QPDFObjectHandle.hh for this
|
||||||
|
// function.
|
||||||
|
|
||||||
|
std::map<std::string, QPDFObjectHandle> result;
|
||||||
|
if (this->hasKey("/Resources"))
|
||||||
|
{
|
||||||
|
QPDFObjectHandle resources = this->getKey("/Resources");
|
||||||
|
if (resources.hasKey("/XObject"))
|
||||||
|
{
|
||||||
|
QPDFObjectHandle xobject = resources.getKey("/XObject");
|
||||||
|
std::set<std::string> keys = xobject.getKeys();
|
||||||
|
for (std::set<std::string>::iterator iter = keys.begin();
|
||||||
|
iter != keys.end(); ++iter)
|
||||||
|
{
|
||||||
|
std::string key = (*iter);
|
||||||
|
QPDFObjectHandle value = xobject.getKey(key);
|
||||||
|
if (value.isStream())
|
||||||
|
{
|
||||||
|
QPDFObjectHandle dict = value.getDict();
|
||||||
|
if (dict.hasKey("/Subtype") &&
|
||||||
|
(dict.getKey("/Subtype").getName() == "/Image") &&
|
||||||
|
(! dict.hasKey("/ImageMask")))
|
||||||
|
{
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<QPDFObjectHandle>
|
||||||
|
QPDFObjectHandle::getPageContents()
|
||||||
|
{
|
||||||
|
assertPageObject();
|
||||||
|
|
||||||
|
std::vector<QPDFObjectHandle> result;
|
||||||
|
QPDFObjectHandle contents = this->getKey("/Contents");
|
||||||
|
if (contents.isArray())
|
||||||
|
{
|
||||||
|
int n_items = contents.getArrayNItems();
|
||||||
|
for (int i = 0; i < n_items; ++i)
|
||||||
|
{
|
||||||
|
QPDFObjectHandle item = contents.getArrayItem(i);
|
||||||
|
if (item.isStream())
|
||||||
|
{
|
||||||
|
result.push_back(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw QEXC::General("unknown item type while inspecting "
|
||||||
|
"element of /Contents array in page "
|
||||||
|
"dictionary");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (contents.isStream())
|
||||||
|
{
|
||||||
|
result.push_back(contents);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw QEXC::General("unknown object type inspecting /Contents "
|
||||||
|
"key in page dictionary");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDFObjectHandle::unparse()
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
if (this->isIndirect())
|
||||||
|
{
|
||||||
|
result = QUtil::int_to_string(this->objid) + " " +
|
||||||
|
QUtil::int_to_string(this->generation) + " R";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = unparseResolved();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDFObjectHandle::unparseResolved()
|
||||||
|
{
|
||||||
|
dereference();
|
||||||
|
return this->obj.getPointer()->unparse();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::newIndirect(QPDF* qpdf, int objid, int generation)
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle(qpdf, objid, generation);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::newBool(bool value)
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle(new QPDF_Bool(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::newNull()
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle(new QPDF_Null());
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::newInteger(int value)
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle(new QPDF_Integer(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::newReal(std::string const& value)
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle(new QPDF_Real(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::newName(std::string const& name)
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle(new QPDF_Name(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::newString(std::string const& str)
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle(new QPDF_String(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::newArray(std::vector<QPDFObjectHandle> const& items)
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle(new QPDF_Array(items));
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::newDictionary(
|
||||||
|
std::map<std::string, QPDFObjectHandle> const& items)
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle(new QPDF_Dictionary(items));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDFObjectHandle::newStream(QPDF* qpdf, int objid, int generation,
|
||||||
|
QPDFObjectHandle stream_dict,
|
||||||
|
off_t offset, int length)
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle(new QPDF_Stream(
|
||||||
|
qpdf, objid, generation,
|
||||||
|
stream_dict, offset, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFObjectHandle::makeDirectInternal(std::set<int>& visited)
|
||||||
|
{
|
||||||
|
assertInitialized();
|
||||||
|
|
||||||
|
if (isStream())
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFObjectHandle ERR clone stream");
|
||||||
|
throw QEXC::General("attempt to make a stream into a direct object");
|
||||||
|
}
|
||||||
|
|
||||||
|
int cur_objid = this->objid;
|
||||||
|
if (cur_objid != 0)
|
||||||
|
{
|
||||||
|
if (visited.count(cur_objid))
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFObjectHandle makeDirect loop");
|
||||||
|
throw QEXC::General("loop detected while converting object from "
|
||||||
|
"indirect to direct");
|
||||||
|
}
|
||||||
|
visited.insert(cur_objid);
|
||||||
|
}
|
||||||
|
|
||||||
|
dereference();
|
||||||
|
this->objid = 0;
|
||||||
|
this->generation = 0;
|
||||||
|
|
||||||
|
QPDFObject* new_obj = 0;
|
||||||
|
|
||||||
|
if (isBool())
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFObjectHandle clone bool");
|
||||||
|
new_obj = new QPDF_Bool(getBoolValue());
|
||||||
|
}
|
||||||
|
else if (isNull())
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFObjectHandle clone null");
|
||||||
|
new_obj = new QPDF_Null();
|
||||||
|
}
|
||||||
|
else if (isInteger())
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFObjectHandle clone integer");
|
||||||
|
new_obj = new QPDF_Integer(getIntValue());
|
||||||
|
}
|
||||||
|
else if (isReal())
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFObjectHandle clone real");
|
||||||
|
new_obj = new QPDF_Real(getRealValue());
|
||||||
|
}
|
||||||
|
else if (isName())
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFObjectHandle clone name");
|
||||||
|
new_obj = new QPDF_Name(getName());
|
||||||
|
}
|
||||||
|
else if (isString())
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFObjectHandle clone string");
|
||||||
|
new_obj = new QPDF_String(getStringValue());
|
||||||
|
}
|
||||||
|
else if (isArray())
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFObjectHandle clone array");
|
||||||
|
std::vector<QPDFObjectHandle> items;
|
||||||
|
int n = getArrayNItems();
|
||||||
|
for (int i = 0; i < n; ++i)
|
||||||
|
{
|
||||||
|
items.push_back(getArrayItem(i));
|
||||||
|
items.back().makeDirectInternal(visited);
|
||||||
|
}
|
||||||
|
new_obj = new QPDF_Array(items);
|
||||||
|
}
|
||||||
|
else if (isDictionary())
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFObjectHandle clone dictionary");
|
||||||
|
std::set<std::string> keys = getKeys();
|
||||||
|
std::map<std::string, QPDFObjectHandle> items;
|
||||||
|
for (std::set<std::string>::iterator iter = keys.begin();
|
||||||
|
iter != keys.end(); ++iter)
|
||||||
|
{
|
||||||
|
items[*iter] = getKey(*iter);
|
||||||
|
items[*iter].makeDirectInternal(visited);
|
||||||
|
}
|
||||||
|
new_obj = new QPDF_Dictionary(items);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("QPDFObjectHandle::makeIndirect: "
|
||||||
|
"unknown object type");
|
||||||
|
}
|
||||||
|
|
||||||
|
this->obj = new_obj;
|
||||||
|
|
||||||
|
if (cur_objid)
|
||||||
|
{
|
||||||
|
visited.erase(cur_objid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFObjectHandle::makeDirect()
|
||||||
|
{
|
||||||
|
std::set<int> visited;
|
||||||
|
makeDirectInternal(visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFObjectHandle::assertInitialized() const
|
||||||
|
{
|
||||||
|
if (! this->initialized)
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("operation attempted on uninitialized "
|
||||||
|
"QPDFObjectHandle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFObjectHandle::assertType(char const* type_name, bool istype)
|
||||||
|
{
|
||||||
|
if (! istype)
|
||||||
|
{
|
||||||
|
throw QEXC::Internal(std::string("operation for ") + type_name +
|
||||||
|
" object attempted on object of wrong type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFObjectHandle::assertPageObject()
|
||||||
|
{
|
||||||
|
if (! (this->isDictionary() && this->hasKey("/Type") &&
|
||||||
|
(this->getKey("/Type").getName() == "/Page")))
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("page operation called on non-Page object");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFObjectHandle::dereference()
|
||||||
|
{
|
||||||
|
if (this->obj.getPointer() == 0)
|
||||||
|
{
|
||||||
|
this->obj = QPDF::Resolver::resolve(
|
||||||
|
this->qpdf, this->objid, this->generation);
|
||||||
|
if (this->obj.getPointer() == 0)
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFObjectHandle indirect to unknown");
|
||||||
|
this->obj = new QPDF_Null();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
458
libqpdf/QPDFTokenizer.cc
Normal file
458
libqpdf/QPDFTokenizer.cc
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDFTokenizer.hh>
|
||||||
|
|
||||||
|
// DO NOT USE ctype -- it is locale dependent for some things, and
|
||||||
|
// it's not worth the risk of including it in case it may accidentally
|
||||||
|
// be used.
|
||||||
|
|
||||||
|
#include <qpdf/PCRE.hh>
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
#include <qpdf/QTC.hh>
|
||||||
|
|
||||||
|
// See note above about ctype.
|
||||||
|
static bool is_hex_digit(char ch)
|
||||||
|
{
|
||||||
|
return (strchr("0123456789abcdefABCDEF", ch) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFTokenizer::QPDFTokenizer() :
|
||||||
|
pound_special_in_name(true)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFTokenizer::allowPoundAnywhereInName()
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFTokenizer allow pound anywhere in name");
|
||||||
|
this->pound_special_in_name = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFTokenizer::reset()
|
||||||
|
{
|
||||||
|
state = st_top;
|
||||||
|
type = tt_bad;
|
||||||
|
val = "";
|
||||||
|
raw_val = "";
|
||||||
|
error_message = "";
|
||||||
|
unread_char = false;
|
||||||
|
char_to_unread = '\0';
|
||||||
|
string_depth = 0;
|
||||||
|
string_ignoring_newline = false;
|
||||||
|
last_char_was_bs = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFTokenizer::presentCharacter(char ch)
|
||||||
|
{
|
||||||
|
static PCRE num_re("^[\\+\\-]?(?:\\.\\d+|\\d+(?:\\.\\d+)?)$");
|
||||||
|
|
||||||
|
if (state == st_token_ready)
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("QPDF tokenizer presented character "
|
||||||
|
"while token is waiting");
|
||||||
|
}
|
||||||
|
|
||||||
|
char orig_ch = ch;
|
||||||
|
|
||||||
|
// State machine is implemented such that some characters may be
|
||||||
|
// handled more than once. This happens whenever you have to use
|
||||||
|
// the character that caused a state change in the new state.
|
||||||
|
|
||||||
|
bool handled = true;
|
||||||
|
if (state == st_top)
|
||||||
|
{
|
||||||
|
// Note: we specifically do not use ctype here. It is
|
||||||
|
// locale-dependent.
|
||||||
|
if (strchr(" \t\n\v\f\r", ch))
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
else if (ch == '%')
|
||||||
|
{
|
||||||
|
// Discard comments
|
||||||
|
state = st_in_comment;
|
||||||
|
}
|
||||||
|
else if (ch == '(')
|
||||||
|
{
|
||||||
|
string_depth = 1;
|
||||||
|
string_ignoring_newline = false;
|
||||||
|
memset(bs_num_register, '\0', sizeof(bs_num_register));
|
||||||
|
last_char_was_bs = false;
|
||||||
|
state = st_in_string;
|
||||||
|
}
|
||||||
|
else if (ch == '<')
|
||||||
|
{
|
||||||
|
state = st_lt;
|
||||||
|
}
|
||||||
|
else if (ch == '>')
|
||||||
|
{
|
||||||
|
state = st_gt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
val += ch;
|
||||||
|
if (ch == ')')
|
||||||
|
{
|
||||||
|
type = tt_bad;
|
||||||
|
QTC::TC("qpdf", "QPDF_Tokenizer bad )");
|
||||||
|
error_message = "unexpected )";
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
else if (ch == '[')
|
||||||
|
{
|
||||||
|
type = tt_array_open;
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
else if (ch == ']')
|
||||||
|
{
|
||||||
|
type = tt_array_close;
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
else if (ch == '{')
|
||||||
|
{
|
||||||
|
type = tt_brace_open;
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
else if (ch == '}')
|
||||||
|
{
|
||||||
|
type = tt_brace_close;
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state = st_literal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (state == st_in_comment)
|
||||||
|
{
|
||||||
|
if ((ch == '\r') || (ch == '\n'))
|
||||||
|
{
|
||||||
|
state = st_top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (state == st_lt)
|
||||||
|
{
|
||||||
|
if (ch == '<')
|
||||||
|
{
|
||||||
|
val = "<<";
|
||||||
|
type = tt_dict_open;
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
handled = false;
|
||||||
|
state = st_in_hexstring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (state == st_gt)
|
||||||
|
{
|
||||||
|
if (ch == '>')
|
||||||
|
{
|
||||||
|
val = ">>";
|
||||||
|
type = tt_dict_close;
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
val = ">";
|
||||||
|
type = tt_bad;
|
||||||
|
QTC::TC("qpdf", "QPDF_Tokenizer bad >");
|
||||||
|
error_message = "unexpected >";
|
||||||
|
unread_char = true;
|
||||||
|
char_to_unread = ch;
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (state == st_in_string)
|
||||||
|
{
|
||||||
|
if (string_ignoring_newline && (! ((ch == '\r') || (ch == '\n'))))
|
||||||
|
{
|
||||||
|
string_ignoring_newline = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int bs_num_count = strlen(bs_num_register);
|
||||||
|
bool ch_is_octal = ((ch >= '0') && (ch <= '7'));
|
||||||
|
if ((bs_num_count == 3) || ((bs_num_count > 0) && (! ch_is_octal)))
|
||||||
|
{
|
||||||
|
// We've accumulated \ddd. PDF Spec says to ignore
|
||||||
|
// high-order overflow.
|
||||||
|
val += (char) strtol(bs_num_register, 0, 8);
|
||||||
|
memset(bs_num_register, '\0', sizeof(bs_num_register));
|
||||||
|
bs_num_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string_ignoring_newline && ((ch == '\r') || (ch == '\n')))
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
else if (ch_is_octal && (last_char_was_bs || (bs_num_count > 0)))
|
||||||
|
{
|
||||||
|
bs_num_register[bs_num_count++] = ch;
|
||||||
|
}
|
||||||
|
else if (last_char_was_bs)
|
||||||
|
{
|
||||||
|
switch (ch)
|
||||||
|
{
|
||||||
|
case 'n':
|
||||||
|
val += '\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
val += '\r';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
val += '\t';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
val += '\b';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
val += '\f';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\r':
|
||||||
|
case '\n':
|
||||||
|
string_ignoring_newline = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// PDF spec says backslash is ignored before anything else
|
||||||
|
val += ch;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ch == '\\')
|
||||||
|
{
|
||||||
|
// last_char_was_bs is set/cleared below as appropriate
|
||||||
|
if (bs_num_count)
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("QPDFTokenizer: bs_num_count != 0 "
|
||||||
|
"when ch == '\\'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ch == '(')
|
||||||
|
{
|
||||||
|
val += ch;
|
||||||
|
++string_depth;
|
||||||
|
}
|
||||||
|
else if ((ch == ')') && (--string_depth == 0))
|
||||||
|
{
|
||||||
|
type = tt_string;
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
val += ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_char_was_bs = ((! last_char_was_bs) && (ch == '\\'));
|
||||||
|
}
|
||||||
|
else if (state == st_literal)
|
||||||
|
{
|
||||||
|
if (strchr(" \t\n\v\f\r()<>[]{}/%", ch) != 0)
|
||||||
|
{
|
||||||
|
// A C-loacle whitespace character or delimiter terminates
|
||||||
|
// token. It is important to unread the whitespace
|
||||||
|
// character even though it is ignored since it may be the
|
||||||
|
// newline after a stream keyword. Removing it here could
|
||||||
|
// make the stream-reading code break on some files,
|
||||||
|
// though not on any files in the test suite as of this
|
||||||
|
// writing.
|
||||||
|
|
||||||
|
type = tt_word;
|
||||||
|
unread_char = true;
|
||||||
|
char_to_unread = ch;
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
val += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
handled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (handled)
|
||||||
|
{
|
||||||
|
// okay
|
||||||
|
}
|
||||||
|
else if (state == st_in_hexstring)
|
||||||
|
{
|
||||||
|
if (ch == '>')
|
||||||
|
{
|
||||||
|
type = tt_string;
|
||||||
|
state = st_token_ready;
|
||||||
|
if (val.length() % 2)
|
||||||
|
{
|
||||||
|
// PDF spec says odd hexstrings have implicit
|
||||||
|
// trailing 0.
|
||||||
|
val += '0';
|
||||||
|
}
|
||||||
|
char num[3];
|
||||||
|
num[2] = '\0';
|
||||||
|
std::string nval;
|
||||||
|
for (unsigned int i = 0; i < val.length(); i += 2)
|
||||||
|
{
|
||||||
|
num[0] = val[i];
|
||||||
|
num[1] = val[i+1];
|
||||||
|
char nch = (char)(strtol(num, 0, 16));
|
||||||
|
nval += nch;
|
||||||
|
}
|
||||||
|
val = nval;
|
||||||
|
}
|
||||||
|
else if (is_hex_digit(ch))
|
||||||
|
{
|
||||||
|
val += ch;
|
||||||
|
}
|
||||||
|
else if (strchr(" \t\n\v\f\r", ch))
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type = tt_bad;
|
||||||
|
QTC::TC("qpdf", "QPDF_Tokenizer bad (");
|
||||||
|
error_message = std::string("invalid character (") +
|
||||||
|
ch + ") in hexstring";
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("invalid state while reading token");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((state == st_token_ready) && (type == tt_word))
|
||||||
|
{
|
||||||
|
if ((val.length() > 0) && (val[0] == '/'))
|
||||||
|
{
|
||||||
|
type = tt_name;
|
||||||
|
// Deal with # in name token. Note: '/' by itself is a
|
||||||
|
// valid name, so don't strip leading /. That way we
|
||||||
|
// don't have to deal with the empty string as a name.
|
||||||
|
std::string nval = "/";
|
||||||
|
char const* valstr = val.c_str() + 1;
|
||||||
|
for (char const* p = valstr; *p; ++p)
|
||||||
|
{
|
||||||
|
if ((*p == '#') && this->pound_special_in_name)
|
||||||
|
{
|
||||||
|
if (p[1] && p[2] &&
|
||||||
|
is_hex_digit(p[1]) && is_hex_digit(p[2]))
|
||||||
|
{
|
||||||
|
char num[3];
|
||||||
|
num[0] = p[1];
|
||||||
|
num[1] = p[2];
|
||||||
|
num[2] = '\0';
|
||||||
|
char ch = (char)(strtol(num, 0, 16));
|
||||||
|
if (ch == '\0')
|
||||||
|
{
|
||||||
|
type = tt_bad;
|
||||||
|
QTC::TC("qpdf", "QPDF_Tokenizer null in name");
|
||||||
|
error_message =
|
||||||
|
"null character not allowed in name token";
|
||||||
|
nval += "#00";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nval += ch;
|
||||||
|
}
|
||||||
|
p += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDF_Tokenizer bad name");
|
||||||
|
type = tt_bad;
|
||||||
|
error_message = "invalid name token";
|
||||||
|
nval += *p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nval += *p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val = nval;
|
||||||
|
}
|
||||||
|
else if (num_re.match(val.c_str()))
|
||||||
|
{
|
||||||
|
if (val.find('.') != std::string::npos)
|
||||||
|
{
|
||||||
|
type = tt_real;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type = tt_integer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((val == "true") || (val == "false"))
|
||||||
|
{
|
||||||
|
type = tt_bool;
|
||||||
|
}
|
||||||
|
else if (val == "null")
|
||||||
|
{
|
||||||
|
type = tt_null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// I don't really know what it is, so leave it as tt_word.
|
||||||
|
// Lots of cases ($, #, etc.) other than actual words fall
|
||||||
|
// into this category, but that's okay at least for now.
|
||||||
|
type = tt_word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! (betweenTokens() || ((state == st_token_ready) && unread_char)))
|
||||||
|
{
|
||||||
|
this->raw_val += orig_ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFTokenizer::presentEOF()
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case st_token_ready:
|
||||||
|
case st_top:
|
||||||
|
// okay
|
||||||
|
break;
|
||||||
|
|
||||||
|
case st_in_comment:
|
||||||
|
state = st_top;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
type = tt_bad;
|
||||||
|
error_message = "EOF while reading token";
|
||||||
|
state = st_token_ready;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFTokenizer::getToken(Token& token, bool& unread_char, char& ch)
|
||||||
|
{
|
||||||
|
bool ready = (this->state == st_token_ready);
|
||||||
|
unread_char = this->unread_char;
|
||||||
|
ch = this->char_to_unread;
|
||||||
|
if (ready)
|
||||||
|
{
|
||||||
|
token = Token(type, val, raw_val, error_message);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFTokenizer::betweenTokens()
|
||||||
|
{
|
||||||
|
return ((state == st_top) || (state == st_in_comment));
|
||||||
|
}
|
2021
libqpdf/QPDFWriter.cc
Normal file
2021
libqpdf/QPDFWriter.cc
Normal file
File diff suppressed because it is too large
Load Diff
61
libqpdf/QPDFXRefEntry.cc
Normal file
61
libqpdf/QPDFXRefEntry.cc
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDFXRefEntry.hh>
|
||||||
|
#include <qpdf/QPDFExc.hh>
|
||||||
|
#include <qpdf/QUtil.hh>
|
||||||
|
|
||||||
|
QPDFXRefEntry::QPDFXRefEntry() :
|
||||||
|
type(0),
|
||||||
|
field1(0),
|
||||||
|
field2(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFXRefEntry::QPDFXRefEntry(int type, int field1, int field2) :
|
||||||
|
type(type),
|
||||||
|
field1(field1),
|
||||||
|
field2(field2)
|
||||||
|
{
|
||||||
|
if ((type < 1) || (type > 2))
|
||||||
|
{
|
||||||
|
throw QPDFExc("invalid xref type " + QUtil::int_to_string(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
QPDFXRefEntry::getType() const
|
||||||
|
{
|
||||||
|
return this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
QPDFXRefEntry::getOffset() const
|
||||||
|
{
|
||||||
|
if (this->type != 1)
|
||||||
|
{
|
||||||
|
throw QPDFExc(
|
||||||
|
"getOffset called for xref entry of type != 1");
|
||||||
|
}
|
||||||
|
return this->field1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
QPDFXRefEntry::getObjStreamNumber() const
|
||||||
|
{
|
||||||
|
if (this->type != 2)
|
||||||
|
{
|
||||||
|
throw QPDFExc(
|
||||||
|
"getObjStreamNumber called for xref entry of type != 2");
|
||||||
|
}
|
||||||
|
return this->field1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
QPDFXRefEntry::getObjStreamIndex() const
|
||||||
|
{
|
||||||
|
if (this->type != 2)
|
||||||
|
{
|
||||||
|
throw QPDFExc(
|
||||||
|
"getObjStreamIndex called for xref entry of type != 2");
|
||||||
|
}
|
||||||
|
return this->field2;
|
||||||
|
}
|
51
libqpdf/QPDF_Array.cc
Normal file
51
libqpdf/QPDF_Array.cc
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDF_Array.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
|
||||||
|
QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& items) :
|
||||||
|
items(items)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDF_Array::~QPDF_Array()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Array::unparse()
|
||||||
|
{
|
||||||
|
std::string result = "[ ";
|
||||||
|
for (std::vector<QPDFObjectHandle>::iterator iter = this->items.begin();
|
||||||
|
iter != this->items.end(); ++iter)
|
||||||
|
{
|
||||||
|
result += (*iter).unparse();
|
||||||
|
result += " ";
|
||||||
|
}
|
||||||
|
result += "]";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
QPDF_Array::getNItems() const
|
||||||
|
{
|
||||||
|
return this->items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDF_Array::getItem(int n) const
|
||||||
|
{
|
||||||
|
if ((n < 0) || (n >= (int)this->items.size()))
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("bounds array accessing QPDF_Array element");
|
||||||
|
}
|
||||||
|
return this->items[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDF_Array::setItem(int n, QPDFObjectHandle const& oh)
|
||||||
|
{
|
||||||
|
// Call getItem for bounds checking
|
||||||
|
(void) getItem(n);
|
||||||
|
this->items[n] = oh;
|
||||||
|
}
|
23
libqpdf/QPDF_Bool.cc
Normal file
23
libqpdf/QPDF_Bool.cc
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDF_Bool.hh>
|
||||||
|
|
||||||
|
QPDF_Bool::QPDF_Bool(bool val) :
|
||||||
|
val(val)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDF_Bool::~QPDF_Bool()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Bool::unparse()
|
||||||
|
{
|
||||||
|
return (val ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDF_Bool::getVal() const
|
||||||
|
{
|
||||||
|
return this->val;
|
||||||
|
}
|
84
libqpdf/QPDF_Dictionary.cc
Normal file
84
libqpdf/QPDF_Dictionary.cc
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDF_Dictionary.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QPDF_Null.hh>
|
||||||
|
#include <qpdf/QPDF_Name.hh>
|
||||||
|
|
||||||
|
QPDF_Dictionary::QPDF_Dictionary(
|
||||||
|
std::map<std::string, QPDFObjectHandle> const& items) :
|
||||||
|
items(items)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDF_Dictionary::~QPDF_Dictionary()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Dictionary::unparse()
|
||||||
|
{
|
||||||
|
std::string result = "<< ";
|
||||||
|
for (std::map<std::string, QPDFObjectHandle>::iterator iter =
|
||||||
|
this->items.begin();
|
||||||
|
iter != this->items.end(); ++iter)
|
||||||
|
{
|
||||||
|
result += QPDF_Name::normalizeName((*iter).first) +
|
||||||
|
" " + (*iter).second.unparse() + " ";
|
||||||
|
}
|
||||||
|
result += ">>";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDF_Dictionary::hasKey(std::string const& key)
|
||||||
|
{
|
||||||
|
return ((this->items.count(key) > 0) &&
|
||||||
|
(! this->items[key].isNull()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDF_Dictionary::getKey(std::string const& key)
|
||||||
|
{
|
||||||
|
// PDF spec says fetching a non-existent key from a dictionary
|
||||||
|
// returns the null object.
|
||||||
|
if (this->items.count(key))
|
||||||
|
{
|
||||||
|
// May be a null object
|
||||||
|
return (*(this->items.find(key))).second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return QPDFObjectHandle::newNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::string>
|
||||||
|
QPDF_Dictionary::getKeys()
|
||||||
|
{
|
||||||
|
std::set<std::string> result;
|
||||||
|
for (std::map<std::string, QPDFObjectHandle>::const_iterator iter =
|
||||||
|
this->items.begin();
|
||||||
|
iter != this->items.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (hasKey((*iter).first))
|
||||||
|
{
|
||||||
|
result.insert((*iter).first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDF_Dictionary::replaceKey(std::string const& key,
|
||||||
|
QPDFObjectHandle const& value)
|
||||||
|
{
|
||||||
|
// add or replace value
|
||||||
|
this->items[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDF_Dictionary::removeKey(std::string const& key)
|
||||||
|
{
|
||||||
|
// no-op if key does not exist
|
||||||
|
this->items.erase(key);
|
||||||
|
}
|
25
libqpdf/QPDF_Integer.cc
Normal file
25
libqpdf/QPDF_Integer.cc
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDF_Integer.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QUtil.hh>
|
||||||
|
|
||||||
|
QPDF_Integer::QPDF_Integer(int val) :
|
||||||
|
val(val)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDF_Integer::~QPDF_Integer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Integer::unparse()
|
||||||
|
{
|
||||||
|
return QUtil::int_to_string(this->val);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
QPDF_Integer::getVal() const
|
||||||
|
{
|
||||||
|
return this->val;
|
||||||
|
}
|
46
libqpdf/QPDF_Name.cc
Normal file
46
libqpdf/QPDF_Name.cc
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDF_Name.hh>
|
||||||
|
|
||||||
|
QPDF_Name::QPDF_Name(std::string const& name) :
|
||||||
|
name(name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDF_Name::~QPDF_Name()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Name::normalizeName(std::string const& name)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
char num[4];
|
||||||
|
result += name[0];
|
||||||
|
for (unsigned int i = 1; i < name.length(); ++i)
|
||||||
|
{
|
||||||
|
char ch = name[i];
|
||||||
|
// Don't use locale/ctype here; follow PDF spec guidlines.
|
||||||
|
if (strchr("#()<>[]{}/%", ch) || (ch < 33) || (ch > 126))
|
||||||
|
{
|
||||||
|
sprintf(num, "#%02x", (unsigned char) ch);
|
||||||
|
result += num;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Name::unparse()
|
||||||
|
{
|
||||||
|
return normalizeName(this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Name::getName() const
|
||||||
|
{
|
||||||
|
return this->name;
|
||||||
|
}
|
12
libqpdf/QPDF_Null.cc
Normal file
12
libqpdf/QPDF_Null.cc
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDF_Null.hh>
|
||||||
|
|
||||||
|
QPDF_Null::~QPDF_Null()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Null::unparse()
|
||||||
|
{
|
||||||
|
return "null";
|
||||||
|
}
|
23
libqpdf/QPDF_Real.cc
Normal file
23
libqpdf/QPDF_Real.cc
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDF_Real.hh>
|
||||||
|
|
||||||
|
QPDF_Real::QPDF_Real(std::string const& val) :
|
||||||
|
val(val)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDF_Real::~QPDF_Real()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Real::unparse()
|
||||||
|
{
|
||||||
|
return this->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Real::getVal()
|
||||||
|
{
|
||||||
|
return this->val;
|
||||||
|
}
|
309
libqpdf/QPDF_Stream.cc
Normal file
309
libqpdf/QPDF_Stream.cc
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
|
||||||
|
#include <qpdf/QPDF_Stream.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QEXC.hh>
|
||||||
|
#include <qpdf/QUtil.hh>
|
||||||
|
#include <qpdf/Pipeline.hh>
|
||||||
|
#include <qpdf/Pl_Flate.hh>
|
||||||
|
#include <qpdf/Pl_PNGFilter.hh>
|
||||||
|
#include <qpdf/Pl_RC4.hh>
|
||||||
|
#include <qpdf/Pl_Buffer.hh>
|
||||||
|
#include <qpdf/Pl_ASCII85Decoder.hh>
|
||||||
|
#include <qpdf/Pl_ASCIIHexDecoder.hh>
|
||||||
|
#include <qpdf/Pl_LZWDecoder.hh>
|
||||||
|
|
||||||
|
#include <qpdf/QTC.hh>
|
||||||
|
#include <qpdf/QPDF.hh>
|
||||||
|
#include <qpdf/QPDFExc.hh>
|
||||||
|
#include <qpdf/Pl_QPDFTokenizer.hh>
|
||||||
|
|
||||||
|
QPDF_Stream::QPDF_Stream(QPDF* qpdf, int objid, int generation,
|
||||||
|
QPDFObjectHandle stream_dict,
|
||||||
|
off_t offset, int length) :
|
||||||
|
qpdf(qpdf),
|
||||||
|
objid(objid),
|
||||||
|
generation(generation),
|
||||||
|
stream_dict(stream_dict),
|
||||||
|
offset(offset),
|
||||||
|
length(length)
|
||||||
|
{
|
||||||
|
if (! stream_dict.isDictionary())
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("stream object instantiated with non-dictionary "
|
||||||
|
"object for dictionary");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDF_Stream::~QPDF_Stream()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
QPDF_Stream::unparse()
|
||||||
|
{
|
||||||
|
// Unparse stream objects as indirect references
|
||||||
|
return QUtil::int_to_string(this->objid) + " " +
|
||||||
|
QUtil::int_to_string(this->generation) + " R";
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFObjectHandle
|
||||||
|
QPDF_Stream::getDict() const
|
||||||
|
{
|
||||||
|
return this->stream_dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
PointerHolder<Buffer>
|
||||||
|
QPDF_Stream::getStreamData()
|
||||||
|
{
|
||||||
|
Pl_Buffer buf("stream data buffer");
|
||||||
|
if (! pipeStreamData(&buf, true, false, false))
|
||||||
|
{
|
||||||
|
throw QPDFExc("getStreamData called on unfilterable stream");
|
||||||
|
}
|
||||||
|
return buf.getBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDF_Stream::filterable(std::vector<std::string>& filters,
|
||||||
|
int& predictor, int& columns,
|
||||||
|
bool& early_code_change)
|
||||||
|
{
|
||||||
|
// Initialize values to their defaults as per the PDF spec
|
||||||
|
predictor = 1;
|
||||||
|
columns = 0;
|
||||||
|
early_code_change = true;
|
||||||
|
|
||||||
|
bool filterable = true;
|
||||||
|
|
||||||
|
// See if we can support any decode parameters that are specified.
|
||||||
|
|
||||||
|
QPDFObjectHandle decode_obj =
|
||||||
|
this->stream_dict.getKey("/DecodeParms");
|
||||||
|
if (decode_obj.isNull())
|
||||||
|
{
|
||||||
|
// no problem
|
||||||
|
}
|
||||||
|
else if (decode_obj.isDictionary())
|
||||||
|
{
|
||||||
|
std::set<std::string> keys = decode_obj.getKeys();
|
||||||
|
for (std::set<std::string>::iterator iter = keys.begin();
|
||||||
|
iter != keys.end(); ++iter)
|
||||||
|
{
|
||||||
|
std::string const& key = *iter;
|
||||||
|
if (key == "/Predictor")
|
||||||
|
{
|
||||||
|
QPDFObjectHandle predictor_obj = decode_obj.getKey(key);
|
||||||
|
if (predictor_obj.isInteger())
|
||||||
|
{
|
||||||
|
predictor = predictor_obj.getIntValue();
|
||||||
|
if (! ((predictor == 1) || (predictor == 12)))
|
||||||
|
{
|
||||||
|
filterable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filterable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (key == "/EarlyChange")
|
||||||
|
{
|
||||||
|
QPDFObjectHandle earlychange_obj = decode_obj.getKey(key);
|
||||||
|
if (earlychange_obj.isInteger())
|
||||||
|
{
|
||||||
|
int earlychange = earlychange_obj.getIntValue();
|
||||||
|
early_code_change = (earlychange == 1);
|
||||||
|
if (! ((earlychange == 0) || (earlychange == 1)))
|
||||||
|
{
|
||||||
|
filterable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filterable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (key == "/Columns")
|
||||||
|
{
|
||||||
|
QPDFObjectHandle columns_obj = decode_obj.getKey(key);
|
||||||
|
if (columns_obj.isInteger())
|
||||||
|
{
|
||||||
|
columns = columns_obj.getIntValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filterable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filterable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw QPDFExc(qpdf->getFilename(), this->offset,
|
||||||
|
"invalid decode parameters object type for this stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((predictor > 1) && (columns == 0))
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
filterable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! filterable)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check filters
|
||||||
|
|
||||||
|
QPDFObjectHandle filter_obj = this->stream_dict.getKey("/Filter");
|
||||||
|
bool filters_okay = true;
|
||||||
|
|
||||||
|
if (filter_obj.isNull())
|
||||||
|
{
|
||||||
|
// No filters
|
||||||
|
}
|
||||||
|
else if (filter_obj.isName())
|
||||||
|
{
|
||||||
|
// One filter
|
||||||
|
filters.push_back(filter_obj.getName());
|
||||||
|
}
|
||||||
|
else if (filter_obj.isArray())
|
||||||
|
{
|
||||||
|
// Potentially multiple filters
|
||||||
|
int n = filter_obj.getArrayNItems();
|
||||||
|
for (int i = 0; i < n; ++i)
|
||||||
|
{
|
||||||
|
QPDFObjectHandle item = filter_obj.getArrayItem(i);
|
||||||
|
if (item.isName())
|
||||||
|
{
|
||||||
|
filters.push_back(item.getName());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filters_okay = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filters_okay = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! filters_okay)
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDF_Stream invalid filter");
|
||||||
|
throw QPDFExc(qpdf->getFilename(), this->offset,
|
||||||
|
"invalid filter object type for this stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
// `filters' now contains a list of filters to be applied in
|
||||||
|
// order. See which ones we can support.
|
||||||
|
|
||||||
|
for (std::vector<std::string>::iterator iter = filters.begin();
|
||||||
|
iter != filters.end(); ++iter)
|
||||||
|
{
|
||||||
|
std::string const& filter = *iter;
|
||||||
|
if (! ((filter == "/FlateDecode") ||
|
||||||
|
(filter == "/LZWDecode") ||
|
||||||
|
(filter == "/ASCII85Decode") ||
|
||||||
|
(filter == "/ASCIIHexDecode")))
|
||||||
|
{
|
||||||
|
filterable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool filter,
|
||||||
|
bool normalize, bool compress)
|
||||||
|
{
|
||||||
|
std::vector<std::string> filters;
|
||||||
|
int predictor = 1;
|
||||||
|
int columns = 0;
|
||||||
|
bool early_code_change = true;
|
||||||
|
if (filter)
|
||||||
|
{
|
||||||
|
filter = filterable(filters, predictor, columns, early_code_change);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipeline == 0)
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDF_Stream pipeStreamData with null pipeline");
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the pipeline in reverse order. Force pipelines we
|
||||||
|
// create to be deleted when this function finishes.
|
||||||
|
std::vector<PointerHolder<Pipeline> > to_delete;
|
||||||
|
|
||||||
|
if (filter)
|
||||||
|
{
|
||||||
|
if (compress)
|
||||||
|
{
|
||||||
|
pipeline = new Pl_Flate("compress object stream", pipeline,
|
||||||
|
Pl_Flate::a_deflate);
|
||||||
|
to_delete.push_back(pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalize)
|
||||||
|
{
|
||||||
|
pipeline = new Pl_QPDFTokenizer("normalizer", pipeline);
|
||||||
|
to_delete.push_back(pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<std::string>::reverse_iterator iter = filters.rbegin();
|
||||||
|
iter != filters.rend(); ++iter)
|
||||||
|
{
|
||||||
|
std::string const& filter = *iter;
|
||||||
|
if (filter == "/FlateDecode")
|
||||||
|
{
|
||||||
|
if (predictor == 12)
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDF_Stream PNG filter");
|
||||||
|
pipeline = new Pl_PNGFilter(
|
||||||
|
"png decode", pipeline, Pl_PNGFilter::a_decode,
|
||||||
|
columns, 0 /* not used */);
|
||||||
|
to_delete.push_back(pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline = new Pl_Flate("stream inflate",
|
||||||
|
pipeline, Pl_Flate::a_inflate);
|
||||||
|
to_delete.push_back(pipeline);
|
||||||
|
}
|
||||||
|
else if (filter == "/ASCII85Decode")
|
||||||
|
{
|
||||||
|
pipeline = new Pl_ASCII85Decoder("ascii85 decode", pipeline);
|
||||||
|
to_delete.push_back(pipeline);
|
||||||
|
}
|
||||||
|
else if (filter == "/ASCIIHexDecode")
|
||||||
|
{
|
||||||
|
pipeline = new Pl_ASCIIHexDecoder("asciiHex decode", pipeline);
|
||||||
|
to_delete.push_back(pipeline);
|
||||||
|
}
|
||||||
|
else if (filter == "/LZWDecode")
|
||||||
|
{
|
||||||
|
pipeline = new Pl_LZWDecoder("lzw decode", pipeline,
|
||||||
|
early_code_change);
|
||||||
|
to_delete.push_back(pipeline);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw QEXC::Internal("QPDFStream: unknown filter "
|
||||||
|
"encountered after check");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDF::Pipe::pipeStreamData(this->qpdf, this->objid, this->generation,
|
||||||
|
this->offset, this->length,
|
||||||
|
this->stream_dict, pipeline);
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user