2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-22 02:49:00 +00:00

Generate help content from manual

This is a massive rewrite of the help text and cli.rst section of the
manual. All command-line flags now have their own help and are
specifically index. qpdf --help is completely redone.
This commit is contained in:
Jay Berkenbilt 2022-01-11 11:49:33 -05:00
parent b4bd124be4
commit c8729398dd
21 changed files with 4336 additions and 1981 deletions

View File

@ -127,7 +127,9 @@ check: $(TEST_TARGETS)
.PHONY: spell .PHONY: spell
# npm install -g cspell; add exceptions to cSpell.json # npm install -g cspell; add exceptions to cSpell.json
spell: spell:
cspell **/*.hh include/qpdf/*.h **/*.cc manual/* ChangeLog README* TODO cspell **/*.hh include/qpdf/*.h **/*.cc \
manual/*.rst manual/*.in manual/_ext/*.py \
ChangeLog README* TODO
# Install targets are in the make directory in the rules-specific make # Install targets are in the make directory in the rules-specific make
# fragments. # fragments.

6
TODO
View File

@ -34,16 +34,14 @@ Documentation
* Consider which parts might be good candidates for moving to the * Consider which parts might be good candidates for moving to the
wiki. wiki.
* See #530 -- add an appendix explaining PDF encryption in general
plus how it's handled by qpdf. Or maybe this should go on the wiki.
Document-level work Document-level work
=================== ===================
* Ideas here may by superseded by #593. * Ideas here may by superseded by #593.
* QPDFPageCopier -- object for moving pages around within files or * QPDFPageCopier -- object for moving pages around within files or
between files and performing various transformations between files and performing various transformations. Reread/rewrite
_page-selection in the manual if needed.
* Handle all the stuff of pages and split-pages * Handle all the stuff of pages and split-pages
* Do n-up, booklet, collation * Do n-up, booklet, collation

View File

@ -54,6 +54,7 @@
"cerr", "cerr",
"cfis", "cfis",
"cflags", "cflags",
"ciphertext",
"classname", "classname",
"clearsign", "clearsign",
"cleartext", "cleartext",
@ -149,6 +150,7 @@
"hosoda", "hosoda",
"htcondor", "htcondor",
"htdocs", "htdocs",
"idempotency",
"ifdefs", "ifdefs",
"ifeq", "ifeq",
"ifstream", "ifstream",

View File

@ -19,10 +19,16 @@ def warn(*args, **kwargs):
class Main: class Main:
SOURCES = [whoami, 'job.yml', 'manual/cli.rst'] SOURCES = [
whoami,
'manual/_ext/qpdf.py',
'job.yml',
'manual/cli.rst',
]
DESTS = { DESTS = {
'decl': 'libqpdf/qpdf/auto_job_decl.hh', 'decl': 'libqpdf/qpdf/auto_job_decl.hh',
'init': 'libqpdf/qpdf/auto_job_init.hh', 'init': 'libqpdf/qpdf/auto_job_init.hh',
'help': 'libqpdf/qpdf/auto_job_help.hh',
} }
SUMS = 'job.sums' SUMS = 'job.sums'
@ -100,14 +106,22 @@ class Main:
short_text = None short_text = None
long_text = None long_text = None
print('this->ap.addHelpFooter("For detailed help, visit' # Generate a bunch of short static functions rather than a big
' the qpdf manual: https://qpdf.readthedocs.io\\n");', file=f) # member function for help. Some compilers have problems with
# very large member functions in classes in anonymous
# namespaces.
help_files = 0
help_lines = 0
self.all_topics = set(self.options_without_help)
self.referenced_topics = set()
def set_indent(x): def set_indent(x):
nonlocal indent nonlocal indent
indent = ' ' * len(x) indent = ' ' * len(x)
def append_long_text(line): def append_long_text(line, topic):
nonlocal indent, long_text nonlocal indent, long_text
if line == '\n': if line == '\n':
long_text += '\n' long_text += '\n'
@ -115,13 +129,23 @@ class Main:
long_text += line[len(indent):] long_text += line[len(indent):]
else: else:
long_text = long_text.strip() long_text = long_text.strip()
if long_text != '': if long_text == '':
long_text += '\n' raise Exception(f'missing long text for {topic}')
long_text += '\n'
for i in re.finditer(r'--help=([^\.\s]+)', long_text):
self.referenced_topics.add(i.group(1))
return True return True
return False return False
lineno = 0 lineno = 0
for line in df.readlines(): for line in df.readlines():
if help_lines == 0:
if help_files > 0:
print('}', file=f)
help_files += 1
help_lines += 1
print(f'static void add_help_{help_files}(QPDFArgParser& ap)\n'
'{', file=f)
lineno += 1 lineno += 1
if state == st_top: if state == st_top:
m = re.match(r'^(\s*\.\. )help-topic (\S+): (.*)$', line) m = re.match(r'^(\s*\.\. )help-topic (\S+): (.*)$', line)
@ -132,8 +156,9 @@ class Main:
long_text = '' long_text = ''
state = st_topic state = st_topic
continue continue
m = re.match(r'^(\s*\.\. )qpdf:option:: (([^=\s]+)(=(\S+))?)$', m = re.match(
line) r'^(\s*\.\. )qpdf:option:: (([^=\s]+)([= ](.+))?)$',
line)
if m: if m:
if topic is None: if topic is None:
raise Exception('option seen before topic') raise Exception('option seen before topic')
@ -150,9 +175,11 @@ class Main:
state = st_option state = st_option
continue continue
elif state == st_topic: elif state == st_topic:
if append_long_text(line): if append_long_text(line, topic):
print(f'this->ap.addHelpTopic("{topic}", "{short_text}",' self.all_topics.add(topic)
print(f'ap.addHelpTopic("{topic}", "{short_text}",'
f' R"({long_text})");', file=f) f' R"({long_text})");', file=f)
help_lines += 1
state = st_top state = st_top
elif state == st_option: elif state == st_option:
if line == '\n' or line.startswith(indent): if line == '\n' or line.startswith(indent):
@ -162,12 +189,36 @@ class Main:
short_text = m.group(2) short_text = m.group(2)
state = st_option_help state = st_option_help
else: else:
raise Exception('option without help text')
state = st_top state = st_top
elif state == st_option_help: elif state == st_option_help:
if append_long_text(line): if append_long_text(line, option):
print(f'this->ap.addOptionHelp("{option}", "{topic}",' if option in self.options_without_help:
self.options_without_help.remove(option)
else:
raise Exception(
f'help for unknown option {option},'
f' lineno={lineno}')
print(f'ap.addOptionHelp("{option}", "{topic}",'
f' "{short_text}", R"({long_text})");', file=f) f' "{short_text}", R"({long_text})");', file=f)
help_lines += 1
state = st_top state = st_top
if help_lines == 20:
help_lines = 0
print('}', file=f)
print('static void add_help(QPDFArgParser& ap)\n{', file=f)
for i in range(help_files):
print(f' add_help_{i+1}(ap);', file=f)
print('ap.addHelpFooter("For detailed help, visit'
' the qpdf manual: https://qpdf.readthedocs.io\\n");', file=f)
print('}\n', file=f)
for i in self.referenced_topics:
if i not in self.all_topics:
raise Exception(f'help text referenced --help={i}')
for i in self.options_without_help:
raise Exception(
'Options without help: ' +
', '.join(self.options_without_help))
def generate(self): def generate(self):
warn(f'{whoami}: regenerating auto job files') warn(f'{whoami}: regenerating auto job files')
@ -175,12 +226,19 @@ class Main:
with open('job.yml', 'r') as f: with open('job.yml', 'r') as f:
data = yaml.safe_load(f.read()) data = yaml.safe_load(f.read())
self.validate(data) self.validate(data)
self.options_without_help = set(
['--completion-bash', '--completion-zsh', '--help']
)
with open(self.DESTS['decl'], 'w') as f: with open(self.DESTS['decl'], 'w') as f:
print(BANNER, file=f) print(BANNER, file=f)
self.generate_decl(data, f) self.generate_decl(data, f)
with open(self.DESTS['init'], 'w') as f: with open(self.DESTS['init'], 'w') as f:
print(BANNER, file=f) print(BANNER, file=f)
self.generate_init(data, f) self.generate_init(data, f)
with open(self.DESTS['help'], 'w') as f:
with open('manual/cli.rst', 'r') as df:
print(BANNER, file=f)
self.generate_doc(df, f)
# Update hashes last to ensure that this will be rerun in the # Update hashes last to ensure that this will be rerun in the
# event of a failure. # event of a failure.
@ -275,24 +333,29 @@ class Main:
print('this->ap.addPositional(' print('this->ap.addPositional('
f'p(&ArgParser::{prefix}Positional));', file=f) f'p(&ArgParser::{prefix}Positional));', file=f)
for i in o.get('bare', []): for i in o.get('bare', []):
self.options_without_help.add(f'--{i}')
identifier = self.to_identifier(i, prefix, False) identifier = self.to_identifier(i, prefix, False)
print(f'this->ap.addBare("{i}", ' print(f'this->ap.addBare("{i}", '
f'b(&ArgParser::{identifier}));', file=f) f'b(&ArgParser::{identifier}));', file=f)
for i in o.get('optional_parameter', []): for i in o.get('optional_parameter', []):
self.options_without_help.add(f'--{i}')
identifier = self.to_identifier(i, prefix, False) identifier = self.to_identifier(i, prefix, False)
print(f'this->ap.addOptionalParameter("{i}", ' print(f'this->ap.addOptionalParameter("{i}", '
f'p(&ArgParser::{identifier}));', file=f) f'p(&ArgParser::{identifier}));', file=f)
for k, v in o.get('required_parameter', {}).items(): for k, v in o.get('required_parameter', {}).items():
self.options_without_help.add(f'--{k}')
identifier = self.to_identifier(k, prefix, False) identifier = self.to_identifier(k, prefix, False)
print(f'this->ap.addRequiredParameter("{k}", ' print(f'this->ap.addRequiredParameter("{k}", '
f'p(&ArgParser::{identifier})' f'p(&ArgParser::{identifier})'
f', "{v}");', file=f) f', "{v}");', file=f)
for k, v in o.get('required_choices', {}).items(): for k, v in o.get('required_choices', {}).items():
self.options_without_help.add(f'--{k}')
identifier = self.to_identifier(k, prefix, False) identifier = self.to_identifier(k, prefix, False)
print(f'this->ap.addChoices("{k}", ' print(f'this->ap.addChoices("{k}", '
f'p(&ArgParser::{identifier})' f'p(&ArgParser::{identifier})'
f', true, {v}_choices);', file=f) f', true, {v}_choices);', file=f)
for k, v in o.get('optional_choices', {}).items(): for k, v in o.get('optional_choices', {}).items():
self.options_without_help.add(f'--{k}')
identifier = self.to_identifier(k, prefix, False) identifier = self.to_identifier(k, prefix, False)
print(f'this->ap.addChoices("{k}", ' print(f'this->ap.addChoices("{k}", '
f'p(&ArgParser::{identifier})' f'p(&ArgParser::{identifier})'
@ -312,8 +375,6 @@ class Main:
for j in ft['options']: for j in ft['options']:
print('this->ap.copyFromOtherTable' print('this->ap.copyFromOtherTable'
f'("{j}", "{other_table}");', file=f) f'("{j}", "{other_table}");', file=f)
with open('manual/cli.rst', 'r') as df:
self.generate_doc(df, f)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -1,6 +1,8 @@
# Generated by generate_auto_job # Generated by generate_auto_job
generate_auto_job 1f42fc554778d95210d11c44e858214b4854ead907d1c9ea84fe37f993ea1a23 generate_auto_job 466aa9211549cebeb3fedc6413108981aeeddd89936621095f5f5223cee9880b
job.yml 25c85cba1ae01dac9cd0f9cb7b734e7e3e531c0023ea2b892dc0d40bda1c1146 job.yml 25c85cba1ae01dac9cd0f9cb7b734e7e3e531c0023ea2b892dc0d40bda1c1146
libqpdf/qpdf/auto_job_decl.hh 97395ecbe590b23ae04d6cce2080dbd0e998917ff5eeaa5c6aafa91041d3cd6a libqpdf/qpdf/auto_job_decl.hh 97395ecbe590b23ae04d6cce2080dbd0e998917ff5eeaa5c6aafa91041d3cd6a
libqpdf/qpdf/auto_job_init.hh 2afffb5002ff28a3909f709709f65d77bf2289dd72d5ea3d1598a36664a49c73 libqpdf/qpdf/auto_job_help.hh fa7ff1d1f6289881ac3a485107d15240c4992c59cff506be425354557108d184
manual/cli.rst f0109cca3366a9da4b0a05e3cce996ece2d776321a3f689aeaa2d6af599eee88 libqpdf/qpdf/auto_job_init.hh 465bf46769559ceb77110d1b9d3293ba9b3595850b49848c31aeabd10aadb4ad
manual/_ext/qpdf.py 855fe12de5af7a10bb24be6ecc4d5dff4c84ac58cf388a13be6bbb394346a67d
manual/cli.rst c26e877d2065ac917edffdd6a037d2191b64d7c25beb4e8df1acc174b20b3ff4

View File

@ -967,21 +967,20 @@ void
QPDFArgParser::getAllHelp(std::ostringstream& msg) QPDFArgParser::getAllHelp(std::ostringstream& msg)
{ {
getTopHelp(msg); getTopHelp(msg);
auto show = [this, &msg](std::map<std::string, HelpTopic>& topics, auto show = [this, &msg](std::map<std::string, HelpTopic>& topics) {
std::string const& label) {
for (auto const& i: topics) for (auto const& i: topics)
{ {
auto const& topic = i.first; auto const& topic = i.first;
msg << std::endl msg << std::endl
<< "== " << label << " " << topic << "== " << topic
<< " (" << i.second.short_text << ") ==" << " (" << i.second.short_text << ") =="
<< std::endl << std::endl
<< std::endl; << std::endl;
getTopicHelp(topic, i.second, msg); getTopicHelp(topic, i.second, msg);
} }
}; };
show(this->m->help_topics, "topic"); show(this->m->help_topics);
show(this->m->option_help, "option"); show(this->m->option_help);
msg << std::endl << "====" << std::endl; msg << std::endl << "====" << std::endl;
} }

View File

@ -48,6 +48,8 @@ ArgParser::ArgParser(QPDFArgParser& ap, QPDFJob& o) :
initOptionTables(); initOptionTables();
} }
#include <qpdf/auto_job_help.hh>
void void
ArgParser::initOptionTables() ArgParser::initOptionTables()
{ {
@ -55,6 +57,8 @@ ArgParser::initOptionTables()
# include <qpdf/auto_job_init.hh> # include <qpdf/auto_job_init.hh>
this->ap.addFinalCheck( this->ap.addFinalCheck(
QPDFArgParser::bindBare(&ArgParser::doFinalChecks, this)); QPDFArgParser::bindBare(&ArgParser::doFinalChecks, this));
// add_help is defined in auto_job_help.hh
add_help(this->ap);
} }
void void
@ -127,513 +131,6 @@ ArgParser::argCopyright()
<< std::endl; << std::endl;
} }
#if 0
void
ArgParser::argHelp()
{
// QXXXQ
std::cout
// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
<< "Usage: qpdf [options] {infile | --empty} [page_selection_options] outfile\n"
<< "\n"
<< "An option summary appears below. Please see the documentation for details.\n"
<< "\n"
<< "If @filename appears anywhere in the command-line, each line of filename\n"
<< "will be interpreted as an argument. No interpolation is done. Line\n"
<< "terminators are stripped, but leading and trailing whitespace is\n"
<< "intentionally preserved. @- can be specified to read from standard input.\n"
<< "\n"
<< "The output file can be - to indicate writing to standard output, or it can\n"
<< "be --replace-input to cause qpdf to replace the input file with the output.\n"
<< "\n"
<< "Note that when contradictory options are provided, whichever options are\n"
<< "provided last take precedence.\n"
<< "\n"
<< "\n"
<< "Basic Options\n"
<< "-------------\n"
<< "\n"
<< "--version show version of qpdf\n"
<< "--copyright show qpdf's copyright and license information\n"
<< "--help show command-line argument help\n"
<< "--show-crypto show supported crypto providers; default is first\n"
<< "--completion-bash output a bash complete command you can eval\n"
<< "--completion-zsh output a zsh complete command you can eval\n"
<< "--password=password specify a password for accessing encrypted files\n"
<< "--password-file=file get the password the first line \"file\"; use \"-\"\n"
<< " to read the password from stdin (without prompt or\n"
<< " disabling echo, so use with caution)\n"
<< "--is-encrypted silently exit 0 if the file is encrypted or 2\n"
<< " if not; useful for shell scripts\n"
<< "--requires-password silently exit 0 if a password (other than as\n"
<< " supplied) is required, 2 if the file is not\n"
<< " encrypted, or 3 if the file is encrypted\n"
<< " but requires no password or the supplied password\n"
<< " is correct; useful for shell scripts\n"
<< "--verbose provide additional informational output\n"
<< "--progress give progress indicators while writing output\n"
<< "--no-warn suppress warnings\n"
<< "--warning-exit-0 exit with code 0 instead of 3 if there are warnings\n"
<< "--linearize generated a linearized (web optimized) file\n"
<< "--replace-input use in place of specifying an output file; qpdf will\n"
<< " replace the input file with the output\n"
<< "--copy-encryption=file copy encryption parameters from specified file\n"
<< "--encryption-file-password=password\n"
<< " password used to open the file from which encryption\n"
<< " parameters are being copied\n"
<< "--allow-weak-crypto allow creation of files using weak cryptographic\n"
<< " algorithms\n"
<< "--encrypt options -- generate an encrypted file\n"
<< "--decrypt remove any encryption on the file\n"
<< "--password-is-hex-key treat primary password option as a hex-encoded key\n"
<< "--suppress-password-recovery\n"
<< " do not attempt recovering from password string\n"
<< " encoding errors\n"
<< "--password-mode=mode control qpdf's encoding of passwords\n"
<< "--pages options -- select specific pages from one or more files\n"
<< "--collate=n causes files specified in --pages to be collated\n"
<< " in groups of n pages (default 1) rather than\n"
<< " concatenated\n"
<< "--flatten-rotation move page rotation from /Rotate key to content\n"
<< "--rotate=[+|-]angle[:page-range]\n"
<< " rotate each specified page 0, 90, 180, or 270\n"
<< " degrees; rotate all pages if no page range is given\n"
<< "--split-pages=[n] write each output page to a separate file\n"
<< "--overlay options -- overlay pages from another file\n"
<< "--underlay options -- underlay pages from another file\n"
<< "\n"
<< "Note that you can use the @filename or @- syntax for any argument at any\n"
<< "point in the command. This provides a good way to specify a password without\n"
<< "having to explicitly put it on the command line. @filename or @- must be a\n"
<< "word by itself. Syntax such as --arg=@filename doesn't work.\n"
<< "\n"
<< "If none of --copy-encryption, --encrypt or --decrypt are given, qpdf will\n"
<< "preserve any encryption data associated with a file.\n"
<< "\n"
<< "Note that when copying encryption parameters from another file, all\n"
<< "parameters will be copied, including both user and owner passwords, even\n"
<< "if the user password is used to open the other file. This works even if\n"
<< "the owner password is not known.\n"
<< "\n"
<< "The --password-is-hex-key option overrides the normal computation of\n"
<< "encryption keys. It only applies to the password used to open the main\n"
<< "file. This option is not ordinarily useful but can be helpful for forensic\n"
<< "or investigatory purposes. See manual for further discussion.\n"
<< "\n"
<< "The --rotate flag can be used to specify pages to rotate pages either\n"
<< "0, 90, 180, or 270 degrees. The page range is specified in the same\n"
<< "format as with the --pages option, described below. Repeat the option\n"
<< "to rotate multiple groups of pages. If the angle is preceded by + or -,\n"
<< "it is added to or subtracted from the original rotation. Otherwise, the\n"
<< "rotation angle is set explicitly to the given value. You almost always\n"
<< "want to use + or - unless you are certain about the internals of the PDF\n"
<< "you are working with.\n"
<< "\n"
<< "If --split-pages is specified, each page is written to a separate output\n"
<< "file. File names are generated as follows:\n"
<< "* If the string %d appears in the output file name, it is replaced with a\n"
<< " zero-padded page range starting from 1\n"
<< "* Otherwise, if the output file name ends in .pdf (case insensitive), a\n"
<< " zero-padded page range, preceded by a dash, is inserted before the file\n"
<< " extension\n"
<< "* Otherwise, the file name is appended with a zero-padded page range\n"
<< " preceded by a dash.\n"
<< "Page ranges are single page numbers for single-page groups or first-last\n"
<< "for multipage groups.\n"
<< "\n"
<< "\n"
<< "Encryption Options\n"
<< "------------------\n"
<< "\n"
<< " --encrypt user-password owner-password key-length flags --\n"
<< "\n"
<< "Note that -- terminates parsing of encryption flags.\n"
<< "\n"
<< "Either or both of the user password and the owner password may be\n"
<< "empty strings.\n"
<< "\n"
<< "key-length may be 40, 128, or 256\n"
<< "\n"
<< "Additional flags are dependent upon key length.\n"
<< "\n"
<< " If 40:\n"
<< "\n"
<< " --print=[yn] allow printing\n"
<< " --modify=[yn] allow document modification\n"
<< " --extract=[yn] allow text/graphic extraction\n"
<< " --annotate=[yn] allow comments and form fill-in and signing\n"
<< "\n"
<< " If 128:\n"
<< "\n"
<< " --accessibility=[yn] allow accessibility to visually impaired\n"
<< " --extract=[yn] allow other text/graphic extraction\n"
<< " --print=print-opt control printing access\n"
<< " --assemble=[yn] allow document assembly\n"
<< " --annotate=[yn] allow commenting/filling form fields\n"
<< " --form=[yn] allow filling form fields\n"
<< " --modify-other=[yn] allow other modifications\n"
<< " --modify=modify-opt control modify access (old way)\n"
<< " --cleartext-metadata prevents encryption of metadata\n"
<< " --use-aes=[yn] indicates whether to use AES encryption\n"
<< " --force-V4 forces use of V=4 encryption handler\n"
<< "\n"
<< " If 256, options are the same as 128 with these exceptions:\n"
<< " --force-V4 this option is not available with 256-bit keys\n"
<< " --use-aes this option is always on with 256-bit keys\n"
<< " --force-R5 forces use of deprecated R=5 encryption\n"
<< " --allow-insecure allow the owner password to be empty when the\n"
<< " user password is not empty\n"
<< "\n"
<< " print-opt may be:\n"
<< "\n"
<< " full allow full printing\n"
<< " low allow only low-resolution printing\n"
<< " none disallow printing\n"
<< "\n"
<< " modify-opt may be:\n"
<< "\n"
<< " all allow full document modification\n"
<< " annotate allow comment authoring and form operations\n"
<< " form allow form field fill-in and signing\n"
<< " assembly allow document assembly only\n"
<< " none allow no modifications\n"
<< "\n"
<< "The default for each permission option is to be fully permissive. Please\n"
<< "refer to the manual for more details on the modify options.\n"
<< "\n"
<< "Specifying cleartext-metadata forces the PDF version to at least 1.5.\n"
<< "Specifying use of AES forces the PDF version to at least 1.6. These\n"
<< "options are both off by default.\n"
<< "\n"
<< "The --force-V4 flag forces the V=4 encryption handler introduced in PDF 1.5\n"
<< "to be used even if not otherwise needed. This option is primarily useful\n"
<< "for testing qpdf and has no other practical use.\n"
<< "\n"
<< "A warning will be issued if you attempt to encrypt a file with a format that\n"
<< "uses a weak cryptographic algorithm such as RC4. To suppress the warning,\n"
<< "specify the option --allow-weak-crypto. This option is outside of encryption\n"
<< "options (e.g. --allow-week-crypto --encrypt u o 128 --)\n"
<< "\n"
<< "\n"
<< "Password Modes\n"
<< "--------------\n"
<< "\n"
<< "The --password-mode controls how qpdf interprets passwords supplied\n"
<< "on the command-line. qpdf's default behavior is correct in almost all\n"
<< "cases, but you can fine-tune with this option.\n"
<< "\n"
<< " bytes: use the password literally as supplied\n"
<< " hex-bytes: interpret the password as a hex-encoded byte string\n"
<< " unicode: interpret the password as a UTF-8 encoded string\n"
<< " auto: attempt to infer the encoding and adjust as needed\n"
<< "\n"
<< "This is a complex topic. See the manual for a complete discussion.\n"
<< "\n"
<< "\n"
<< "Page Selection Options\n"
<< "----------------------\n"
<< "\n"
<< "These options allow pages to be selected from one or more PDF files.\n"
<< "Whatever file is given as the primary input file is used as the\n"
<< "starting point, but its pages are replaced with pages as specified.\n"
<< "\n"
<< "--keep-files-open=[yn]\n"
<< "--keep-files-open-threshold=count\n"
<< "--pages file [ --password=password ] [ page-range ] ... --\n"
<< "\n"
<< "For each file that pages should be taken from, specify the file, a\n"
<< "password needed to open the file (if any), and a page range. The\n"
<< "password needs to be given only once per file. If any of the input\n"
<< "files are the same as the primary input file or the file used to copy\n"
<< "encryption parameters (if specified), you do not need to repeat the\n"
<< "password here. The same file can be repeated multiple times. The\n"
<< "filename \".\" may be used to refer to the current input file. All\n"
<< "non-page data (info, outlines, page numbers, etc. are taken from the\n"
<< "primary input file. To discard this, use --empty as the primary\n"
<< "input.\n"
<< "\n"
<< "By default, when more than 200 distinct files are specified, qpdf will\n"
<< "close each file when not being referenced. With 200 files or fewer, all\n"
<< "files will be kept open at the same time. This behavior can be overridden\n"
<< "by specifying --keep-files-open=[yn]. Closing and opening files can have\n"
<< "very high overhead on certain file systems, especially networked file\n"
<< "systems. The threshold of 200 can be modified with\n"
<< "--keep-files-open-threshold\n"
<< "\n"
<< "The page range is a set of numbers separated by commas, ranges of\n"
<< "numbers separated dashes, or combinations of those. The character\n"
<< "\"z\" represents the last page. A number preceded by an \"r\" indicates\n"
<< "to count from the end, so \"r3-r1\" would be the last three pages of the\n"
<< "document. Pages can appear in any order. Ranges can appear with a\n"
<< "high number followed by a low number, which causes the pages to appear in\n"
<< "reverse. Numbers may be repeated. A page range may be appended with :odd\n"
<< "to indicate odd pages in the selected range or :even to indicate even\n"
<< "pages.\n"
<< "\n"
<< "If the page range is omitted, the range of 1-z is assumed. qpdf decides\n"
<< "that the page range is omitted if the range argument is either -- or a\n"
<< "valid file name and not a valid range.\n"
<< "\n"
<< "The usual behavior of --pages is to add all pages from the first file,\n"
<< "then all pages from the second file, and so on. If the --collate option\n"
<< "is specified, then pages are collated instead. In other words, qpdf takes\n"
<< "the first page from the first file, the first page from the second file,\n"
<< "and so on until it runs out of files; then it takes the second page from\n"
<< "each file, etc. When a file runs out of pages, it is skipped until all\n"
<< "specified pages are taken from all files.\n"
<< "\n"
<< "See the manual for examples and a discussion of additional subtleties.\n"
<< "\n"
<< "\n"
<< "Overlay and Underlay Options\n"
<< "----------------------------\n"
<< "\n"
<< "These options allow pages from another file to be overlaid or underlaid\n"
<< "on the primary output. Overlaid pages are drawn on top of the destination\n"
<< "page and may obscure the page. Underlaid pages are drawn below the\n"
<< "destination page.\n"
<< "\n"
<< "{--overlay | --underlay } file\n"
" [ --password=password ]\n"
" [ --to=page-range ]\n"
" [ --from=[page-range] ]\n"
" [ --repeat=page-range ]\n"
" --\n"
<< "\n"
<< "For overlay and underlay, a file and optional password are specified, along\n"
<< "with a series of optional page ranges. The default behavior is that each\n"
<< "page of the overlay or underlay file is imposed on the corresponding page\n"
<< "of the primary output until it runs out of pages, and any extra pages are\n"
<< "ignored. The page range options all take page ranges in the same form as\n"
<< "the --pages option. They have the following meanings:\n"
<< "\n"
<< " --to: the pages in the primary output to which overlay/underlay is\n"
<< " applied\n"
<< " --from: the pages from the overlay/underlay file that are used\n"
<< " --repeat: pages from the overlay/underlay that are repeated after\n"
<< " any \"from\" pages have been exhausted\n"
<< "\n"
<< "\n"
<< "Embedded Files/Attachments Options\n"
<< "----------------------------------\n"
<< "\n"
<< "These options can be used to work with embedded files, also known as\n"
<< "attachments.\n"
<< "\n"
<< "--list-attachments show key and stream number for embedded files;\n"
<< " combine with --verbose for more detailed information\n"
<< "--show-attachment=key write the contents of the specified attachment to\n"
<< " standard output as binary data\n"
<< "--add-attachment file options --\n"
<< " add or replace an attachment\n"
<< "--remove-attachment=key remove the specified attachment; repeatable\n"
<< "--copy-attachments-from file options --\n"
<< " copy attachments from another file\n"
<< "\n"
<< "The \"key\" option is the unique name under which the attachment is registered\n"
<< "within the PDF file. You can get this using the --list-attachments option. This\n"
<< "is usually the same as the filename, but it doesn't have to be.\n"
<< "\n"
<< "Options for adding attachments:\n"
<< "\n"
<< " file path to the file to attach\n"
<< " --key=key the name of this in the embedded files table;\n"
<< " defaults to the last path element of file\n"
<< " --filename=name the file name of the attachment; this is what is\n"
<< " usually displayed to the user; defaults to the\n"
<< " last path element of file\n"
<< " --creationdate=date creation date in PDF format; defaults to the\n"
<< " current time\n"
<< " --moddate=date modification date in PDF format; defaults to the\n"
<< " current time\n"
<< " --mimetype=type/subtype mime type of attachment (e.g. application/pdf)\n"
<< " --description=\"text\" attachment description\n"
<< " --replace replace any existing attachment with the same key\n"
<< "\n"
<< "Options for copying attachments:\n"
<< "\n"
<< " file file whose attachments should be copied\n"
<< " --password=password password to open the other file, if needed\n"
<< " --prefix=prefix a prefix to insert in front of each key;\n"
<< " required if needed to ensure each attachment\n"
<< " has a unique key\n"
<< "\n"
<< "Date format: D:yyyymmddhhmmss<z> where <z> is either Z for UTC or a timezone\n"
<< "offset in the form -hh'mm' or +hh'mm'.\n"
<< "Examples: D:20210207161528-05'00', D:20210207211528Z\n"
<< "\n"
<< "\n"
<< "Advanced Parsing Options\n"
<< "------------------------\n"
<< "\n"
<< "These options control aspects of how qpdf reads PDF files. Mostly these are\n"
<< "of use to people who are working with damaged files. There is little reason\n"
<< "to use these options unless you are trying to solve specific problems.\n"
<< "\n"
<< "--suppress-recovery prevents qpdf from attempting to recover damaged files\n"
<< "--ignore-xref-streams tells qpdf to ignore any cross-reference streams\n"
<< "\n"
<< "\n"
<< "Advanced Transformation Options\n"
<< "-------------------------------\n"
<< "\n"
<< "These transformation options control fine points of how qpdf creates\n"
<< "the output file. Mostly these are of use only to people who are very\n"
<< "familiar with the PDF file format or who are PDF developers.\n"
<< "\n"
<< "--stream-data=option controls transformation of stream data (below)\n"
<< "--compress-streams=[yn] controls whether to compress streams on output\n"
<< "--decode-level=option controls how to filter streams from the input\n"
<< "--recompress-flate recompress streams already compressed with Flate\n"
<< "--compression-level=n set zlib compression level; most effective with\n"
<< " --recompress-flate --object-streams=generate\n"
<< "--normalize-content=[yn] enables or disables normalization of content streams\n"
<< "--object-streams=mode controls handing of object streams\n"
<< "--preserve-unreferenced preserve unreferenced objects\n"
<< "--remove-unreferenced-resources={auto,yes,no}\n"
<< " whether to remove unreferenced page resources\n"
<< "--preserve-unreferenced-resources\n"
<< " synonym for --remove-unreferenced-resources=no\n"
<< "--newline-before-endstream always put a newline before endstream\n"
<< "--coalesce-contents force all pages' content to be a single stream\n"
<< "--flatten-annotations=option\n"
<< " incorporate rendering of annotations into page\n"
<< " contents including those for interactive form\n"
<< " fields; may also want --generate-appearances\n"
<< "--generate-appearances generate appearance streams for form fields\n"
<< "--optimize-images compress images with DCT (JPEG) when advantageous\n"
<< "--oi-min-width=w do not optimize images whose width is below w;\n"
<< " default is 128. Use 0 to mean no minimum\n"
<< "--oi-min-height=h do not optimize images whose height is below h\n"
<< " default is 128. Use 0 to mean no minimum\n"
<< "--oi-min-area=a do not optimize images whose pixel count is below a\n"
<< " default is 16,384. Use 0 to mean no minimum\n"
<< "--externalize-inline-images convert inline images to regular images; by\n"
<< " default, images of at least 1,024 bytes are\n"
<< " externalized\n"
<< "--ii-min-bytes=bytes specify minimum size of inline images to be\n"
<< " converted to regular images\n"
<< "--keep-inline-images exclude inline images from image optimization\n"
<< "--remove-page-labels remove any page labels present in the output file\n"
<< "--qdf turns on \"QDF mode\" (below)\n"
<< "--linearize-pass1=file write intermediate pass of linearized file\n"
<< " for debugging\n"
<< "--min-version=version sets the minimum PDF version of the output file\n"
<< "--force-version=version forces this to be the PDF version of the output file\n"
<< "\n"
<< "Options for --flatten-annotations are all, print, or screen. If the option\n"
<< "is print, only annotations marked as print are included. If the option is\n"
<< "screen, options marked as \"no view\" are excluded. Otherwise, annotations\n"
<< "are flattened regardless of the presence of print or NoView flags. It is\n"
<< "common for PDF files to have a flag set that appearance streams need to be\n"
<< "regenerated. This happens when someone changes a form value with software\n"
<< "that does not know how to render the new value. qpdf will not flatten form\n"
<< "fields in files like this. If you get this warning, you have two choices:\n"
<< "either use qpdf's --generate-appearances flag to tell qpdf to go ahead and\n"
<< "regenerate appearances, or use some other tool to generate the appearances.\n"
<< "qpdf does a pretty good job with most forms when only ASCII and \"Windows\n"
<< "ANSI\" characters are used in form field values, but if your form fields\n"
<< "contain other characters, rich text, or are other than left justified, you\n"
<< "will get better results first saving with other software.\n"
<< "\n"
<< "Version numbers may be expressed as major.minor.extension-level, so 1.7.3\n"
<< "means PDF version 1.7 at extension level 3.\n"
<< "\n"
<< "Values for stream data options:\n"
<< "\n"
<< " compress recompress stream data when possible (default)\n"
<< " preserve leave all stream data as is\n"
<< " uncompress uncompress stream data when possible\n"
<< "\n"
<< "Values for object stream mode:\n"
<< "\n"
<< " preserve preserve original object streams (default)\n"
<< " disable don't write any object streams\n"
<< " generate use object streams wherever possible\n"
<< "\n"
<< "When --compress-streams=n is specified, this overrides the default behavior\n"
<< "of qpdf, which is to attempt compress uncompressed streams. Setting\n"
<< "stream data mode to uncompress or preserve has the same effect.\n"
<< "\n"
<< "The --decode-level parameter may be set to one of the following values:\n"
<< " none do not decode streams\n"
<< " generalized decode streams compressed with generalized filters\n"
<< " including LZW, Flate, and the ASCII encoding filters.\n"
<< " specialized additionally decode streams with non-lossy specialized\n"
<< " filters including RunLength\n"
<< " all additionally decode streams with lossy filters\n"
<< " including DCT (JPEG)\n"
<< "\n"
<< "In qdf mode, by default, content normalization is turned on, and the\n"
<< "stream data mode is set to uncompress. QDF mode does not support\n"
<< "linearized files. The --linearize flag disables qdf mode.\n"
<< "\n"
<< "Setting the minimum PDF version of the output file may raise the version\n"
<< "but will never lower it. Forcing the PDF version of the output file may\n"
<< "set the PDF version to a lower value than actually allowed by the file's\n"
<< "contents. You should only do this if you have no other possible way to\n"
<< "open the file or if you know that the file definitely doesn't include\n"
<< "features not supported later versions.\n"
<< "\n"
<< "Testing, Inspection, and Debugging Options\n"
<< "------------------------------------------\n"
<< "\n"
<< "These options can be useful for digging into PDF files or for use in\n"
<< "automated test suites for software that uses the qpdf library.\n"
<< "\n"
<< "--deterministic-id generate deterministic /ID\n"
<< "--static-id generate static /ID: FOR TESTING ONLY!\n"
<< "--static-aes-iv use a static initialization vector for AES-CBC\n"
<< " This is option is not secure! FOR TESTING ONLY!\n"
<< "--no-original-object-ids suppress original object ID comments in qdf mode\n"
<< "--show-encryption quickly show encryption parameters\n"
<< "--show-encryption-key when showing encryption, reveal the actual key\n"
<< "--check-linearization check file integrity and linearization status\n"
<< "--show-linearization check and show all linearization data\n"
<< "--show-xref show the contents of the cross-reference table\n"
<< "--show-object=trailer|obj[,gen]\n"
<< " show the contents of the given object\n"
<< " --raw-stream-data show raw stream data instead of object contents\n"
<< " --filtered-stream-data show filtered stream data instead of object contents\n"
<< "--show-npages print the number of pages in the file\n"
<< "--show-pages shows the object/generation number for each page\n"
<< " --with-images also shows the object IDs for images on each page\n"
<< "--check check file structure + encryption, linearization\n"
<< "--json generate a json representation of the file\n"
<< "--json-help describe the format of the json representation\n"
<< "--json-key=key repeatable; prune json structure to include only\n"
<< " specified keys. If absent, all keys are shown\n"
<< "--json-object=trailer|[obj,gen]\n"
<< " repeatable; include only specified objects in the\n"
<< " \"objects\" section of the json. If absent, all\n"
<< " objects are shown\n"
<< "\n"
<< "The json representation generated by qpdf is designed to facilitate\n"
<< "processing of qpdf from other programming languages that have a hard\n"
<< "time calling C++ APIs. Run qpdf --json-help for details on the format.\n"
<< "The manual has more in-depth information about the json representation\n"
<< "and certain compatibility guarantees that qpdf provides.\n"
<< "\n"
<< "The --raw-stream-data and --filtered-stream-data options are ignored\n"
<< "unless --show-object is given. Either of these options will cause the\n"
<< "stream data to be written to standard output.\n"
<< "\n"
<< "If --filtered-stream-data is given and --normalize-content=y is also\n"
<< "given, qpdf will attempt to normalize the stream data as if it is a\n"
<< "page content stream. This attempt will be made even if it is not a\n"
<< "page content stream, in which case it will produce unusable results.\n"
<< "\n"
<< "Ordinarily, qpdf exits with a status of 0 on success or a status of 2\n"
<< "if any errors occurred. If there were warnings but not errors, qpdf\n"
<< "exits with a status of 3. If warnings would have been issued but --no-warn\n"
<< "was given, an exit status of 3 is still used. If you want qpdf to exit\n"
<< "with status 0 when there are warnings, use the --warning-exit-0 flag.\n"
<< "When --no-warn and --warning-exit-0 are used together, the effect is for\n"
<< "qpdf to completely ignore warnings. qpdf does not use exit status 1,\n"
<< "since that is used by the shell if it can't execute qpdf.\n";
}
#endif
void void
ArgParser::argJsonHelp() ArgParser::argJsonHelp()
{ {

View File

@ -0,0 +1,822 @@
//
// This file is automatically generated by generate_auto_job.
// Edits will be automatically overwritten if the build is
// run in maintainer mode.
//
static void add_help_1(QPDFArgParser& ap)
{
ap.addHelpTopic("usage", "basic invocation", R"(Read a PDF file, apply transformations or modifications, and write
a new PDF file.
Usage: qpdf infile [options] [outfile]
OR qpdf help-option
- infile, options, and outfile may be in any order as long as infile
precedes outfile.
- Use --empty in place of an input file for a zero-page, empty input
- Use --replace-input in place of an output file to overwrite the
input file with the output
- outfile may be - to write to stdout; reading from stdin is not supported
- @filename is an argument file; each line is treated as a separate
command-line argument
- @- may be used to read arguments from stdin
- Later options may override earlier options if contradictory
)");
ap.addOptionHelp("--empty", "usage", "empty input file", R"(Use in place of infile for an empty input. Especially useful
with --pages.
)");
ap.addOptionHelp("--replace-input", "usage", "replace input with output", R"(Use in place of outfile to overwrite the input file with the output.
)");
ap.addHelpTopic("exit-status", "meanings of qpdf's exit codes", R"(Meaning of exit codes:
0: no errors or warnings
1: not used by qpdf but may be used by the shell if unable to invoke qpdf
2: errors detected
3: warnings detected, unless --warning-exit-0 is given
)");
ap.addOptionHelp("--warning-exit-0", "exit-status", "exit 0 even with warnings", R"(Use exit status 0 instead of 3 when warnings are present. When
combined with --no-warn, warnings are completely ignored.
)");
ap.addHelpTopic("completion", "shell completion", R"(Shell completion is supported with bash and zsh. Use
eval $(qpdf --completion-bash) or eval $(qpdf --completion-zsh)
to enable. The QPDF_EXECUTABLE environment variable overrides the
path to qpdf that these commands output.
)");
ap.addOptionHelp("--completion-bash", "completion", "enable bash completion", R"(Output a command that enables bash completion
)");
ap.addOptionHelp("--completion-zsh", "completion", "enable zsh completion", R"(Output a command that enables zsh completion
)");
ap.addHelpTopic("help", "information about qpdf", R"(Help options provide some information about qpdf itself. Help
options are only valid as the first and only command-line argument.
)");
ap.addOptionHelp("--help", "help", "provide help", R"(Display help information. Run qpdf --help for information about
how to get help on various topics.
)");
ap.addOptionHelp("--version", "help", "show qpdf version", R"(Display the version of qpdf.
)");
ap.addOptionHelp("--copyright", "help", "show copyright information", R"(Display copyright and license information.
)");
ap.addOptionHelp("--show-crypto", "help", "show available crypto providers", R"(Show a list of available crypto providers, one per line. The
default provider is shown first.
)");
ap.addHelpTopic("general", "general options", R"(General options control qpdf's behavior in ways that are not
directly related to the operation it is performing.
)");
ap.addOptionHelp("--password", "general", "specify password", R"(--password=password
Specify a password for an encrypted, password-protected file.
Not needed for encrypted files with no password.
)");
ap.addOptionHelp("--password-file", "general", "read password from a file", R"(--password-file=filename
The first line of the specified file is used as the password.
This is used in place of the --password option.
)");
ap.addOptionHelp("--verbose", "general", "print additional information", R"(Output additional information about various things qpdf is
doing, including information about files created and operations
performed.
)");
ap.addOptionHelp("--progress", "general", "show progress when writing", R"(Indicate progress when writing files.
)");
ap.addOptionHelp("--no-warn", "general", "suppress printing warning messages", R"(Suppress printing warning messages. If warnings were
encountered, qpdf still exits with exit status 3.
Use --warning-exit-0 with --no-warn to completely ignore
warnings.
)");
}
static void add_help_2(QPDFArgParser& ap)
{
ap.addOptionHelp("--deterministic-id", "general", "generate ID deterministically", R"(Generate a secure, random document ID only using static
information, such as the page contents. Does not use the file's
name or attributes or the current time.
)");
ap.addOptionHelp("--allow-weak-crypto", "general", "allow insecure cryptographic algorithms", R"(All creation of files with weak cryptographic algorithms. This
option is necessary to create 40-bit files or 128-bit files that
use RC4 encryption.
)");
ap.addOptionHelp("--keep-files-open", "general", "manage keeping multiple files open", R"(--keep-files-open=[yn]
When qpdf needs to work with many files, as when merging large
numbers of files, explicitly indicate whether files should be
kept open. The default behavior is to determine this based on
the number of files.
)");
ap.addOptionHelp("--keep-files-open-threshold", "general", "set threshold for --keep-files-open", R"(--keep-files-open-threshold=count
Set the threshold used by --keep-files-open, overriding the
default value of 200.
)");
ap.addHelpTopic("advanced-control", "tweak qpdf's behavior", R"(Advanced control options control qpdf's behavior in ways that would
normally never be needed by a user but that may be useful to
developers or people investigating problems with specific files.
)");
ap.addOptionHelp("--password-is-hex-key", "advanced-control", "provide hex-encoded encryption key", R"(Provide the underlying file encryption key has a hex-encoded
string rather than supplying a password. This is an expert
option.
)");
ap.addOptionHelp("--suppress-password-recovery", "advanced-control", "don't try different password encodings", R"(Suppress qpdf's behavior of attempting different encodings of a
password that contains non-ASCII Unicode characters if the first
attempt doesn't succeed.
)");
ap.addOptionHelp("--password-mode", "advanced-control", "tweak how qpdf encodes passwords", R"(--password-mode={mode}
Fine-tune how qpdf controls encoding of Unicode passwords. Valid
options are auto, bytes, hex-bytes, and unicode.
)");
ap.addOptionHelp("--suppress-recovery", "advanced-control", "suppress error recovery", R"(Avoid attempting to recover when errors are found in a file's
cross reference table or stream lengths.
)");
ap.addOptionHelp("--ignore-xref-streams", "advanced-control", "use xref tables rather than streams", R"(Ignore any cross-reference streams in the file, falling back to
cross-reference tables or triggering document recovery.
)");
ap.addHelpTopic("transformation", "make structural PDF changes", R"(The options below tell qpdf to apply transformations that change
the structure without changing the content.
)");
ap.addOptionHelp("--linearize", "transformation", "linearize (web-optimize) output", R"(Create linearized (web-optimized) output files.
)");
ap.addOptionHelp("--encrypt", "transformation", "start encryption options", R"(--encrypt user owner key-length [ options ] --
Run qpdf --help=encryption for details.
)");
ap.addOptionHelp("--decrypt", "transformation", "remove encryption from input file", R"(Create an unencrypted output file even if the input file was
encrypted. Normally qpdf preserves whatever encryption was
present on the input file. This option overrides that behavior.
)");
ap.addOptionHelp("--copy-encryption", "transformation", "copy another file's encryption details", R"(--copy-encryption=file
Copy encryption details from the specified file instead of
preserving the input file's encryption. Use --encryption-file-password
to specify the encryption file's password.
)");
ap.addOptionHelp("--encryption-file-password", "transformation", "supply password for --copy-encryption", R"(--encryption-file-password=password
If the file named in --copy-encryption requires a password, use
this option to specify the password.
)");
ap.addOptionHelp("--qdf", "transformation", "enable viewing PDF code in a text editor", R"(Create a PDF file suitable for viewing in a text editor and even
editing. This is to edit the PDF code, not the page contents.
All streams that can be uncompressed are uncompressed, and
content streams are normalized, among other changes. The
companion tool "fix-qdf" can be used to repair hand-edited QDF
files. QDF is a feature specific to the qpdf tool. There is a
chapter about it in the manual.
)");
ap.addOptionHelp("--no-original-object-ids", "transformation", "omit original object ID in qdf", R"(Omit comments in a QDF file indicating the object ID an object
had in the original file.
)");
ap.addOptionHelp("--compress-streams", "transformation", "compress uncompressed streams", R"(--compress-streams=[yn]
Setting --compress-streams=n prevents qpdf from compressing
uncompressed streams. This can be useful if you are leaving some
streams uncompressed intentionally.
)");
}
static void add_help_3(QPDFArgParser& ap)
{
ap.addOptionHelp("--decode-level", "transformation", "control which streams to uncompress", R"(--decode-level=option
When uncompressing streams, control which types of compression
schemes should be uncompressed:
- none: don't uncompress anything
- generalized: uncompress streams compressed with a
general-purpose compression algorithm. This is the default.
- specialized: in addition to generalized, also uncompress
streams compressed with a special-purpose but non-lossy
compression scheme
- all: in addition to specialized, uncompress streams compressed
with lossy compression schemes like JPEG (DCT)
qpdf does not know how to uncompress all compression schemes.
)");
ap.addOptionHelp("--stream-data", "transformation", "control stream compression", R"(--stream-data=option
This option controls how streams are compressed in the output.
It is less granular than the newer options, --compress-streams
and --decode-level.
Options:
- compress: same as --compress-streams=y --decode-level=generalized
- preserve: same as --compress-streams=n --decode-level=none
- uncompress: same as --compress-streams=n --decode-level=generalized
)");
ap.addOptionHelp("--recompress-flate", "transformation", "uncompress and recompress flate", R"(The default generalized compression scheme used by PDF is flate,
which is the same as used by zip and gzip. Usually qpdf just
leaves these alone. This option tells qpdf to uncompress and
recompress streams compressed with flate. This can be useful
when combined with --compression-level.
)");
ap.addOptionHelp("--compression-level", "transformation", "set compression level for flate", R"(--compression-level=level
Set a compression level from 1 (least, fastest) to 9 (most,
slowest) when compressing files with flate (used in zip and
gzip), which is the default compression for most PDF files.
You need --recompress-flate with this option if you want to
change already compressed streams.
)");
ap.addOptionHelp("--normalize-content", "transformation", "fix newlines in content streams", R"(--normalize-content=[yn]
Normalize newlines to UNIX-style newlines in PDF content
streams, which is useful for viewing them in a programmer's text
editor across multiple platforms. This is also turned on by
--qdf.
)");
ap.addOptionHelp("--object-streams", "transformation", "control use of object streams", R"(--object-streams=mode
Control what qpdf does regarding object streams. Options:
- preserve: preserve original object streams, if any (the default)
- disable: create output files with no object streams
- generate: create object streams, and compress objects when possible
)");
ap.addOptionHelp("--preserve-unreferenced", "transformation", "preserve unreferenced objects", R"(Preserve all objects from the input even if not referenced.
)");
ap.addOptionHelp("--remove-unreferenced-resources", "transformation", "remove unreferenced page resources", R"(--remove-unreferenced-resources=option
Remove from a page's resource dictionary any resources that are
not referenced in the page's contents. Options: "auto"
(default), "yes", "no".
)");
ap.addOptionHelp("--preserve-unreferenced-resources", "transformation", "use --remove-unreferenced-resources=no", R"(Synonym for --remove-unreferenced-resources=no. Use that instead.
)");
ap.addOptionHelp("--newline-before-endstream", "transformation", "force a newline before endstream", R"(For an extra newline before endstream. Using this option enables
qpdf to preserve PDF/A when rewriting such files.
)");
ap.addOptionHelp("--coalesce-contents", "transformation", "combine content streams", R"(If a page has an array of content streams, concatenate them into
a single content stream.
)");
ap.addOptionHelp("--externalize-inline-images", "transformation", "convert inline to regular images", R"(Convert inline images to regular images.
)");
ap.addOptionHelp("--ii-min-bytes", "transformation", "set minimum size for --externalize-inline-images", R"(--ii-min-bytes=size-in-bytes
Don't externalize inline images smaller than this size. The
default is 1,024. Use 0 for no minimum.
)");
ap.addOptionHelp("--min-version", "transformation", "set minimum PDF version", R"(--min-version=version
Force the PDF version of the output to be at least the
specified version.
)");
ap.addOptionHelp("--force-version", "transformation", "set output PDF version", R"(--force-version=version
Force the output PDF file's PDF version header to be the specified
value, even if the file uses features that may not be available
in that version.
)");
ap.addHelpTopic("page-ranges", "page range syntax", R"(A full description of the page range syntax, with examples, can be
found in the manual. Summary:
- a,b,c pages a, b, and c
- a-b pages a through b inclusive; if a > b, this counts down
- r<n> where <n> represents a number is the <n>th page from the end
- z the last page, same as r1
You can append :even or :odd to select every other page from the
resulting set of pages, where :odd starts with the first page and
:even starts with the second page. These are odd and even pages
from the resulting set, not based on the original page numbers.
)");
ap.addHelpTopic("modification", "change parts of the PDF", R"(Modification options make systematic changes to certain parts of
the PDF, causing the PDF to render differently from the original.
)");
ap.addOptionHelp("--pages", "modification", "begin page selection", R"(--pages file [ --password=password ] [ page-range ] [ ... ] --
Run qpdf --help=page-selection for details.
)");
ap.addOptionHelp("--collate", "modification", "collate with --pages", R"(--collate=n
Collate rather than concatenate pages specified with --pages.
With a numeric argument, collate in groups of n. The default
is 1. Run qpdf --help=page-selection for additional details.
)");
}
static void add_help_4(QPDFArgParser& ap)
{
ap.addOptionHelp("--split-pages", "modification", "write pages to separate files", R"(--split-pages=[n]
This option causes qpdf to create separate output files for each
page or group of pages rather than a single output file.
File names are generated from the specified output file as follows:
- If the string %d appears in the output file name, it is replaced with a
zero-padded page range starting from 1
- Otherwise, if the output file name ends in .pdf (case insensitive), a
zero-padded page range, preceded by a dash, is inserted before the file
extension
- Otherwise, the file name is appended with a zero-padded page range
preceded by a dash.
Page ranges are single page numbers for single-page groups or first-last
for multi-page groups.
)");
ap.addOptionHelp("--overlay", "modification", "begin overlay options", R"(--overlay file [ options ] --
Overlay pages from another file on the output.
Run qpdf --help=overlay-underlay for details.
)");
ap.addOptionHelp("--underlay", "modification", "begin underlay options", R"(--underlay file [ options ] --
Underlay pages from another file on the output.
Run qpdf --help=overlay-underlay for details.
)");
ap.addOptionHelp("--flatten-rotation", "modification", "remove rotation from page dictionary", R"(Rotate a page using content commands instead of page-level
metadata. This can be useful if a broken PDF viewer fails to
properly consider page rotation metadata.
)");
ap.addOptionHelp("--flatten-annotations", "modification", "push annotations into content", R"(--flatten-annotations=option
Push page annotations into the content streams. This may be
necessary in some case when printing or splitting files.
Options: "all", "print", "screen".
)");
ap.addOptionHelp("--rotate", "modification", "rotate pages", R"(--rotate=[+|-]angle[:page-range]
Rotate specified pages by multiples of 90 degrees specifying
either absolute or relative angles. "angle" may be 0, 90, 180,
or 270. You almost always want to use +angle or -angle rather
than just angle, as discussed in the manual. Run
qpdf --help=page-ranges for help with page ranges.
)");
ap.addOptionHelp("--generate-appearances", "modification", "generate appearances for form fields", R"(PDF form fields consist of values and appearances, which may be
inconsistent with each other if a form field value has been
modified without updating its appearance. This option tells qpdf
to generate new appearance streams. There are some limitations,
which are discussed in the manual.
)");
ap.addOptionHelp("--optimize-images", "modification", "use efficient compression for images", R"(Attempt to use DCT (JPEG) compression for images that fall
within certain constraints as long as doing so decreases the
size in bytes of the image. See also help for the following
options:
--oi-min-width
--oi-min-height
--oi-min-area
--keep-inline-images
The --verbose flag is useful with this option.
)");
ap.addOptionHelp("--oi-min-width", "modification", "minimum width for --optimize-images", R"(--oi-min-width=width
Don't optimize images whose width is below the specified value.
)");
ap.addOptionHelp("--oi-min-height", "modification", "minimum height for --optimize-images", R"(--oi-min-height=height
Don't optimize images whose height is below the specified value.
)");
ap.addOptionHelp("--oi-min-area", "modification", "minimum area for --optimize-images", R"(--oi-min-area=area-in-pixels
Don't optimize images whose area in pixels is below the specified value.
)");
ap.addOptionHelp("--keep-inline-images", "modification", "exclude inline images from optimization", R"(Prevent inline images from being considered by --optimize-images.
)");
ap.addOptionHelp("--remove-page-labels", "modification", "remove page labels (numbers)", R"(Exclude page labels (explicit page numbers) from the output file.
)");
ap.addHelpTopic("encryption", "create encrypted files", R"(Create encrypted files. Usage:
--encrypt user-password owner-password key-length [ options ] --
Either or both of user-password and owner-password may be empty
strings. key-length may be 40, 128, or 256. Encryption options are
terminated by "--" by itself.
40-bit encryption is insecure, as is 128-bit encryption without
AES. Use 256-bit encryption unless you have a specific reason to
use an insecure format, such as testing or compatibility with very
old viewers. You must use the --allow-weak-crypto to create
encrypted files that use insecure cryptographic algorithms. The
--allow-weak-crypto flag appears outside of --encrypt ... --
(before --encrypt or after --).
Available options vary by key length. Not all readers respect all
restrictions. Different PDF readers respond differently to various
combinations of options. Sometimes a PDF viewer may show you
restrictions that differ from what you selected. This is probably
not a bug in qpdf.
Options for 40-bit only:
--annotate=[yn] restrict comments, filling forms, and signing
--extract=[yn] restrict text/graphic extraction
--modify=[yn] restrict document modification
--print=[yn] restrict printing
Options for 128-bit or 256-bit:
--accessibility=[yn] restrict accessibility (usually ignored)
--annotate=[yn] restrict commenting/filling form fields
--assemble=[yn] restrict document assembly
--extract=[yn] restrict text/graphic extraction
--form=[yn] restrict filling form fields
--modify-other=[yn] restrict other modifications
--modify=modify-opt control modify access by level
--print=print-opt control printing access
--cleartext-metadata prevent encryption of metadata
For 128-bit only:
--use-aes=[yn] indicates whether to use AES encryption
--force-V4 forces use of V=4 encryption handler
For 256-bit only:
--force-R5 forces use of deprecated R=5 encryption
--allow-insecure allow user password with empty owner password
Values for print-opt:
none disallow printing
low allow only low-resolution printing
full allow full printing
Values for modify-opt:
none allow no modifications
assembly allow document assembly only
form assembly + filling in form fields and signing
annotate form + commenting and modifying forms
all allow full document modification
)");
ap.addOptionHelp("--accessibility", "encryption", "restrict document accessibility", R"(--accessibility=[yn]
This option is ignored except with very old encryption formats.
The current PDF specification does not allow restriction of
document accessibility. This option is not available with 40-bit
encryption.
)");
ap.addOptionHelp("--annotate", "encryption", "restrict document annotation", R"(--annotate=[yn]
Enable/disable modifying annotations including making comments
and filling in form fields. For 128-bit and 256-bit encryption,
this also enables editing, creating, and deleting form fields
unless --modify-other=n or --modify=none is also specified.
)");
ap.addOptionHelp("--assemble", "encryption", "restrict document assembly", R"(--assemble=[yn]
Enable/disable document assembly (rotation and reordering of
pages). This option is not available with 40-bit encryption.
)");
ap.addOptionHelp("--extract", "encryption", "restrict text/graphic extraction", R"(--extract=[yn]
Enable/disable text/graphic extraction for purposes other than
accessibility.
)");
ap.addOptionHelp("--form", "encryption", "restrict form filling", R"(--form=[yn]
Enable/disable whether filling form fields is allowed even if
modification of annotations is disabled. This option is not
available with 40-bit encryption.
)");
}
static void add_help_5(QPDFArgParser& ap)
{
ap.addOptionHelp("--modify-other", "encryption", "restrict other modifications", R"(--modify-other=[yn]
Enable/disable modifications not controlled by --assemble,
--annotate, or --form. --modify-other=n is implied by any of the
other --modify options. This option is not available with 40-bit
encryption.
)");
ap.addOptionHelp("--modify", "encryption", "restrict document modification", R"(--modify=modify-opt
For 40-bit files, modify-opt may only be y or n and controls all
aspects of document modification.
For 128-bit and 256-bit encryption, modify-opt values allow
enabling and disabling levels of restriction in a manner similar
to how some PDF creation tools do it. modify-opt values map to
other combinations of options as follows:
all: allow full modification (the default)
annotate: --modify-other=n
form: --modify-other=n --annotate=n
assembly: --modify-other=n --annotate=n --form=n
none: --modify-other=n --annotate=n --form=n --assemble=n
)");
ap.addOptionHelp("--print", "encryption", "restrict printing", R"(--print=print-opt
Control what kind of printing is allowed. For 40-bit encryption,
print-opt may only be y or n and enables or disables all
printing. For 128-bit and 256-bit encryption, print-opt may have
the following values:
none: disallow printing
low: allow low-resolution printing only
full: allow full printing (the default)
)");
ap.addOptionHelp("--cleartext-metadata", "encryption", "don't encrypt metadata", R"(If specified, don't encrypt document metadata even when
encrypting the rest of the document. This option is not
available with 40-bit encryption.
)");
ap.addOptionHelp("--use-aes", "encryption", "use AES with 128-bit encryption", R"(--use-aes=[yn]
Enables/disables use of the more secure AES encryption with
128-bit encryption. Specifying --use-aes=y forces the PDF
version to be at least 1.6. This option is only available with
128-bit encryption. The default is "n" for compatibility
reasons. Use 256-bit encryption instead.
)");
ap.addOptionHelp("--allow-insecure", "encryption", "allow empty owner passwords", R"(Allow creation of PDF files with empty owner passwords and
non-empty user passwords when using 256-bit encryption.
)");
ap.addOptionHelp("--force-V4", "encryption", "force V=4 in encryption dictionary", R"(This option is for testing and is never needed in practice since
qpdf does this automatically when needed.
)");
ap.addOptionHelp("--force-R5", "encryption", "use unsupported R=5 encryption", R"(Use an undocumented, unsupported, deprecated encryption
algorithm that existed only in Acrobat version IX. This option
should not be used except for compatibility testing.
)");
ap.addHelpTopic("page-selection", "select pages from one or more files", R"(Use the --pages option to select pages from multiple files. Usage:
qpdf in.pdf --pages input-file [ --password=password ] [ page-range ] \
[ ... ] -- out.pdf
Between --pages and the -- that terminates pages option, repeat
the following:
filename [ --password=password ] [ page-range ]
Document-level information, such as outlines, tags, etc., is taken
from in.pdf is preserved in out.pdf. You can use --empty in place
of an input file to start from an empty file and just copy pages
equally from all files. You can use "." as a shorthand for the
primary input file (if not --empty). In the above example, "."
would refer to in.pdf.
Use --password=password to specify the password for a
password-protected input file. If the same input file is used more
than once, you only need to supply the password the first time. If
the page range is omitted, all pages are selected.
Run qpdf --help=page-ranges for help with page ranges.
Use --collate=n to cause pages to be collated in groups of n pages
(default 1) instead of concatenating the input.
Examples:
- Start with in.pdf and append all pages from a.pdf and the even
pages from b.pdf, and write the output to out.pdf. Document-level
information from in.pdf is retained. Note the use of "." to refer
to in.pdf.
qpdf in.pdf --pages . a.pdf b.pdf:even -- out.pdf
- Take all the pages from a.pdf, all the pages from b.pdf in
reverse, and only pages 3 and 6 from c.pdf and write the result
to out.pdf. Use password "x" to open b.pdf:
qpdf --empty --pages a.pdf b.pdf --password=x z-1 c.pdf 3,6
More examples are in the manual.
)");
ap.addHelpTopic("overlay-underlay", "overlay/underlay pages from other files", R"(These options allow pages from another file to be overlaid or
underlaid on the primary output. Overlaid pages are drawn on top of
the destination page and may obscure the page. Underlaid pages are
drawn below the destination page. Usage:
{--overlay | --underlay } file
[ --password=password ]
[ --to=page-range ]
[ --from=[page-range] ]
[ --repeat=page-range ]
--
Note the use of "--" by itself to terminate overlay/underlay options.
For overlay and underlay, a file and optional password are specified, along
with a series of optional page ranges. The default behavior is that each
page of the overlay or underlay file is imposed on the corresponding page
of the primary output until it runs out of pages, and any extra pages are
ignored. You can also give a page range with --repeat to cause
those pages to be repeated after the original pages are exhausted.
Run qpdf --help=page-ranges for help with page ranges.
)");
ap.addOptionHelp("--to", "overlay-underlay", "destination pages for underlay/overlay", R"(--to=page-range
Specify the range of pages in the primary output to apply
overlay/underlay to. See qpdf --help=page-ranges for help with
the page range syntax.
)");
ap.addOptionHelp("--from", "overlay-underlay", "source pages for underlay/overlay", R"(--from=[page-range]
Specify pages from the overlay/underlay file that are applied to
the destination pages. See qpdf --help=page-ranges for help
with the page range syntax. The page range may be omitted
if --repeat is used.
)");
ap.addOptionHelp("--repeat", "overlay-underlay", "overlay/underlay pages to repeat", R"(--repeat=page-range
Specify pages from the overlay/underlay that are repeated after
"from" pages have been exhausted. See qpdf --help=page-ranges
for help with the page range syntax.
)");
ap.addHelpTopic("attachments", "work with embedded files", R"(It is possible to list, add, or delete embedded files (also known
as attachments) and to copy attachments from other files. See help
on individual options for details. Run qpdf --help=add-attachment
for additional details about adding attachments.
)");
ap.addOptionHelp("--list-attachments", "attachments", "list embedded files", R"(Show the key and stream number for each embedded file. Combine
with --verbose for more detailed information.
)");
ap.addOptionHelp("--show-attachment", "attachments", "export an embedded file", R"(--show-attachment=key
Write the contents of the specified attachment to standard
output as binary data. Get the key with --list-attachments.
)");
ap.addOptionHelp("--add-attachment", "attachments", "start add attachment options", R"(--add-attachment file options --
The --add-attachment flag and its options may be repeated to add
multiple attachments. Run qpdf --help=add-attachment for details.
)");
ap.addOptionHelp("--remove-attachment", "attachments", "remove an embedded file", R"(--remove-attachment=key
Remove an embedded file using its key. Get the key with
--list-attachments.
)");
ap.addOptionHelp("--copy-attachments-from", "attachments", "start copy attachment options", R"(--copy-attachments-from file options --
The --copy-attachments-from flag and its options may be repeated
to copy attachments from multiple files. Run
qpdf --help=copy-attachments for details.
)");
}
static void add_help_6(QPDFArgParser& ap)
{
ap.addHelpTopic("pdf-dates", "PDF date format", R"(When a date is required, the date should conform to the PDF date
format specification, which is "D:yyyymmddhhmmssz" where "z" is
either literally upper case "Z" for UTC or a timezone offset in
the form "-hh'mm'" or "+hh'mm'". Negative timezone offsets indicate
time before UTC. Positive offsets indicate how far after. For
example, US Eastern Standard Time (America/New_York) is "-05'00'",
and Indian Standard Time (Asia/Calcutta) is "+05'30'".
Examples:
- D:20210207161528-05'00' February 7, 2021 at 4:15:28 p.m.
- D:20210207211528Z February 7, 2021 at 21:15:28 UTC
)");
ap.addHelpTopic("add-attachment", "attach (embed) files", R"(The options listed below appear between --add-attachment and its
terminating "--".
)");
ap.addOptionHelp("--key", "add-attachment", "specify attachment key", R"(--key=key
Specify the key to use for the attachment in the embedded files
table. It defaults to the last element of the attached file's
filename.
)");
ap.addOptionHelp("--filename", "add-attachment", "set attachment's displayed filename", R"(--filename=name
Specify the filename to be used for the attachment. This is what
is usually displayed to the user and is the name most graphical
PDF viewers will use when saving a file. It defaults to the last
element of the attached file's filename.
)");
ap.addOptionHelp("--creationdate", "add-attachment", "set attachment's creation date", R"(--creationdate=date
Specify the attachment's creation date in PDF format; defaults
to the current time. Run qpdf --help=pdf-dates for information
about the date format.
)");
ap.addOptionHelp("--moddate", "add-attachment", "set attachment's modification date", R"(--moddate=date
Specify the attachment's modification date in PDF format;
defaults to the current time. Run qpdf --help=pdf-dates for
information about the date format.
)");
ap.addOptionHelp("--mimetype", "add-attachment", "attachment mime type (e.g. application/pdf)", R"(--mimetype=type/subtype
Specify the mime type for the attachment, such as text/plain,
application/pdf, image/png, etc.
)");
ap.addOptionHelp("--description", "add-attachment", "set attachment's description", R"(--description="text"
Supply descriptive text for the attachment, displayed by some
PDF viewers.
)");
ap.addOptionHelp("--replace", "add-attachment", "replace attachment with same key", R"(Indicate that any existing attachment with the same key should
be replaced by the new attachment. Otherwise, qpdf gives an
error if an attachment with that key is already present.
)");
ap.addHelpTopic("copy-attachments", "copy attachments from another file", R"(The options listed below appear between --copy-attachments-from and
its terminating "--".
To copy attachments from a password-protected file, use
the --password option after the file name.
)");
ap.addOptionHelp("--prefix", "copy-attachments", "key prefix for copying attachments", R"(--prefix=prefix
Prepend a prefix to each key; may be needed if there are
duplicate attachment keys. This affects the key only, not the
file name.
)");
ap.addOptionHelp("--is-encrypted", "copy-attachments", "silently test whether a file is encrypted", R"(Silently exit with a code indicating the file's encryption status:
0: the file is encrypted
1: not used
2: the file is not encrypted
This can be used with password-protected files even if you don't
know the password.
)");
ap.addOptionHelp("--requires-password", "copy-attachments", "silently test a file's password", R"(Silently exit with a code indicating the file's password status:
0: a password, other than as supplied, is required
1: not used
2: the file is not encrypted
3: the file is encrypted, and correct password (if any) has been supplied
)");
ap.addOptionHelp("--check", "copy-attachments", "partially check whether PDF is valid", R"(Check the structure of the PDF file as well as a number of other
aspects of the file, and write information about the file to
standard output. Note that qpdf does not perform any validation
of the actual PDF page content or semantic correctness of the
PDF file. It merely checks that the PDF file is syntactically
valid.
)");
ap.addOptionHelp("--show-encryption", "copy-attachments", "information about encrypted files", R"(Show document encryption parameters. Also show the document's
user password if the owner password is given and the file was
encrypted using older encryption formats that allow user
password recovery.
)");
ap.addOptionHelp("--show-encryption-key", "copy-attachments", "show key with --show-encryption", R"(When used with --show-encryption, causes the underlying
encryption key to be displayed.
)");
ap.addOptionHelp("--check-linearization", "copy-attachments", "check linearization tables", R"(Check to see whether a file is linearized and, if so, whether
the linearization hint tables are correct.
)");
ap.addOptionHelp("--show-linearization", "copy-attachments", "show linearization hint tables", R"(Check and display all data in the linearization hint tables.
)");
ap.addOptionHelp("--show-xref", "copy-attachments", "show cross reference data", R"(Show the contents of the cross-reference table or stream (object
locations in the file) in a human-readable form. This is
especially useful for files with cross-reference streams, which
are stored in a binary format.
)");
}
static void add_help_7(QPDFArgParser& ap)
{
ap.addOptionHelp("--show-object", "copy-attachments", "show contents of an object", R"(--show-object=trailer|obj[,gen]
Show the contents of the given object. This is especially useful
for inspecting objects that are inside of object streams (also
known as "compressed objects").
)");
ap.addOptionHelp("--raw-stream-data", "copy-attachments", "show raw stream data", R"(When used with --show-object, if the object is a stream, write
the raw (compressed) binary stream data to standard output
instead of the object's contents. See also
--filtered-stream-data.
)");
ap.addOptionHelp("--filtered-stream-data", "copy-attachments", "show filtered stream data", R"(When used with --show-object, if the object is a stream, write
the filtered (uncompressed, potentially binary) stream data to
standard output instead of the object's contents. See also
--raw-stream-data.
)");
ap.addOptionHelp("--show-npages", "copy-attachments", "show number of pages", R"(Print the number of pages in the input file on a line by itself.
Useful for scripts.
)");
ap.addOptionHelp("--show-pages", "copy-attachments", "display page dictionary information", R"(Show the object and generation number for each page dictionary
object and for each content stream associated with the page.
)");
ap.addOptionHelp("--with-images", "copy-attachments", "include image details with --show-pages", R"(When used with --show-pages, also shows the object and
generation numbers for the image objects on each page.
)");
ap.addHelpTopic("json", "JSON output for PDF information", R"(Show information about the PDF file in JSON format. Please see the
JSON chapter in the qpdf manual for details.
)");
ap.addOptionHelp("--json", "json", "show file in json format", R"(Generate a JSON representation of the file. This is described in
depth in the JSON section of the manual.
)");
ap.addOptionHelp("--json-help", "json", "show format of json output", R"(Describe the format of the JSON output.
)");
ap.addOptionHelp("--json-key", "json", "restrict which keys are in json output", R"(--json-key=key
This option is repeatable. If given, only the specified
top-level keys will be included in the JSON output. Otherwise,
all keys will be included.
)");
ap.addOptionHelp("--json-object", "json", "restrict which objects are in JSON", R"(--json-object=trailer|obj[,gen]
This option is repeatable. If given, only specified objects will
be shown in the "objects" key of the JSON output. Otherwise, all
objects will be shown.
)");
ap.addHelpTopic("testing", "options for testing or debugging", R"(The options below are useful when writing automated test code that
includes files created by qpdf or when testing qpdf itself.
)");
ap.addOptionHelp("--static-id", "testing", "use a fixed document ID", R"(Use a fixed value for the document ID. This is intended for
testing only. Never use it for production files. See also
qpdf --help=--deterministic-id.
)");
ap.addOptionHelp("--static-aes-iv", "testing", "use a fixed AES vector", R"(Use a static initialization vector for AES-CBC. This is intended
for testing only so that output files can be reproducible. Never
use it for production files. This option is not secure since it
significantly weakens the encryption.
)");
ap.addOptionHelp("--linearize-pass1", "testing", "save pass 1 of linearization", R"(--linearize-pass1=file
Write the first pass of linearization to the named file. The
resulting file is not a valid PDF file. This option is useful only
for debugging qpdf.
)");
}
static void add_help(QPDFArgParser& ap)
{
add_help_1(ap);
add_help_2(ap);
add_help_3(ap);
add_help_4(ap);
add_help_5(ap);
add_help_6(ap);
add_help_7(ap);
ap.addHelpFooter("For detailed help, visit the qpdf manual: https://qpdf.readthedocs.io\n");
}

View File

@ -162,4 +162,3 @@ this->ap.copyFromOtherTable("annotate", "128-bit encryption");
this->ap.copyFromOtherTable("form", "128-bit encryption"); this->ap.copyFromOtherTable("form", "128-bit encryption");
this->ap.copyFromOtherTable("modify-other", "128-bit encryption"); this->ap.copyFromOtherTable("modify-other", "128-bit encryption");
this->ap.copyFromOtherTable("modify", "128-bit encryption"); this->ap.copyFromOtherTable("modify", "128-bit encryption");
this->ap.addHelpFooter("For detailed help, visit the qpdf manual: https://qpdf.readthedocs.io\n");

View File

@ -6,7 +6,7 @@ Topics:
baaa: Baaa Options baaa: Baaa Options
quack: Quack Options quack: Quack Options
== topic baaa (Baaa Options) == == baaa (Baaa Options) ==
Ewe can do sheepish things. Ewe can do sheepish things.
For example, ewe can add more ram to your computer. For example, ewe can add more ram to your computer.
@ -15,15 +15,15 @@ Related options:
--ewe: just for ewe --ewe: just for ewe
--ram: curly horns --ram: curly horns
== topic quack (Quack Options) == == quack (Quack Options) ==
Just put stuff after quack to get a count at the end. Just put stuff after quack to get a count at the end.
== option --ewe (just for ewe) == == --ewe (just for ewe) ==
You are not a ewe. You are not a ewe.
== option --ram (curly horns) == == --ram (curly horns) ==
curly horns curly horns

1
manual/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__

125
manual/_ext/qpdf.py Normal file
View File

@ -0,0 +1,125 @@
from collections import defaultdict
from operator import itemgetter
import re
from sphinx import addnodes
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, Index
from sphinx.roles import XRefRole
from sphinx.util.nodes import make_refnode
# Reference:
# https://www.sphinx-doc.org/en/master/development/tutorials/todo.html
# https://www.sphinx-doc.org/en/master/development/tutorials/recipe.html
class OptionDirective(ObjectDescription):
has_content = True
def handle_signature(self, sig, signode):
signode += addnodes.desc_name(text=sig)
return sig
def add_target_and_index(self, name_cls, sig, signode):
m = re.match(r'^--([^= ]+)', sig)
if not m:
raise Exception('option must start with --')
option_name = m.group(1)
signode['ids'].append(f'option-{option_name}')
qpdf = self.env.get_domain('qpdf')
qpdf.add_option(sig, option_name)
class OptionIndex(Index):
name = 'options'
localname = 'qpdf Command-line Options'
shortname = 'Options'
def generate(self, docnames=None):
content = defaultdict(list)
options = self.domain.get_objects()
options = sorted(options, key=itemgetter(0))
# name, subtype, docname, anchor, extra, qualifier, description
for name, display_name, typ, docname, anchor, _ in options:
m = re.match(r'^(--([^= ]+))', display_name)
if not m:
raise Exception(
'OptionIndex.generate: display name not as expected')
content[m.group(2)[0].lower()].append(
(m.group(1), 0, docname, anchor, '', '', typ))
content = sorted(content.items())
return content, True
class QpdfDomain(Domain):
name = 'qpdf'
label = 'qpdf documentation domain'
roles = {
'ref': XRefRole()
}
directives = {
'option': OptionDirective,
}
indices = {
OptionIndex,
}
initial_data = {
'options': [], # object list
}
def get_full_qualified_name(self, node):
return '{}.{}'.format('option', node.arguments[0])
def get_objects(self):
for obj in self.data['options']:
yield(obj)
def resolve_xref(self, env, from_doc_name, builder, typ, target, node,
contnode):
match = [(docname, anchor)
for name, sig, typ, docname, anchor, priority
in self.get_objects() if name == f'option.{target[2:]}']
if len(match) > 0:
to_doc_name = match[0][0]
match_target = match[0][1]
return make_refnode(builder, from_doc_name, to_doc_name,
match_target, contnode, match_target)
else:
raise Exception(f'invalid option xref ({target})')
def add_option(self, signature, option_name):
if self.env.docname != 'cli':
raise Exception(
'qpdf:option directives don\'t work outside of cli.rst')
name = f'option.{option_name}'
anchor = f'option-{option_name}'
# name, display_name, type, docname, anchor, priority
self.data['options'].append(
(name, signature, '', self.env.docname, anchor, 0))
def purge_options(self, docname):
self.data['options'] = list([
x for x in self.data['options']
if x[3] != docname
])
def purge_options(app, env, docname):
option = env.get_domain('qpdf')
option.purge_options(docname)
def setup(app):
app.add_domain(QpdfDomain)
app.connect('env-purge-doc', purge_options)
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -17,7 +17,7 @@ ifeq ($(BUILD_PDF),1)
TARGETS_manual += $(PDF_TARGET) TARGETS_manual += $(PDF_TARGET)
endif endif
MANUAL_DEPS = $(wildcard manual/*.rst) manual/conf.py MANUAL_DEPS = $(wildcard manual/*.rst) manual/conf.py manual/_ext/qpdf.py
# Prevent targets that run $(SPHINX) from running in parallel by using # Prevent targets that run $(SPHINX) from running in parallel by using
# order-only dependencies (the dependencies listed after the |) to # order-only dependencies (the dependencies listed after the |) to

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,10 @@
# To see the default sample conf.py, run sphinx-quickstart in an empty # To see the default sample conf.py, run sphinx-quickstart in an empty
# directory. Most of the original comments and options were removed. # directory. Most of the original comments and options were removed.
import sphinx_rtd_theme # noQA F401 import sphinx_rtd_theme # noQA F401
import os
import sys
sys.path.append(os.path.abspath("./_ext"))
project = 'QPDF' project = 'QPDF'
copyright = '2005-2021, Jay Berkenbilt' copyright = '2005-2021, Jay Berkenbilt'
@ -16,6 +20,7 @@ release = '10.5.0'
version = release version = release
extensions = [ extensions = [
'sphinx_rtd_theme', 'sphinx_rtd_theme',
'qpdf',
] ]
html_theme = 'sphinx_rtd_theme' html_theme = 'sphinx_rtd_theme'
html_theme_options = { html_theme_options = {

327
manual/encryption.rst Normal file
View File

@ -0,0 +1,327 @@
.. _pdf-encryption:
PDF Encryption
==============
This chapter discusses PDF encryption in a general way with an angle
toward how it works in :command:`qpdf`. This chapter is not intended
to replace the PDF specification. Please consult the spec for full
details.
PDF Encryption Concepts
-----------------------
Encryption
Encryption is the replacement of *clear text* with encrypted text,
also known as *ciphertext*. The clear text may be retrieved from the
ciphertext if the encryption key is known.
PDF files consist of an object structure. PDF objects may be of a
variety of types including (among others) numbers, boolean values,
names, arrays, dictionaries, strings, and streams. In a PDF file,
only strings and streams are encrypted.
Security Handler
Since the inception of PDF, there have been several modifications to
the way files are encrypted. Encryption is handled by a *security
handler*. The *standard security handler* is password-based. This is
the only security handler implemented by qpdf, and this material is
all focused on the standard security handler. There are various
flags that control the specific details of encryption with the
standard security handler. These are discussed below.
Encryption Key
This refers to the actual key used by the encryption and decryption
algorithms. It is distinct from the password. The main encryption
key is generated at random and stored encrypted in the PDF file. The
passwords used to protect a PDF file, if any, are used to protect
the encryption key. This design makes it possible to use different
passwords (e.g., user and owner passwords) to retrieve the
encryption key or even to change the password on a file without
changing the encryption key. qpdf can expose the encryption key when
run with the :qpdf:ref:`--show-encryption-key` option and can accept
a hex-encoded encryption key in place of a password when run with
the :qpdf:ref:`--password-is-hex-key` option.
Password Protection
Password protection is distinct from encryption. This point is often
misunderstood. A PDF file can be encrypted without being
password-protected. The intent of PDF encryption was that there
would be two passwords: a *user password* and an *owner password*.
Either password can be used to retrieve the encryption key. A
conforming reader is supposed to obey the security restrictions
if the file is opened using the user password but not if the file is
opened with the owner password. :command:`qpdf` makes no distinction
between which password is used to open the file. The distinction
made by conforming readers between the user and owner password is
what makes it common to create encrypted files with no password
protection. This is done by using the empty string as the user
password and some secret string as the owner password. When a user
opens the PDF file, the empty string is used to retrieve the
encryption key, making the file usable, but a conforming reader
restricts certain operations from the user.
What does all this mean? Here are a few things to realize.
- Since the user password and the owner password are both used to
recover the single encryption key, there is *fundamentally no way*
to prevent an application from disregarding the security
restrictions on a file. Any software that can read the encrypted
file at all has the encryption key. Therefore, the security of the
restrictions placed on PDF files is solely enforced by the software.
Any open source PDF reader could be trivially modified to ignore the
security restrictions on a file. The PDF specification is clear
about this point. This means that PDF restrictions on
non-password-protected files only restrict users who don't know how
to circumvent them.
- If a file is password-protected, you have to know at least one of
the user or owner password to retrieve the encryption key. However,
in the case of 40-bit encryption, the actual encryption key is only
5 bytes long and can be easily brute-forced. As such, files
encrypted with 40-bit encryption are not secure regardless of how
strong the password is. With 128-bit encryption, the default
security handler uses RC4 encryption, which is also known be
insecure. As such, the only way to securely encrypt a PDF file using
the standard security handler (as of the last review of this chapter
in 2022) is to use AES encryption. This is the only supported
algorithm with 256-bit encryption, and it can be selected to be used
with 128-bit encryption as well. However there is no reason to use
128-bit encryption with AES. If you are going to use AES, just use
256-bit encryption instead. The security of a 256-bit AES-encrypted
PDF file with a strong password is comparable to using a
general-purpose encryption tool like :command:`gpg` or
:command:`openssl` to encrypt the PDF file with the same password,
but the advantage of using PDF encryption is that no software is
required beyond a regular PDF viewer.
PDF Encryption Details
----------------------
This section describes a few details about PDF encryption. It does not
describe all the details. For that, read the PDF specification. The
details presented here, however, should go a long way toward helping a
casual user/developer understand what's going on with encrypted PDF
files.
Here are more concepts to understand.
Algorithm parameters ``V`` and ``R``
There are two parameters that control the details of encryption
using the standard security handler: ``V`` and ``R``.
``V`` is a code specifying the algorithms that are used for
encrypting the file, handling keys, etc. It may have any of the
following values:
- 1: The original algorithm, which encrypted files using 40-bit keys.
- 2: An extension of the original algorithm allowing longer keys.
Introduced in PDF 1.4.
- 3: An unpublished algorithm that permits file encryption key
lengths ranging from 40 to 128 bits. Introduced in PDF 1.4. qpdf
is believed to be able to read files with ``V`` = 3 but does not
write such files.
- 4: An extension of the algorithm that allows it to be
parameterized by additional rules for handling strings and
streams. Introduced in PDF 1.5.
- 5: An algorithm that allows specification of separate security
handlers for strings and streams as well as embedded files, and
which supports 256-bit keys. Introduced in PDF 1.7 extension level
3 and later extended in extension level 8. This is the encryption
system in the PDF 2.0 specification, ISO-32000.
``R`` is a code specifying the revision of the standard handler. It
is tightly coupled with the value of ``V``. ``R`` may have any of
the following values:
- 2: ``V`` must be 1
- 3: ``V`` must be 2 or 3
- 4: ``V`` must be 4
- 5: ``V`` must be 5; this extension was never fully specified and
existed for a short time in some versions of Acrobat.
:command:`qpdf` is able to read and write this format, but it
should not be used for any purpose other than testing
compatibility with the format.
- 6: ``V`` must be 5. This is the only value that is not deprecated
in the PDF 2.0 specification, ISO-32000.
Encryption Dictionary
Encrypted PDF files have an encryption dictionary. There are several
fields, but these are the important ones for our purposes:
- ``V`` and ``R`` as described above
- ``O``, ``U``, ``OE``, ``UE``: values used by the algorithms that
recover the encryption key from the user and owner password. Which
of these are defined and how they are used vary based on the value
of ``R``.
- ``P``: a bit field that describes which restrictions are in place.
This is discussed below in :ref:`security-restrictions`
Encryption Algorithms
PDF files may be encrypted with the obsolete, insecure RC4 algorithm
or the more secure AES algorithm. See also :ref:`weak-crypto` for a
discussion. 40-bit encryption always uses RC4. 128-bit can use
either RC4 (the default for compatibility reasons) or, starting with
PDF 1.6, AES. 256-bit encryption always uses AES.
.. _security-restrictions:
PDF Security Restrictions
-------------------------
PDF security restrictions are described by a bit field whose value is
stored in the ``P`` field in the encryption dictionary. The value of
``P`` is used by the algorithms to recover the encryption key given
the password, which makes the value of ``P`` tamper-resistent.
``P`` is a 32-bit integer, treated as a signed twos-complement number.
A 1 in any bit position means the permission is granted. The PDF
specification numbers the bits from 1 (least significant bit) to 32
(most significant bit) rather than the more customary 0 to 31. For
consistency with the spec, the remainder of this section uses the
1-based numbering.
Only bits 3, 4, 5, 6, 9, 10, 11, and 12 are used. All other bits are
set to 1. Since bit 32 is always set to 1, the value of ``P`` is
always a negative number. (:command:`qpdf` recognizes a positive
number on behalf of buggy writers that treat ``P`` as unsigned. Such
files have been seen in the wild.)
Here are the meanings of the bit positions. All bits not listed must
have the value 1 except bits 1 and 2, which must have the value 0.
However, the values of bits other than those in the table are ignored,
so having incorrect values probably doesn't break anything in most
cases. A value of 1 indicates that the permission is granted.
- 3: for ``R`` = 2 printing; for ``R`` >= 3, printing at low
resolution
- 4: modifying the document except as controlled by bits 6,
9, and 11
- 5: extracting text and graphics for purposes other than
accessibility to visually impaired users
- 6: add or modify annotations, fill in interactive form fields;
if bit 4 is also set, create or modify interactive form fields
- 9: for ``R`` >= 3, fill in interactive form fields even if bit 6 is
clear
- 10: not used; formerly granted permission to extract material
for accessibility, but the specification now disallows restriction
of accessibility, and conforming readers are to treat this bit as if
it is set regardless of its value
- 11: for ``R`` >= 3, assemble document including inserting, rotating,
or deleting pages or creating document outlines or thumbnail images
- 12: for ``R`` >= 3, allow printing at full resolution
.. _qpdf-P:
How qpdf handles security restrictions
--------------------------------------
The section describes exactly what the qpdf library does with regard
to ``P`` based on the various settings of different security options.
- Start with all bits set except bits 1 and 2, which are cleared
- For ``R`` = 2:
- ``--print=n``: clear bit 3
- ``--modify=n``: clear bit 4
- ``--extract=n``: clear bit 5
- ``--annotate=n``: clear bit 6
- For ``R >= 3``:
- ``--accessibility=n``: for ``R`` = 3, clear bit 10; otherwise,
ignore so bit 10 is always clear if ``R`` >= 4. qpdf allows
creating files with bit 10 clear so that it can be used to create
test files to ensure that a conforming reader ignores the value of
the bit. You should never intentionally clear accessibility.
- ``--extract=n``: clear bit 5
- ``--print=none``: clear bits 3 and 12
- ``--print=low``: clear bit 12
- ``--modify=none``: clear bits 4, 6, 9, and 11
- ``--modify=assembly``: clear bits 4, 6, and 9
- ``--modify=form``: clear bits 4 and 6
- ``--modify=annotate``: clear bit 4
- ``--assemble=n``: clear bit 11
- ``--annotate=n``: clear bit 6
- ``--form=n``: clear bit 9
- ``--modify-other=n``: clear bit 4
Options to :command:`qpdf`, both at the CLI and library level, allow
more granular clearing of permission bits than do most tools,
including Adobe Acrobat. As such, PDF viewers may respond in
surprising ways based on options passed to qpdf. If you observe this,
it is probably not because of a bug in qpdf.
.. _pdf-passwords:
User and Owner Passwords
------------------------
When you use qpdf to show encryption parameters and you open a file
with the owner password, sometimes qpdf reveals the user password, and
sometimes it doesn't. Here's why.
For ``V`` < 5, the user password is actually stored in the PDF file
encrypted with a key that is derived from the owner password, and the
main encryption key is encrypted using a key derived from the user
password. When you open a PDF file, the reader first tries to treat
the given password as the user password, using it to recover the
encryption key. If that works, you're in with restrictions (assuming
the reader chooses to enforce them). If it doesn't work, then the
reader treats the password as the owner password, using it to recover
the user password, and then uses the user password to retrieve the
encryption key. This is why creating a file with the same user
password and owner password with ``V`` < 5 results in a file that some
readers will never allow you to open as the owner. Typically when a
reader encounters a file with ``V`` < 5, it will first attempt to
treat the empty string as a user password. If that works, the file is
encrypted but not password-protected. If it doesn't work, then a
password prompt is given. Creating a file with an empty owner password
is like creating a file with the same owner and user password: there
is no way to open the file as an owner.
For ``V`` >= 5, the main encryption key is independently encrypted
using the user password and the owner password. There is no way to
recover the user password from the owner password. Restrictions are
imposed or not depending on which password was used. In this case, the
password supplied, if any, is tried both as the user password and the
owner password, and whichever works is used. Typically the password is
tried as the owner password first. (This is what the PDF specification
says to do.) As such, specifying a user password and leaving the owner
password blank results in a file that is opened as owner with no
password, effectively rendering the security restrictions useless.
This is why :command:`qpdf` requires you to pass
:qpdf:ref:`--allow-insecure` to create a file with an empty owner
password when 256-bit encryption is in use.

View File

@ -30,5 +30,11 @@ documentation, please visit `https://qpdf.readthedocs.io
design design
linearization linearization
object-streams object-streams
encryption
release-notes release-notes
acknowledgement acknowledgement
Indices
=======
* :ref:`qpdf-options`

View File

@ -208,7 +208,7 @@ files you need to build.
Runtime Crypto Provider Selection Runtime Crypto Provider Selection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can use the :samp:`--show-crypto` option to You can use the :qpdf:ref:`--show-crypto` option to
:command:`qpdf` to get a list of available crypto :command:`qpdf` to get a list of available crypto
providers. The default provider is always listed first, and the rest are providers. The default provider is always listed first, and the rest are
listed in lexical order. Each crypto provider is listed on a line by listed in lexical order. Each crypto provider is listed on a line by

View File

@ -51,7 +51,7 @@ Compatibility
Documentation Documentation
The :command:`qpdf` command can be invoked with the The :command:`qpdf` command can be invoked with the
:samp:`--json-help` option. This will output a JSON :qpdf:ref:`--json-help` option. This will output a JSON
structure that has the same structure as the JSON output that qpdf structure that has the same structure as the JSON output that qpdf
generates, except that each field in the help output is a description generates, except that each field in the help output is a description
of the corresponding field in the JSON output. The specific of the corresponding field in the JSON output. The specific
@ -134,7 +134,7 @@ There are a few limitations to be aware of with the JSON structure:
encoding. In other words, it's best if you don't try to use the JSON encoding. In other words, it's best if you don't try to use the JSON
format to extract binary strings from the PDF file, but if you really format to extract binary strings from the PDF file, but if you really
had to, it could be done. Note that qpdf's had to, it could be done. Note that qpdf's
:samp:`--show-object` option does not have this :qpdf:ref:`--show-object` option does not have this
limitation and will reveal the string as encoded in the original limitation and will reveal the string as encoded in the original
file. file.
@ -150,9 +150,9 @@ be aware of:
- While qpdf guarantees that keys present in the help will be present - While qpdf guarantees that keys present in the help will be present
in the output, those fields may be null or empty if the information in the output, those fields may be null or empty if the information
is not known or absent in the file. Also, if you specify is not known or absent in the file. Also, if you specify
:samp:`--json-keys`, the keys that are not listed :qpdf:ref:`--json-key`, the keys that are not listed
will be excluded entirely except for those that will be excluded entirely except for those that
:samp:`--json-help` says are always present. :qpdf:ref:`--json-help` says are always present.
- In a few places, there are keys with names containing - In a few places, there are keys with names containing
``pageposfrom1``. The values of these keys are null or an integer. If ``pageposfrom1``. The values of these keys are null or an integer. If
@ -168,7 +168,7 @@ be aware of:
- The image information included in the ``page`` section of the JSON - The image information included in the ``page`` section of the JSON
output includes the key "``filterable``". Note that the value of this output includes the key "``filterable``". Note that the value of this
field may depend on the :samp:`--decode-level` that field may depend on the :qpdf:ref:`--decode-level` that
you invoke qpdf with. The JSON output includes a top-level key you invoke qpdf with. The JSON output includes a top-level key
"``parameters``" that indicates the decode level used for computing "``parameters``" that indicates the decode level used for computing
whether a stream was filterable. For example, jpeg images will be whether a stream was filterable. For example, jpeg images will be

View File

@ -90,7 +90,7 @@ For a detailed list of changes, please see the file
- Handling of Weak Cryptography Algorithms - Handling of Weak Cryptography Algorithms
- From the qpdf CLI, the - From the qpdf CLI, the
:samp:`--allow-weak-crypto` is now required to :qpdf:ref:`--allow-weak-crypto` is now required to
suppress a warning when explicitly creating PDF files using RC4 suppress a warning when explicitly creating PDF files using RC4
encryption. While qpdf will always retain the ability to read encryption. While qpdf will always retain the ability to read
and write such files, doing so will require explicit and write such files, doing so will require explicit
@ -108,7 +108,7 @@ For a detailed list of changes, please see the file
(with no resource dictionary). (with no resource dictionary).
- Fix crash that could occur under certain conditions when using - Fix crash that could occur under certain conditions when using
:samp:`--pages` with files that had form :qpdf:ref:`--pages` with files that had form
fields. fields.
- Library Enhancements - Library Enhancements
@ -127,7 +127,7 @@ For a detailed list of changes, please see the file
- CLI Enhancements - CLI Enhancements
- Improve diagnostics around parsing - Improve diagnostics around parsing
:samp:`--pages` command-line options :qpdf:ref:`--pages` command-line options
- Packaging Changes - Packaging Changes
@ -139,7 +139,7 @@ For a detailed list of changes, please see the file
- When generating a file while preserving object streams, - When generating a file while preserving object streams,
unreferenced objects are correctly removed unless unreferenced objects are correctly removed unless
:samp:`--preserve-unreferenced` is specified. :qpdf:ref:`--preserve-unreferenced` is specified.
- Library Enhancements - Library Enhancements
@ -202,19 +202,19 @@ For a detailed list of changes, please see the file
- Operations that work on combining pages are much better about - Operations that work on combining pages are much better about
protecting form fields. In particular, protecting form fields. In particular,
:samp:`--split-pages` and :qpdf:ref:`--split-pages` and
:samp:`--pages` now preserve interaction form :qpdf:ref:`--pages` now preserve interaction form
functionality by copying the relevant form field information functionality by copying the relevant form field information
from the original files. Additionally, if you use from the original files. Additionally, if you use
:samp:`--pages` to select only some pages from :qpdf:ref:`--pages` to select only some pages from
the original input file, unused form fields are removed, which the original input file, unused form fields are removed, which
prevents lots of unused annotations from being retained. prevents lots of unused annotations from being retained.
- By default, :command:`qpdf` no longer allows - By default, :command:`qpdf` no longer allows
creation of encrypted PDF files whose user password is creation of encrypted PDF files whose user password is
non-empty and owner password is empty when a 256-bit key is in non-empty and owner password is empty when a 256-bit key is in
use. The :samp:`--allow-insecure` option, use. The :qpdf:ref:`--allow-insecure` option,
specified inside the :samp:`--encrypt` options, specified inside the :qpdf:ref:`--encrypt` options,
allows creation of such files. Behavior changes in the CLI are allows creation of such files. Behavior changes in the CLI are
avoided when possible, but an exception was made here because avoided when possible, but an exception was made here because
this is security-related. qpdf must always allow creation of this is security-related. qpdf must always allow creation of
@ -255,7 +255,7 @@ For a detailed list of changes, please see the file
removing, and and copying file attachments. See :ref:`attachments` for details. removing, and and copying file attachments. See :ref:`attachments` for details.
- Page splitting and merging operations, as well as - Page splitting and merging operations, as well as
:samp:`--flatten-rotation`, are better behaved :qpdf:ref:`--flatten-rotation`, are better behaved
with respect to annotations and interactive form fields. In with respect to annotations and interactive form fields. In
most cases, interactive form field functionality and proper most cases, interactive form field functionality and proper
formatting and functionality of annotations is preserved by formatting and functionality of annotations is preserved by
@ -284,7 +284,7 @@ For a detailed list of changes, please see the file
extraction of attachments. More detailed information can be extraction of attachments. More detailed information can be
obtained by following the reference to the file spec object. obtained by following the reference to the file spec object.
- Add numeric option to :samp:`--collate`. If - Add numeric option to :qpdf:ref:`--collate`. If
:samp:`--collate={n}` :samp:`--collate={n}`
is given, take pages in groups of is given, take pages in groups of
:samp:`{n}` from the given files. :samp:`{n}` from the given files.
@ -367,7 +367,7 @@ For a detailed list of changes, please see the file
- Bug Fixes - Bug Fixes
- The :samp:`--flatten-rotation` option applies - The :qpdf:ref:`--flatten-rotation` option applies
transformations to any annotations that may be on the page. transformations to any annotations that may be on the page.
- If a form XObject lacks a resources dictionary, consider any - If a form XObject lacks a resources dictionary, consider any
@ -390,7 +390,7 @@ For a detailed list of changes, please see the file
10.1.0: January 5, 2021 10.1.0: January 5, 2021
- CLI Enhancements - CLI Enhancements
- Add :samp:`--flatten-rotation` command-line - Add :qpdf:ref:`--flatten-rotation` command-line
option, which causes all pages that are rotated using option, which causes all pages that are rotated using
parameters in the page's dictionary to instead be identically parameters in the page's dictionary to instead be identically
rotated in the page's contents. The change is not user-visible rotated in the page's contents. The change is not user-visible
@ -510,7 +510,7 @@ For a detailed list of changes, please see the file
- Bug Fixes - Bug Fixes
- When concatenating content streams, as with - When concatenating content streams, as with
:samp:`--coalesce-contents`, there were cases :qpdf:ref:`--coalesce-contents`, there were cases
in which qpdf would merge two lexical tokens together, creating in which qpdf would merge two lexical tokens together, creating
invalid results. A newline is now inserted between merged invalid results. A newline is now inserted between merged
content streams if one is not already present. content streams if one is not already present.
@ -527,7 +527,7 @@ For a detailed list of changes, please see the file
already ignored the user's locale for numeric conversion. already ignored the user's locale for numeric conversion.
- Fix several instances in which warnings were not suppressed in - Fix several instances in which warnings were not suppressed in
spite of :samp:`--no-warn` and/or errors or spite of :qpdf:ref:`--no-warn` and/or errors or
warnings were written to standard output rather than standard warnings were written to standard output rather than standard
error. error.
@ -540,10 +540,10 @@ For a detailed list of changes, please see the file
- Enhancements - Enhancements
- New option :samp:`--warning-exit-0` causes qpdf - New option :qpdf:ref:`--warning-exit-0` causes qpdf
to exit with a status of ``0`` rather than ``3`` if there are to exit with a status of ``0`` rather than ``3`` if there are
warnings but no errors. Combine with warnings but no errors. Combine with
:samp:`--no-warn` to completely ignore :qpdf:ref:`--no-warn` to completely ignore
warnings. warnings.
- Performance improvements have been made to - Performance improvements have been made to
@ -656,17 +656,16 @@ For a detailed list of changes, please see the file
:command:`qpdf --json-help` for details. :command:`qpdf --json-help` for details.
- Add new option - Add new option
:samp:`--remove-unreferenced-resources` which :qpdf:ref:`--remove-unreferenced-resources` which
takes ``auto``, ``yes``, or ``no`` as arguments. The new takes ``auto``, ``yes``, or ``no`` as arguments. The new
``auto`` mode, which is the default, performs a fast heuristic ``auto`` mode, which is the default, performs a fast heuristic
over a PDF file when splitting pages to determine whether the over a PDF file when splitting pages to determine whether the
expensive process of finding and removing unreferenced expensive process of finding and removing unreferenced
resources is likely to be of benefit. For most files, this new resources is likely to be of benefit. For most files, this new
default will result in a significant performance improvement default will result in a significant performance improvement
for splitting pages. See :ref:`advanced-transformation` for a more detailed for splitting pages.
discussion.
- The :samp:`--preserve-unreferenced-resources` - The :qpdf:ref:`--preserve-unreferenced-resources`
is now just a synonym for is now just a synonym for
:samp:`--remove-unreferenced-resources=no`. :samp:`--remove-unreferenced-resources=no`.
@ -760,8 +759,8 @@ For a detailed list of changes, please see the file
- CLI Enhancements - CLI Enhancements
- Added options :samp:`--is-encrypted` and - Added options :qpdf:ref:`--is-encrypted` and
:samp:`--requires-password` for testing whether :qpdf:ref:`--requires-password` for testing whether
a file is encrypted or requires a password other than the a file is encrypted or requires a password other than the
supplied (or empty) password. These communicate via exit supplied (or empty) password. These communicate via exit
status, making them useful for shell scripts. They also work on status, making them useful for shell scripts. They also work on
@ -770,7 +769,7 @@ For a detailed list of changes, please see the file
- Added ``encrypt`` key to JSON options. With the exception of - Added ``encrypt`` key to JSON options. With the exception of
the reconstructed user password for older encryption formats, the reconstructed user password for older encryption formats,
this provides the same information as this provides the same information as
:samp:`--show-encryption` but in a consistent, :qpdf:ref:`--show-encryption` but in a consistent,
parseable format. See output of :command:`qpdf parseable format. See output of :command:`qpdf
--json-help` for details. --json-help` for details.
@ -778,7 +777,7 @@ For a detailed list of changes, please see the file
- In QDF mode, be sure not to write more than one XRef stream to - In QDF mode, be sure not to write more than one XRef stream to
a file, even when a file, even when
:samp:`--preserve-unreferenced` is used. :qpdf:ref:`--preserve-unreferenced` is used.
:command:`fix-qdf` assumes that there is only :command:`fix-qdf` assumes that there is only
one XRef stream, and that it appears at the end of the file. one XRef stream, and that it appears at the end of the file.
@ -824,7 +823,7 @@ For a detailed list of changes, please see the file
- CLI Enhancements - CLI Enhancements
- Addition of the :samp:`--show-crypto` option in - Addition of the :qpdf:ref:`--show-crypto` option in
support of selectable crypto providers, as described in :ref:`crypto`. support of selectable crypto providers, as described in :ref:`crypto`.
- Allow ``:even`` or ``:odd`` to be appended to numeric ranges - Allow ``:even`` or ``:odd`` to be appended to numeric ranges
@ -838,7 +837,7 @@ For a detailed list of changes, please see the file
- Bug Fix - Bug Fix
- Fix the name of the temporary file used by - Fix the name of the temporary file used by
:samp:`--replace-input` so that it doesn't :qpdf:ref:`--replace-input` so that it doesn't
require path splitting and works with paths include require path splitting and works with paths include
directories. directories.
@ -891,21 +890,21 @@ For a detailed list of changes, please see the file
- CLI Enhancements - CLI Enhancements
- The :samp:`--replace-input` option may be given - The :qpdf:ref:`--replace-input` option may be given
in place of an output file name. This causes qpdf to overwrite in place of an output file name. This causes qpdf to overwrite
the input file with the output. See the description of the input file with the output. See the description of
:samp:`--replace-input` in :ref:`basic-options` for more details. :qpdf:ref:`--replace-input` for more details.
- The :samp:`--recompress-flate` instructs - The :qpdf:ref:`--recompress-flate` instructs
:command:`qpdf` to recompress streams that are :command:`qpdf` to recompress streams that are
already compressed with ``/FlateDecode``. Useful with already compressed with ``/FlateDecode``. Useful with
:samp:`--compression-level`. :qpdf:ref:`--compression-level`.
- The - The
:samp:`--compression-level={level}` :samp:`--compression-level={level}`
sets the zlib compression level used for any streams compressed sets the zlib compression level used for any streams compressed
by ``/FlateDecode``. Most effective when combined with by ``/FlateDecode``. Most effective when combined with
:samp:`--recompress-flate`. :qpdf:ref:`--recompress-flate`.
- Library Enhancements - Library Enhancements
@ -998,8 +997,8 @@ For a detailed list of changes, please see the file
a file with linearization warnings but not errors, it now a file with linearization warnings but not errors, it now
properly exits with exit code 3 instead of 2. properly exits with exit code 3 instead of 2.
- The :samp:`--completion-bash` and - The :qpdf:ref:`--completion-bash` and
:samp:`--completion-zsh` options now work :qpdf:ref:`--completion-zsh` options now work
properly when qpdf is invoked as an AppImage. properly when qpdf is invoked as an AppImage.
- Calling ``QPDFWriter::set*EncryptionParameters`` on a - Calling ``QPDFWriter::set*EncryptionParameters`` on a
@ -1063,7 +1062,7 @@ For a detailed list of changes, please see the file
qpdf than the library, which may indicate a problem with the qpdf than the library, which may indicate a problem with the
installation. installation.
- New option :samp:`--remove-page-labels` will - New option :qpdf:ref:`--remove-page-labels` will
remove page labels before generating output. This used to remove page labels before generating output. This used to
happen if you ran :command:`qpdf --empty --pages .. happen if you ran :command:`qpdf --empty --pages ..
--`, but the behavior changed in qpdf 8.3.0. This --`, but the behavior changed in qpdf 8.3.0. This
@ -1090,7 +1089,7 @@ For a detailed list of changes, please see the file
during page splitting operations. during page splitting operations.
- Revert change that included preservation of outlines - Revert change that included preservation of outlines
(bookmarks) in :samp:`--split-pages`. The way (bookmarks) in :qpdf:ref:`--split-pages`. The way
it was implemented in 8.3.0 and 8.4.0 caused a very significant it was implemented in 8.3.0 and 8.4.0 caused a very significant
degradation of performance for splitting certain files. A degradation of performance for splitting certain files. A
future release of qpdf may re-introduce the behavior in a more future release of qpdf may re-introduce the behavior in a more
@ -1143,16 +1142,16 @@ For a detailed list of changes, please see the file
depth in :ref:`unicode-passwords`. depth in :ref:`unicode-passwords`.
- New options - New options
:samp:`--externalize-inline-images`, :qpdf:ref:`--externalize-inline-images`,
:samp:`--ii-min-bytes`, and :qpdf:ref:`--ii-min-bytes`, and
:samp:`--keep-inline-images` control qpdf's :qpdf:ref:`--keep-inline-images` control qpdf's
handling of inline images and possible conversion of them to handling of inline images and possible conversion of them to
regular images. By default, regular images. By default,
:samp:`--optimize-images` now also applies to :qpdf:ref:`--optimize-images` now also applies to
inline images. These options are discussed in :ref:`advanced-transformation`. inline images.
- Add options :samp:`--overlay` and - Add options :qpdf:ref:`--overlay` and
:samp:`--underlay` for overlaying or :qpdf:ref:`--underlay` for overlaying or
underlaying pages of other files onto output pages. See underlaying pages of other files onto output pages. See
:ref:`overlay-underlay` for :ref:`overlay-underlay` for
details. details.
@ -1162,32 +1161,32 @@ For a detailed list of changes, please see the file
non-ASCII characters, qpdf will try a number of alternative non-ASCII characters, qpdf will try a number of alternative
passwords to try to compensate for possible character encoding passwords to try to compensate for possible character encoding
errors. This behavior can be suppressed with the errors. This behavior can be suppressed with the
:samp:`--suppress-password-recovery` option. :qpdf:ref:`--suppress-password-recovery` option.
See :ref:`unicode-passwords` for a full See :ref:`unicode-passwords` for a full
discussion. discussion.
- Add the :samp:`--password-mode` option to - Add the :qpdf:ref:`--password-mode` option to
fine-tune how qpdf interprets password arguments, especially fine-tune how qpdf interprets password arguments, especially
when they contain non-ASCII characters. See :ref:`unicode-passwords` for more information. when they contain non-ASCII characters. See :ref:`unicode-passwords` for more information.
- In the :samp:`--pages` option, it is now - In the :qpdf:ref:`--pages` option, it is now
possible to copy the same page more than once from the same possible to copy the same page more than once from the same
file without using the previous workaround of specifying two file without using the previous workaround of specifying two
different paths to the same file. different paths to the same file.
- In the :samp:`--pages` option, allow use of "." - In the :qpdf:ref:`--pages` option, allow use of "."
as a shortcut for the primary input file. That way, you can do as a shortcut for the primary input file. That way, you can do
:command:`qpdf in.pdf --pages . 1-2 -- out.pdf` :command:`qpdf in.pdf --pages . 1-2 -- out.pdf`
instead of having to repeat :file:`in.pdf` instead of having to repeat :file:`in.pdf`
in the command. in the command.
- When encrypting with 128-bit and 256-bit encryption, new - When encrypting with 128-bit and 256-bit encryption, new
encryption options :samp:`--assemble`, encryption options :qpdf:ref:`--assemble`,
:samp:`--annotate`, :qpdf:ref:`--annotate`,
:samp:`--form`, and :qpdf:ref:`--form`, and
:samp:`--modify-other` allow more fine-grained :qpdf:ref:`--modify-other` allow more fine-grained
granularity in configuring options. Before, the granularity in configuring options. Before, the
:samp:`--modify` option only configured certain :qpdf:ref:`--modify` option only configured certain
predefined groups of permissions. predefined groups of permissions.
- Bug Fixes and Enhancements - Bug Fixes and Enhancements
@ -1198,7 +1197,7 @@ For a detailed list of changes, please see the file
file's internal structure shared these resource lists across file's internal structure shared these resource lists across
pages and if some but not all of the pages in the output did pages and if some but not all of the pages in the output did
not reference all the fonts and images. Using the not reference all the fonts and images. Using the
:samp:`--preserve-unreferenced-resources` :qpdf:ref:`--preserve-unreferenced-resources`
option would work around the incorrect behavior. This bug was option would work around the incorrect behavior. This bug was
the result of a typo in the code and a deficiency in the test the result of a typo in the code and a deficiency in the test
suite. The case that triggered the error was known, just not suite. The case that triggered the error was known, just not
@ -1326,11 +1325,11 @@ For a detailed list of changes, please see the file
- Page numbers (also known as page labels) are now preserved when - Page numbers (also known as page labels) are now preserved when
merging and splitting files with the merging and splitting files with the
:samp:`--pages` and :qpdf:ref:`--pages` and
:samp:`--split-pages` options. :qpdf:ref:`--split-pages` options.
- Bookmarks are partially preserved when splitting pages with the - Bookmarks are partially preserved when splitting pages with the
:samp:`--split-pages` option. Specifically, the :qpdf:ref:`--split-pages` option. Specifically, the
outlines dictionary and some supporting metadata are copied outlines dictionary and some supporting metadata are copied
into the split files. The result is that all bookmarks from the into the split files. The result is that all bookmarks from the
original file appear, those that point to pages that are original file appear, those that point to pages that are
@ -1340,48 +1339,48 @@ For a detailed list of changes, please see the file
operations. operations.
- Page collation: add new option - Page collation: add new option
:samp:`--collate`. When specified, the :qpdf:ref:`--collate`. When specified, the
semantics of :samp:`--pages` change from semantics of :qpdf:ref:`--pages` change from
concatenation to collation. See :ref:`page-selection` for examples and discussion. concatenation to collation. See :ref:`page-selection` for examples and discussion.
- Generation of information in JSON format, primarily to - Generation of information in JSON format, primarily to
facilitate use of qpdf from languages other than C++. Add new facilitate use of qpdf from languages other than C++. Add new
options :samp:`--json`, options :qpdf:ref:`--json`,
:samp:`--json-key`, and :qpdf:ref:`--json-key`, and
:samp:`--json-object` to generate a JSON :qpdf:ref:`--json-object` to generate a JSON
representation of the PDF file. Run :command:`qpdf representation of the PDF file. Run :command:`qpdf
--json-help` to get a description of the JSON --json-help` to get a description of the JSON
format. For more information, see :ref:`json`. format. For more information, see :ref:`json`.
- The :samp:`--generate-appearances` flag will - The :qpdf:ref:`--generate-appearances` flag will
cause qpdf to generate appearances for form fields if the PDF cause qpdf to generate appearances for form fields if the PDF
file indicates that form field appearances are out of date. file indicates that form field appearances are out of date.
This can happen when PDF forms are filled in by a program that This can happen when PDF forms are filled in by a program that
doesn't know how to regenerate the appearances of the filled-in doesn't know how to regenerate the appearances of the filled-in
fields. fields.
- The :samp:`--flatten-annotations` flag can be - The :qpdf:ref:`--flatten-annotations` flag can be
used to *flatten* annotations, including form fields. used to *flatten* annotations, including form fields.
Ordinarily, annotations are drawn separately from the page. Ordinarily, annotations are drawn separately from the page.
Flattening annotations is the process of combining their Flattening annotations is the process of combining their
appearances into the page's contents. You might want to do this appearances into the page's contents. You might want to do this
if you are going to rotate or combine pages using a tool that if you are going to rotate or combine pages using a tool that
doesn't understand about annotations. You may also want to use doesn't understand about annotations. You may also want to use
:samp:`--generate-appearances` when using this :qpdf:ref:`--generate-appearances` when using this
flag since annotations for outdated form fields are not flag since annotations for outdated form fields are not
flattened as that would cause loss of information. flattened as that would cause loss of information.
- The :samp:`--optimize-images` flag tells qpdf - The :qpdf:ref:`--optimize-images` flag tells qpdf
to recompresses every image using DCT (JPEG) compression as to recompresses every image using DCT (JPEG) compression as
long as the image is not already compressed with lossy long as the image is not already compressed with lossy
compression and recompressing the image reduces its size. The compression and recompressing the image reduces its size. The
additional options :samp:`--oi-min-width`, additional options :qpdf:ref:`--oi-min-width`,
:samp:`--oi-min-height`, and :qpdf:ref:`--oi-min-height`, and
:samp:`--oi-min-area` prevent recompression of :qpdf:ref:`--oi-min-area` prevent recompression of
images whose width, height, or pixel area (width × height) are images whose width, height, or pixel area (width × height) are
below a specified threshold. below a specified threshold.
- The :samp:`--show-object` option can now be - The :qpdf:ref:`--show-object` option can now be
given as :samp:`--show-object=trailer` to show given as :samp:`--show-object=trailer` to show
the trailer dictionary. the trailer dictionary.
@ -1531,12 +1530,12 @@ For a detailed list of changes, please see the file
:samp:`--keep-files-open={[yn]}` :samp:`--keep-files-open={[yn]}`
to override default determination of whether to keep files open to override default determination of whether to keep files open
when merging. Please see the discussion of when merging. Please see the discussion of
:samp:`--keep-files-open` in :ref:`basic-options` for additional details. :qpdf:ref:`--keep-files-open` for additional details.
8.2.0: August 16, 2018 8.2.0: August 16, 2018
- Command-line Enhancements - Command-line Enhancements
- Add :samp:`--no-warn` option to suppress - Add :qpdf:ref:`--no-warn` option to suppress
issuing warning messages. If there are any conditions that issuing warning messages. If there are any conditions that
would have caused warnings to be issued, the exit status is would have caused warnings to be issued, the exit status is
still 3. still 3.
@ -1556,7 +1555,7 @@ For a detailed list of changes, please see the file
- Bug fix: end of line characters were not properly handled - Bug fix: end of line characters were not properly handled
inside strings in some cases. inside strings in some cases.
- Bug fix: using :samp:`--progress` on very small - Bug fix: using :qpdf:ref:`--progress` on very small
files could cause an infinite loop. files could cause an infinite loop.
- API enhancements - API enhancements
@ -1596,15 +1595,14 @@ For a detailed list of changes, please see the file
old behavior should be desired, or if you have a case where old behavior should be desired, or if you have a case where
page splitting is very slow, the old behavior (and speed) can page splitting is very slow, the old behavior (and speed) can
be enabled by specifying be enabled by specifying
:samp:`--preserve-unreferenced-resources`. For :qpdf:ref:`--preserve-unreferenced-resources`.
additional details, please see :ref:`advanced-transformation`.
- When merging multiple PDF files, qpdf no longer leaves all the - When merging multiple PDF files, qpdf no longer leaves all the
files open. This makes it possible to merge numbers of files files open. This makes it possible to merge numbers of files
that may exceed the operating system's limit for the maximum that may exceed the operating system's limit for the maximum
number of open files. number of open files.
- The :samp:`--rotate` option's syntax has been - The :qpdf:ref:`--rotate` option's syntax has been
extended to make the page range optional. If you specify extended to make the page range optional. If you specify
:samp:`--rotate={angle}` :samp:`--rotate={angle}`
without specifying a page range, the rotation will be applied without specifying a page range, the rotation will be applied
@ -1613,10 +1611,10 @@ For a detailed list of changes, please see the file
down. down.
- When merging multiple files, the - When merging multiple files, the
:samp:`--verbose` option now prints information :qpdf:ref:`--verbose` option now prints information
about each file as it operates on that file. about each file as it operates on that file.
- When the :samp:`--progress` option is - When the :qpdf:ref:`--progress` option is
specified, qpdf will print a running indicator of its best specified, qpdf will print a running indicator of its best
guess at how far through the writing process it is. Note that, guess at how far through the writing process it is. Note that,
as with all progress meters, it's an approximation. This option as with all progress meters, it's an approximation. This option
@ -1672,7 +1670,7 @@ For a detailed list of changes, please see the file
it thinks it is through writing its output. Client programs can it thinks it is through writing its output. Client programs can
use this to implement reasonably accurate progress meters. The use this to implement reasonably accurate progress meters. The
:command:`qpdf` command line tool uses this to :command:`qpdf` command line tool uses this to
implement its :samp:`--progress` option. implement its :qpdf:ref:`--progress` option.
- New methods ``QPDFObjectHandle::newUnicodeString`` and - New methods ``QPDFObjectHandle::newUnicodeString`` and
``QPDFObject::unparseBinary`` have been added to allow for more ``QPDFObject::unparseBinary`` have been added to allow for more
@ -1733,7 +1731,7 @@ For a detailed list of changes, please see the file
:samp:`--linearize-pass1={file}` :samp:`--linearize-pass1={file}`
has been added for debugging qpdf's linearization code. has been added for debugging qpdf's linearization code.
- The option :samp:`--coalesce-contents` can be - The option :qpdf:ref:`--coalesce-contents` can be
used to combine content streams of a page whose contents are an used to combine content streams of a page whose contents are an
array of streams into a single stream. array of streams into a single stream.
@ -1782,8 +1780,7 @@ For a detailed list of changes, please see the file
password when opening encrypted files, and will optionally display password when opening encrypted files, and will optionally display
the encryption key used by a file. This is a non-standard the encryption key used by a file. This is a non-standard
operation, but it can be useful in certain situations. Please see operation, but it can be useful in certain situations. Please see
the discussion of :samp:`--password-is-hex-key` in the discussion of :qpdf:ref:`--password-is-hex-key` or the comments around
:ref:`basic-options` or the comments around
``QPDF::setPasswordIsHexKey`` in ``QPDF::setPasswordIsHexKey`` in
:file:`QPDF.hh` for additional details. :file:`QPDF.hh` for additional details.
@ -1820,8 +1817,8 @@ For a detailed list of changes, please see the file
or RunLength encoding. Library API enhancements and or RunLength encoding. Library API enhancements and
command-line options have been added to control this behavior. command-line options have been added to control this behavior.
See command-line options See command-line options
:samp:`--compress-streams` and :qpdf:ref:`--compress-streams` and
:samp:`--decode-level` and methods :qpdf:ref:`--decode-level` and methods
``QPDFWriter::setCompressStreams`` and ``QPDFWriter::setCompressStreams`` and
``QPDFWriter::setDecodeLevel``. ``QPDFWriter::setDecodeLevel``.
@ -1846,27 +1843,27 @@ For a detailed list of changes, please see the file
- Command-line arguments can now be read from files or standard - Command-line arguments can now be read from files or standard
input using ``@file`` or ``@-`` syntax. Please see :ref:`invocation`. input using ``@file`` or ``@-`` syntax. Please see :ref:`invocation`.
- :samp:`--rotate`: request page rotation - :qpdf:ref:`--rotate`: request page rotation
- :samp:`--newline-before-endstream`: ensure that - :qpdf:ref:`--newline-before-endstream`: ensure that
a newline appears before every ``endstream`` keyword in the a newline appears before every ``endstream`` keyword in the
file; used to prevent qpdf from breaking PDF/A compliance on file; used to prevent qpdf from breaking PDF/A compliance on
already compliant files. already compliant files.
- :samp:`--preserve-unreferenced`: preserve - :qpdf:ref:`--preserve-unreferenced`: preserve
unreferenced objects in the input PDF unreferenced objects in the input PDF
- :samp:`--split-pages`: break output into chunks - :qpdf:ref:`--split-pages`: break output into chunks
with fixed numbers of pages with fixed numbers of pages
- :samp:`--verbose`: print the name of each - :qpdf:ref:`--verbose`: print the name of each
output file that is created output file that is created
- :samp:`--compress-streams` and - :qpdf:ref:`--compress-streams` and
:samp:`--decode-level` replace :qpdf:ref:`--decode-level` replace
:samp:`--stream-data` for improving granularity :qpdf:ref:`--stream-data` for improving granularity
of controlling compression and decompression of stream data. of controlling compression and decompression of stream data.
The :samp:`--stream-data` option will remain The :qpdf:ref:`--stream-data` option will remain
available. available.
- When running :command:`qpdf --check` with other - When running :command:`qpdf --check` with other
@ -1877,8 +1874,8 @@ For a detailed list of changes, please see the file
reference table, or other similar operations. reference table, or other similar operations.
- Process :command:`--pages` earlier so that other - Process :command:`--pages` earlier so that other
options like :samp:`--show-pages` or options like :qpdf:ref:`--show-pages` or
:samp:`--split-pages` can operate on the file :qpdf:ref:`--split-pages` can operate on the file
after page splitting/merging has occurred. after page splitting/merging has occurred.
- API Changes. All new API calls are documented in their respective - API Changes. All new API calls are documented in their respective
@ -1911,7 +1908,7 @@ For a detailed list of changes, please see the file
``QPDFWriter`` methods. ``QPDFWriter`` methods.
6.0.0: November 10, 2015 6.0.0: November 10, 2015
- Implement :samp:`--deterministic-id` command-line - Implement :qpdf:ref:`--deterministic-id` command-line
option and ``QPDFWriter::setDeterministicID`` as well as C API option and ``QPDFWriter::setDeterministicID`` as well as C API
function ``qpdf_set_deterministic_ID`` for generating a function ``qpdf_set_deterministic_ID`` for generating a
deterministic ID for non-encrypted files. When this option is deterministic ID for non-encrypted files. When this option is
@ -2024,12 +2021,12 @@ For a detailed list of changes, please see the file
:file:`QPDFObjectHandle.hh` for additional :file:`QPDFObjectHandle.hh` for additional
notes. notes.
- Add :samp:`--show-npages` command-line option to - Add :qpdf:ref:`--show-npages` command-line option to
the :command:`qpdf` command to show the number of the :command:`qpdf` command to show the number of
pages in a file. pages in a file.
- Allow omission of the page range within - Allow omission of the page range within
:samp:`--pages` for the :qpdf:ref:`--pages` for the
:command:`qpdf` command. When omitted, the page :command:`qpdf` command. When omitted, the page
range is implicitly taken to be all the pages in the file. range is implicitly taken to be all the pages in the file.
@ -2156,8 +2153,9 @@ For a detailed list of changes, please see the file
``QPDFWriter::setMinimumPDFVersion`` and ``QPDFWriter::setMinimumPDFVersion`` and
``QPDFWriter::forcePDFVersion`` that accept an extension level, ``QPDFWriter::forcePDFVersion`` that accept an extension level,
and extended syntax for specifying forced and minimum versions on and extended syntax for specifying forced and minimum versions on
the command line as described in :ref:`advanced-transformation`. Corresponding functions the command line as described in :qpdf:ref:`--force-version` and
have been added to the C API as well. :qpdf:ref:`--min-version`. Corresponding functions have been added
to the C API as well.
- Minor fixes to prevent qpdf from referencing objects in the file - Minor fixes to prevent qpdf from referencing objects in the file
that are not referenced in the file's overall structure. Most that are not referenced in the file's overall structure. Most
@ -2213,12 +2211,12 @@ For a detailed list of changes, please see the file
``QPDFWriter``. ``QPDFWriter``.
- Removed the method ``decodeStreams``. This method was used by - Removed the method ``decodeStreams``. This method was used by
the :samp:`--check` option of the the :qpdf:ref:`--check` option of the
:command:`qpdf` command-line tool to force all :command:`qpdf` command-line tool to force all
streams in the file to be decoded, but it also suffered from streams in the file to be decoded, but it also suffered from
the problem of opening otherwise unreferenced streams and thus the problem of opening otherwise unreferenced streams and thus
could report false positive. The could report false positive. The
:samp:`--check` option now causes qpdf to go :qpdf:ref:`--check` option now causes qpdf to go
through all the motions of writing a new file based on the through all the motions of writing a new file based on the
original one, so it will always reference and check exactly original one, so it will always reference and check exactly
those parts of a file that any ordinary viewer would check. those parts of a file that any ordinary viewer would check.
@ -2307,7 +2305,7 @@ For a detailed list of changes, please see the file
- Options have been added to the :command:`qpdf` - Options have been added to the :command:`qpdf`
command-line tool for copying encryption parameters from another command-line tool for copying encryption parameters from another
file. See :ref:`basic-options`. file. (QXXXQ Link)
- New methods have been added to the ``QPDF`` object for adding and - New methods have been added to the ``QPDF`` object for adding and
removing pages. See :ref:`adding-and-remove-pages`. removing pages. See :ref:`adding-and-remove-pages`.
@ -2571,7 +2569,7 @@ For a detailed list of changes, please see the file
permissions, it does make them available so that applications that permissions, it does make them available so that applications that
use qpdf can enforce permissions. use qpdf can enforce permissions.
- The :samp:`--check` option to - The :qpdf:ref:`--check` option to
:command:`qpdf` has been extended to include some :command:`qpdf` has been extended to include some
additional information. additional information.

View File

@ -13,12 +13,12 @@ cryptography algorithm, and MD5, which is a weak hashing algorithm. In
version 10.4, qpdf generates warnings for some (but not all) cases of version 10.4, qpdf generates warnings for some (but not all) cases of
writing files with weak cryptography when invoked from the command-line. writing files with weak cryptography when invoked from the command-line.
These warnings can be suppressed using the These warnings can be suppressed using the
:samp:`--allow-weak-crypto` option. :qpdf:ref:`--allow-weak-crypto` option.
It is planned for qpdf version 11 to be stricter, making it an error to It is planned for qpdf version 11 to be stricter, making it an error to
write files with insecure cryptography from the command-line tool in write files with insecure cryptography from the command-line tool in
most cases without specifying the most cases without specifying the
:samp:`--allow-weak-crypto` flag and also to require :qpdf:ref:`--allow-weak-crypto` flag and also to require
explicit steps when using the C++ library to enable use of insecure explicit steps when using the C++ library to enable use of insecure
cryptography. cryptography.