From c0c7cef16cb666524e4809834063cfee5262eca1 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Fri, 22 Dec 2023 21:03:47 -0500 Subject: [PATCH] Generate a UNIX man page (fixes #874) --- CMakeLists.txt | 12 +- ChangeLog | 3 + generate_auto_job | 60 +- job.sums | 9 +- libqpdf/qpdf/auto_job_help.hh | 8 +- manual/CMakeLists.txt | 4 +- manual/cli.rst | 8 +- manual/qpdf.1 | 1109 +++++++++++++++++++++++++++++++++ manual/release-notes.rst | 5 + 9 files changed, 1198 insertions(+), 20 deletions(-) create mode 100644 manual/qpdf.1 diff --git a/CMakeLists.txt b/CMakeLists.txt index c264bfa3..e0e3976b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,8 @@ cmake_minimum_required(VERSION 3.16) # make_dist expects the version line to be on a line by itself after # the project line. When updating the version, check make_dist for all # the places it has to be updated. The doc configuration and CI build -# also find the version number here. +# also find the version number here. generate_auto_job also reads the +# version from here. project(qpdf VERSION 11.7.0 LANGUAGES C CXX) @@ -312,9 +313,12 @@ endif() set(auto_job_inputs # Keep in sync with SOURCES in generate_auto_job generate_auto_job + CMakeLists.txt manual/_ext/qpdf.py job.yml - manual/cli.rst) + manual/cli.rst + manual/qpdf.1.in +) set(auto_job_outputs # Keep in sync with DESTS in generate_auto_job @@ -323,7 +327,9 @@ set(auto_job_outputs libqpdf/qpdf/auto_job_help.hh libqpdf/qpdf/auto_job_schema.hh libqpdf/qpdf/auto_job_json_decl.hh - libqpdf/qpdf/auto_job_json_init.hh) + libqpdf/qpdf/auto_job_json_init.hh + manual/qpdf.1 +) if(GENERATE_AUTO_JOB) add_custom_command( diff --git a/ChangeLog b/ChangeLog index 8ccb689c..55c5112c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-12-22 Jay Berkenbilt + * Generate a more complete qpdf "man page" from the same source as + qpdf --help. Fixes #874. + * Allow the syntax "--encrypt --user-password=user-password --owner-password=owner-password --bits={40,128,256}" when encrypting PDF files. This is an alternative to the syntax diff --git a/generate_auto_job b/generate_auto_job index 10d74d89..d4143d5a 100755 --- a/generate_auto_job +++ b/generate_auto_job @@ -134,6 +134,12 @@ BANNER = f'''// // clang-format off //''' +MAN_BANNER = f'''.\\" +.\\" This file is automatically generated by {whoami}. +.\\" Edits will be automatically overwritten if the build is +.\\" run in maintainer mode. +.\\" +''' def warn(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) @@ -156,9 +162,11 @@ class Main: SOURCES = [ # Keep this list in sync with CMakeLists.txt: auto_job_inputs whoami, + 'CMakeLists.txt', 'manual/_ext/qpdf.py', 'job.yml', 'manual/cli.rst', + 'manual/qpdf.1.in', ] # DESTS is a map to the output files this code generates. These # generated files, as well as those added to DESTS later in the @@ -172,6 +180,7 @@ class Main: 'schema': 'libqpdf/qpdf/auto_job_schema.hh', 'json_decl': 'libqpdf/qpdf/auto_job_json_decl.hh', 'json_init': 'libqpdf/qpdf/auto_job_json_init.hh', + 'man': 'manual/qpdf.1', # Others are added in top } # SUMS contains a checksum for each source and destination and is @@ -277,7 +286,7 @@ class Main: for k, v in hashes.items(): print(f'{k} {v}', file=f) - def generate_doc(self, df, f): + def generate_doc(self, df, f, f_man): st_top = 0 st_topic = 1 st_option = 2 @@ -324,6 +333,23 @@ class Main: return True return False + def manify(text): + lines = text.split('\n') + out = [] + last_was_item = False + for line in lines: + if line.startswith('- '): + last_was_item = True + out.append('.IP \\[bu]') + out.append(line[2:]) + elif last_was_item and line.startswith(' '): + out.append(line[2:]) + else: + last_was_item = False + out.append(line) + return '\n'.join(out) + + last_option_topic = '' lineno = 0 for line in df.readlines(): if help_lines == 0: @@ -366,6 +392,8 @@ class Main: self.all_topics.add(topic) print(f'ap.addHelpTopic("{topic}", "{short_text}",' f' R"({long_text})");', file=f) + print(f'.SH {topic.upper()} ({short_text})', file=f_man) + print(manify(long_text), file=f_man, end='') help_lines += 1 state = st_top elif state == st_option: @@ -389,6 +417,11 @@ class Main: self.jdata[option[2:]]['help'] = short_text print(f'ap.addOptionHelp("{option}", "{topic}",' f' "{short_text}", R"({long_text})");', file=f) + if last_option_topic != topic: + print('.PP\nRelated Options:', file=f_man) + last_option_topic = topic + print(f'.TP\n.B {option} \\-\\- {short_text}', file=f_man) + print(manify(long_text), file=f_man, end='') help_lines += 1 state = st_top if help_lines == 20: @@ -400,6 +433,11 @@ class Main: print('ap.addHelpFooter("For detailed help, visit' ' the qpdf manual: https://qpdf.readthedocs.io\\n");', file=f) print('}\n', file=f) + print('''.SH SEE ALSO +.PP +For a summary of qpdf's options, please run \\fBqpdf \\-\\-help\\fR. +A complete manual can be found at https://qpdf.readthedocs.io. +''', file=f_man, end='') for i in self.referenced_topics: if i not in self.all_topics: raise Exception(f'help text referenced --help={i}') @@ -412,6 +450,14 @@ class Main: warn(f'{whoami}: regenerating auto job files') self.validate(data) + version = None + with open('CMakeLists.txt', 'r') as f: + for line in f.readlines(): + if line.strip().startswith('VERSION '): + version = line.strip().split(' ')[1] + if version is None: + raise Exception("can't read version from CMakeLists.txt") + # Keep track of which options are help options since they are # handled specially. Add the built-in help options to tables # that we populate as we read job.yml since we won't encounter @@ -436,9 +482,15 @@ class Main: for i in self.init: print(i, file=f) with write_file(self.DESTS['help']) as f: - with open('manual/cli.rst', 'r') as df: - print(BANNER, file=f) - self.generate_doc(df, f) + with write_file(self.DESTS['man']) as f_man: + print(MAN_BANNER, file=f_man, end='') + with open('manual/qpdf.1.in', 'r') as m_in: + for line in m_in.readlines(): + line = line.replace('@PROJECT_VERSION@', version) + print(line, file=f_man, end='') + with open('manual/cli.rst', 'r') as df: + print(BANNER, file=f) + self.generate_doc(df, f, f_man) # Compute the json files after the config and arg parsing # files. We need to have full information about all the diff --git a/job.sums b/job.sums index 46acb1c6..58931220 100644 --- a/job.sums +++ b/job.sums @@ -1,5 +1,6 @@ # Generated by generate_auto_job -generate_auto_job bf44181b610d335511a41b6c2b9c3497d0b023a1ca2c8e4537b34cb6262ce173 +CMakeLists.txt 66e8f9bf15a0c3394b1b13baaf5a709f7af35a6f733cd173092168e87202c7a5 +generate_auto_job f64733b79dcee5a0e3e8ccc6976448e8ddf0e8b6529987a66a7d3ab2ebc10a86 include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4 include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42 include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5 @@ -8,10 +9,12 @@ include/qpdf/auto_job_c_pages.hh b3cc0f21029f6d89efa043dcdbfa183cb59325b6506001c include/qpdf/auto_job_c_uo.hh ae21b69a1efa9333050f4833d465f6daff87e5b38e5106e49bbef5d4132e4ed1 job.yml 4f89fc7b622df897d30d403d8035aa36fc7de8d8c43042c736e0300d904cb05c libqpdf/qpdf/auto_job_decl.hh 9c6f701c29f3f764d620186bed92685a2edf2e4d11e4f4532862c05470cfc4d2 -libqpdf/qpdf/auto_job_help.hh ea1fdca2aa405bdf193732c5a2789c602efe2add3aa6e2dceecfacee175ce65c +libqpdf/qpdf/auto_job_help.hh bbd37ac0e8b3e38892a328ca08829d6e71c31ea3ab6c1a91b5f6983018695ef9 libqpdf/qpdf/auto_job_init.hh b4c2b3724fba61f1206fd3bae81951636852592f67a63ef9539839c2c5995065 libqpdf/qpdf/auto_job_json_decl.hh 06caa46eaf71db8a50c046f91866baa8087745a9474319fb7c86d92634cc8297 libqpdf/qpdf/auto_job_json_init.hh f5acb9aa103131cb68dec0e12c4d237a6459bdb49b24773c24f0c2724a462b8f libqpdf/qpdf/auto_job_schema.hh b53c006fec2e75b1b73588d242d49a32f7d3db820b1541de106c5d4c27fbb4d9 manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580 -manual/cli.rst 28cc6b36b26377404022bab467e6a16085023fdfa5d9d419595ffcae6c69d531 +manual/cli.rst 7bbeb2f234ca3d095c069f52e4a3c5e42a525b5ef6231955d036a6313eaffcd2 +manual/qpdf.1 745cb32c1772e6d84ef962aca7a439ee045226ae547330778a4a3ba3cd8d25df +manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b diff --git a/libqpdf/qpdf/auto_job_help.hh b/libqpdf/qpdf/auto_job_help.hh index b96c2564..4a0f3a8f 100644 --- a/libqpdf/qpdf/auto_job_help.hh +++ b/libqpdf/qpdf/auto_job_help.hh @@ -37,10 +37,10 @@ description of the JSON input file format. )"); 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 +- 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. diff --git a/manual/CMakeLists.txt b/manual/CMakeLists.txt index 53109937..6f4c0a33 100644 --- a/manual/CMakeLists.txt +++ b/manual/CMakeLists.txt @@ -22,7 +22,7 @@ if(BUILD_DOC) endif() set(MANUAL_SRC ${qpdf_SOURCE_DIR}/manual) -foreach(F qpdf.1 fix-qdf.1 zlib-flate.1) +foreach(F fix-qdf.1 zlib-flate.1) configure_file( ${MANUAL_SRC}/${F}.in ${CMAKE_CURRENT_BINARY_DIR}/${F} @@ -129,7 +129,7 @@ if(NOT WIN32) # environment, especially when all they do is refer people to the # manual. install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/qpdf.1 + ${qpdf_SOURCE_DIR}/manual/qpdf.1 ${CMAKE_CURRENT_BINARY_DIR}/fix-qdf.1 ${CMAKE_CURRENT_BINARY_DIR}/zlib-flate.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 diff --git a/manual/cli.rst b/manual/cli.rst index 9f173ed8..93131542 100644 --- a/manual/cli.rst +++ b/manual/cli.rst @@ -184,10 +184,10 @@ Exit Status 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 + - 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 The exit status of :command:`qpdf` may be interpreted as follows: diff --git a/manual/qpdf.1 b/manual/qpdf.1 new file mode 100644 index 00000000..b8bf5033 --- /dev/null +++ b/manual/qpdf.1 @@ -0,0 +1,1109 @@ +.\" +.\" This file is automatically generated by generate_auto_job. +.\" Edits will be automatically overwritten if the build is +.\" run in maintainer mode. +.\" +.TH QPDF "1" "" "qpdf version 11.7.0" "User Commands" +.SH NAME +qpdf \- PDF transformation software +.SH SYNOPSIS +.B qpdf +.RI "[ " options " ] " infilename " [ " outfilename " ]" +.SH DESCRIPTION +The qpdf program is used to convert one PDF file to another equivalent +PDF file. It is capable of performing a variety of transformations +such as linearization (also known as web optimization or fast web +viewing), encryption, and decryption of PDF files. It also has many +options for inspecting or checking PDF files, some of which are +useful primarily to PDF developers. +.PP +For a summary of qpdf's options, please run \fBqpdf \-\-help\fR. A +complete manual can be found at https://qpdf.readthedocs.io. +.SH USAGE (basic invocation) +Read a PDF file, apply transformations or modifications, and write +a new PDF file. + +Usage: qpdf [infile] [options] [outfile] + OR qpdf --help[={topic|--option}] + +.IP \[bu] +infile, options, and outfile may be in any order as long as infile +precedes outfile. +.IP \[bu] +Use --empty in place of an input file for a zero-page, empty input +.IP \[bu] +Use --replace-input in place of an output file to overwrite the +input file with the output +.IP \[bu] +outfile may be - to write to stdout; reading from stdin is not supported +.IP \[bu] +@filename is an argument file; each line is treated as a separate +command-line argument +.IP \[bu] +@- may be used to read arguments from stdin +.IP \[bu] +Later options may override earlier options if contradictory +.PP +Related Options: +.TP +.B --empty \-\- use empty file as input +Use in place of infile for an empty input. Especially useful +with --pages. +.TP +.B --replace-input \-\- overwrite input with output +Use in place of outfile to overwrite the input file with the output. +.TP +.B --job-json-file \-\- job JSON file +--job-json-file=file + +Specify the name of a file whose contents are expected to +contain a QPDFJob JSON file. Run qpdf --job-json-help for a +description of the JSON input file format. +.SH EXIT-STATUS (meanings of qpdf's exit codes) +Meaning of exit codes: + +.IP \[bu] +0: no errors or warnings +.IP \[bu] +1: not used by qpdf but may be used by the shell if unable to invoke qpdf +.IP \[bu] +2: errors detected +.IP \[bu] +3: warnings detected, unless --warning-exit-0 is given +.PP +Related Options: +.TP +.B --warning-exit-0 \-\- exit 0 even with warnings +Use exit status 0 instead of 3 when warnings are present. When +combined with --no-warn, warnings are completely ignored. +.SH COMPLETION (shell completion) +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. +.PP +Related Options: +.TP +.B --completion-bash \-\- enable bash completion +Output a command that enables bash completion +.TP +.B --completion-zsh \-\- enable zsh completion +Output a command that enables zsh completion +.SH HELP (information about qpdf) +Help options provide some information about qpdf itself. Help +options are only valid as the first and only command-line argument. +.PP +Related Options: +.TP +.B --help \-\- provide help +--help[=--option|topic] + +--help: provide general information and a list of topics +--help=--option: provide help on a specific option +--help=topic: provide help on a topic +.TP +.B --version \-\- show qpdf version +Display the version of qpdf. +.TP +.B --copyright \-\- show copyright information +Display copyright and license information. +.TP +.B --show-crypto \-\- show available crypto providers +Show a list of available crypto providers, one per line. The +default provider is shown first. +.TP +.B --job-json-help \-\- show format of job JSON +Describe the format of the QPDFJob JSON input used by +--job-json-file. +.SH GENERAL (general options) +General options control qpdf's behavior in ways that are not +directly related to the operation it is performing. +.PP +Related Options: +.TP +.B --password \-\- password for encrypted file +--password=password + +Specify a password for an encrypted, password-protected file. +Not needed for encrypted files without a password. +.TP +.B --password-file \-\- read password from a file +--password-file=filename + +The first line of the specified file is used as the password. +This is used in place of the --password option. +.TP +.B --verbose \-\- print additional information +Output additional information about various things qpdf is +doing, including information about files created and operations +performed. +.TP +.B --progress \-\- show progress when writing +Indicate progress when writing files. +.TP +.B --no-warn \-\- suppress printing of warning messages +Suppress printing of warning messages. If warnings were +encountered, qpdf still exits with exit status 3. +Use --warning-exit-0 with --no-warn to completely ignore +warnings. +.TP +.B --deterministic-id \-\- generate ID deterministically +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. +.TP +.B --allow-weak-crypto \-\- allow insecure cryptographic algorithms +Allow creation of files with weak cryptographic algorithms. This +option is necessary to create 40-bit files or 128-bit files that +use RC4 encryption. +.TP +.B --keep-files-open \-\- manage keeping multiple files open +--keep-files-open=[y|n] + +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. +.TP +.B --keep-files-open-threshold \-\- set threshold for --keep-files-open +--keep-files-open-threshold=count + +Set the threshold used by --keep-files-open, overriding the +default value of 200. +.SH ADVANCED-CONTROL (tweak qpdf's behavior) +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. +.PP +Related Options: +.TP +.B --password-is-hex-key \-\- provide hex-encoded encryption key +Provide the underlying file encryption key as a hex-encoded +string rather than supplying a password. This is an expert +option. +.TP +.B --suppress-password-recovery \-\- don't try different password encodings +Suppress qpdf's usual behavior of attempting different encodings +of a password that contains non-ASCII Unicode characters if the +first attempt doesn't succeed. +.TP +.B --password-mode \-\- tweak how qpdf encodes passwords +--password-mode=mode + +Fine-tune how qpdf controls encoding of Unicode passwords. Valid +options are auto, bytes, hex-bytes, and unicode. +.TP +.B --suppress-recovery \-\- suppress error recovery +Avoid attempting to recover when errors are found in a file's +cross reference table or stream lengths. +.TP +.B --ignore-xref-streams \-\- use xref tables rather than streams +Ignore any cross-reference streams in the file, falling back to +cross-reference tables or triggering document recovery. +.SH TRANSFORMATION (make structural PDF changes) +The options below tell qpdf to apply transformations that change +the structure without changing the content. +.PP +Related Options: +.TP +.B --linearize \-\- linearize (web-optimize) output +Create linearized (web-optimized) output files. +.TP +.B --encrypt \-\- start encryption options +--encrypt [options] -- + +Run qpdf --help=encryption for details. +.TP +.B --decrypt \-\- remove encryption from input file +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. +.TP +.B --remove-restrictions \-\- remove security restrictions from input file +Remove restrictions associated with digitally signed PDF files. +This may be combined with --decrypt to allow free editing of +previously signed/encrypted files. This option invalidates the +signature but leaves its visual appearance intact. +.TP +.B --copy-encryption \-\- copy another file's encryption details +--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. +.TP +.B --encryption-file-password \-\- supply password for --copy-encryption +--encryption-file-password=password + +If the file named in --copy-encryption requires a password, use +this option to supply the password. +.TP +.B --qdf \-\- enable viewing PDF code in a text editor +Create a PDF file suitable for viewing in a text editor and even +editing. This is for editing 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. Please see +the "QDF Mode" chapter in the manual. +.TP +.B --no-original-object-ids \-\- omit original object IDs in qdf +Omit comments in a QDF file indicating the object ID an object +had in the original file. +.TP +.B --compress-streams \-\- compress uncompressed streams +--compress-streams=[y|n] + +Setting --compress-streams=n prevents qpdf from compressing +uncompressed streams. This can be useful if you are leaving some +streams uncompressed intentionally. +.TP +.B --decode-level \-\- control which streams to uncompress +--decode-level=parameter + +When uncompressing streams, control which types of compression +schemes should be uncompressed: +.IP \[bu] +none: don't uncompress anything. This is the default with +--json-output. +.IP \[bu] +generalized: uncompress streams compressed with a +general-purpose compression algorithm. This is the default +except when --json-output is given. +.IP \[bu] +specialized: in addition to generalized, also uncompress +streams compressed with a special-purpose but non-lossy +compression scheme +.IP \[bu] +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. +.TP +.B --stream-data \-\- control stream compression +--stream-data=parameter + +This option controls how streams are compressed in the output. +It is less granular than the newer options, --compress-streams +and --decode-level. + +Parameters: +.IP \[bu] +compress: same as --compress-streams=y --decode-level=generalized +.IP \[bu] +preserve: same as --compress-streams=n --decode-level=none +.IP \[bu] +uncompress: same as --compress-streams=n --decode-level=generalized +.TP +.B --recompress-flate \-\- uncompress and recompress flate +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. +.TP +.B --compression-level \-\- set compression level for flate +--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. +.TP +.B --normalize-content \-\- fix newlines in content streams +--normalize-content=[y|n] + +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. +.TP +.B --object-streams \-\- control use of object streams +--object-streams=mode + +Control what qpdf does regarding object streams. Options: +.IP \[bu] +preserve: preserve original object streams, if any (the default) +.IP \[bu] +disable: create output files with no object streams +.IP \[bu] +generate: create object streams, and compress objects when possible +.TP +.B --preserve-unreferenced \-\- preserve unreferenced objects +Preserve all objects from the input even if not referenced. +.TP +.B --remove-unreferenced-resources \-\- remove unreferenced page resources +--remove-unreferenced-resources=parameter + +Remove from a page's resource dictionary any resources that are +not referenced in the page's contents. Parameters: "auto" +(default), "yes", "no". +.TP +.B --preserve-unreferenced-resources \-\- use --remove-unreferenced-resources=no +Synonym for --remove-unreferenced-resources=no. Use that instead. +.TP +.B --newline-before-endstream \-\- force a newline before endstream +For an extra newline before endstream. Using this option enables +qpdf to preserve PDF/A when rewriting such files. +.TP +.B --coalesce-contents \-\- combine content streams +If a page has an array of content streams, concatenate them into +a single content stream. +.TP +.B --externalize-inline-images \-\- convert inline to regular images +Convert inline images to regular images. +.TP +.B --ii-min-bytes \-\- set minimum size for --externalize-inline-images +--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. +.TP +.B --min-version \-\- set minimum PDF version +--min-version=version + +Force the PDF version of the output to be at least the specified +version. The version number format is +"major.minor[.extension-level]", which sets the version header +to "major.minor" and the extension level, if specified, to +"extension-level". +.TP +.B --force-version \-\- set output PDF version +--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. +.SH PAGE-RANGES (page range syntax) +A full description of the page range syntax, with examples, can be +found in the manual. Summary: + +.IP \[bu] +a,b,c pages a, b, and c +.IP \[bu] +a-b pages a through b inclusive; if a > b, this counts down +.IP \[bu] +r where represents a number is the th page from the end +.IP \[bu] +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. +.SH MODIFICATION (change parts of the PDF) +Modification options make systematic changes to certain parts of +the PDF, causing the PDF to render differently from the original. +.PP +Related Options: +.TP +.B --pages \-\- begin page selection +--pages file [--password=password] [page-range] [...] -- + +Run qpdf --help=page-selection for details. +.TP +.B --collate \-\- collate with --pages +--collate[=n] + +Collate rather than concatenate pages specified with --pages. +With a numeric parameter, collate in groups of n. The default +is 1. Run qpdf --help=page-selection for additional details. +.TP +.B --split-pages \-\- write pages to separate files +--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: + +.IP \[bu] +If the string %d appears in the output file name, it is replaced with a +zero-padded page range starting from 1 +.IP \[bu] +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 +.IP \[bu] +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. +.TP +.B --overlay \-\- begin overlay options +--overlay file [options] -- + +Overlay pages from another file on the output. +Run qpdf --help=overlay-underlay for details. +.TP +.B --underlay \-\- begin underlay options +--underlay file [options] -- + +Underlay pages from another file on the output. +Run qpdf --help=overlay-underlay for details. +.TP +.B --flatten-rotation \-\- remove rotation from page dictionary +For each page that is rotated using the /Rotate key in the +page's dictionary, remove the /Rotate key and implement the +identical rotation semantics by modifying the page's contents. +This can be useful if a broken PDF viewer fails to properly +consider page rotation metadata. +.TP +.B --flatten-annotations \-\- push annotations into content +--flatten-annotations=parameter + +Push page annotations into the content streams. This may be +necessary in some case when printing or splitting files. +Parameters: "all", "print", "screen". +.TP +.B --rotate \-\- rotate pages +--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. +.TP +.B --generate-appearances \-\- generate appearances for form fields +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. +.TP +.B --optimize-images \-\- use efficient compression for images +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 +.TP +.B --oi-min-width \-\- minimum width for --optimize-images +--oi-min-width=width + +Don't optimize images whose width is below the specified value. +.TP +.B --oi-min-height \-\- minimum height for --optimize-images +--oi-min-height=height + +Don't optimize images whose height is below the specified value. +.TP +.B --oi-min-area \-\- minimum area for --optimize-images +--oi-min-area=area-in-pixels + +Don't optimize images whose area in pixels is below the specified value. +.TP +.B --keep-inline-images \-\- exclude inline images from optimization +Prevent inline images from being considered by --optimize-images. +.TP +.B --remove-page-labels \-\- remove explicit page numbers +Exclude page labels (explicit page numbers) from the output file. +.SH ENCRYPTION (create encrypted files) +Create encrypted files. Usage: + +--encrypt \ + [--user-password=user-password] \ + [--owner-password=owner-password] \ + --bits=key-length [options] -- + +OR + +--encrypt user-password owner-password key-length [options] -- + +The first form, with flags for the passwords and bit length, was +introduced in qpdf 11.7.0. Only the --bits option is is mandatory. +This form allows you to use any text as the password. If passwords +are specified, they must be given before the --bits option. + +The second form has been in qpdf since the beginning and wil +continue to be supported. Either or both of user-password and +owner-password may be empty strings. + +The key-length parameter must be either 40, 128, or 256. The user +and/or owner password may be omitted. Omitting either pasword +enables the PDF file to be opened without a password. Specifying +the same value for the user and owner password and specifying an +empty owner password are both considered insecure. + +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=[y|n] restrict comments, filling forms, and signing + --extract=[y|n] restrict text/graphic extraction + --modify=[y|n] restrict document modification + --print=[y|n] restrict printing + +Options for 128-bit or 256-bit: + --accessibility=[y|n] restrict accessibility (usually ignored) + --annotate=[y|n] restrict commenting/filling form fields + --assemble=[y|n] restrict document assembly + --extract=[y|n] restrict text/graphic extraction + --form=[y|n] restrict filling form fields + --modify-other=[y|n] 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=[y|n] 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 +.PP +Related Options: +.TP +.B --user-password \-\- specify user password +--user-password=user-password + +Set the user password of the encrypted file. +.TP +.B --owner-password \-\- specify owner password +--owner-password=owner-password + +Set the owner password of the encrypted file. +.TP +.B --bits \-\- specify encryption key length +--bits={48|128|256} + +Specify the encryption key length. For best security, always use +a key length of 256. +.TP +.B --accessibility \-\- restrict document accessibility +--accessibility=[y|n] + +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. +.TP +.B --annotate \-\- restrict document annotation +--annotate=[y|n] + +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. +.TP +.B --assemble \-\- restrict document assembly +--assemble=[y|n] + +Enable/disable document assembly (rotation and reordering of +pages). This option is not available with 40-bit encryption. +.TP +.B --extract \-\- restrict text/graphic extraction +--extract=[y|n] + +Enable/disable text/graphic extraction for purposes other than +accessibility. +.TP +.B --form \-\- restrict form filling +--form=[y|n] + +Enable/disable whether filling form fields is allowed even if +modification of annotations is disabled. This option is not +available with 40-bit encryption. +.TP +.B --modify-other \-\- restrict other modifications +--modify-other=[y|n] + +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. +.TP +.B --modify \-\- restrict document modification +--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 +.TP +.B --print \-\- restrict printing +--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) +.TP +.B --cleartext-metadata \-\- don't encrypt metadata +If specified, don't encrypt document metadata even when +encrypting the rest of the document. This option is not +available with 40-bit encryption. +.TP +.B --use-aes \-\- use AES with 128-bit encryption +--use-aes=[y|n] + +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. +.TP +.B --allow-insecure \-\- allow empty owner passwords +Allow creation of PDF files with empty owner passwords and +non-empty user passwords when using 256-bit encryption. +.TP +.B --force-V4 \-\- force V=4 in encryption dictionary +This option is for testing and is never needed in practice since +qpdf does this automatically when needed. +.TP +.B --force-R5 \-\- use unsupported R=5 encryption +Use an undocumented, unsupported, deprecated encryption +algorithm that existed only in Acrobat version IX. This option +should not be used except for compatibility testing. +.SH PAGE-SELECTION (select pages from one or more files) +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 and 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: + +.IP \[bu] +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 + +.IP \[bu] +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. +.SH OVERLAY-UNDERLAY (overlay/underlay pages from other files) +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. +.PP +Related Options: +.TP +.B --to \-\- destination pages for underlay/overlay +--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. +.TP +.B --from \-\- source pages for underlay/overlay +--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. +.TP +.B --repeat \-\- overlay/underlay pages to repeat +--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. +.SH ATTACHMENTS (work with embedded files) +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. See also +--help=--list-attachments and --help=--show-attachment. +.PP +Related Options: +.TP +.B --add-attachment \-\- start add attachment options +--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. +.TP +.B --copy-attachments-from \-\- start copy attachment options +--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. +.TP +.B --remove-attachment \-\- remove an embedded file +--remove-attachment=key + +Remove an embedded file using its key. Get the key with +--list-attachments. +.SH PDF-DATES (PDF date format) +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: +.IP \[bu] +D:20210207161528-05'00' February 7, 2021 at 4:15:28 p.m. +.IP \[bu] +D:20210207211528Z February 7, 2021 at 21:15:28 UTC +.SH ADD-ATTACHMENT (attach (embed) files) +The options listed below appear between --add-attachment and its +terminating "--". +.PP +Related Options: +.TP +.B --key \-\- specify attachment key +--key=key + +Specify the key to use for the attachment in the embedded files +table. It defaults to the last element (basename) of the +attached file's filename. +.TP +.B --filename \-\- set attachment's displayed filename +--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 (basename) of the attached file's filename. +.TP +.B --creationdate \-\- set attachment's creation date +--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. +.TP +.B --moddate \-\- set attachment's modification date +--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. +.TP +.B --mimetype \-\- attachment mime type, e.g. application/pdf +--mimetype=type/subtype + +Specify the mime type for the attachment, such as text/plain, +application/pdf, image/png, etc. +.TP +.B --description \-\- set attachment's description +--description="text" + +Supply descriptive text for the attachment, displayed by some +PDF viewers. +.TP +.B --replace \-\- replace attachment with same key +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. +.SH COPY-ATTACHMENTS (copy attachments from another file) +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. +.PP +Related Options: +.TP +.B --prefix \-\- key prefix for copying attachments +--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. +.SH INSPECTION (inspect PDF files) +These options provide tools for inspecting PDF files. When any of +the options in this section are specified, no output file may be +given. +.PP +Related Options: +.TP +.B --is-encrypted \-\- silently test whether a file is encrypted +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. +.TP +.B --requires-password \-\- silently test a file's password +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 +.TP +.B --check \-\- partially check whether PDF is valid +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. See also qpdf --help=exit-status. +.TP +.B --show-encryption \-\- information about encrypted files +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. +.TP +.B --show-encryption-key \-\- show key with --show-encryption +When used with --show-encryption or --check, causes the +underlying encryption key to be displayed. +.TP +.B --check-linearization \-\- check linearization tables +Check to see whether a file is linearized and, if so, whether +the linearization hint tables are correct. +.TP +.B --show-linearization \-\- show linearization hint tables +Check and display all data in the linearization hint tables. +.TP +.B --show-xref \-\- show cross reference data +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. +.TP +.B --show-object \-\- show contents of an object +--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"). +.TP +.B --raw-stream-data \-\- show raw stream data +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. +.TP +.B --filtered-stream-data \-\- show filtered stream data +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. +.TP +.B --show-npages \-\- show number of pages +Print the number of pages in the input file on a line by itself. +Useful for scripts. +.TP +.B --show-pages \-\- display page dictionary information +Show the object and generation number for each page dictionary +object and for each content stream associated with the page. +.TP +.B --with-images \-\- include image details with --show-pages +When used with --show-pages, also shows the object and +generation numbers for the image objects on each page. +.TP +.B --list-attachments \-\- list embedded files +Show the key and stream number for each embedded file. Combine +with --verbose for more detailed information. +.TP +.B --show-attachment \-\- export an embedded file +--show-attachment=key + +Write the contents of the specified attachment to standard +output as binary data. Get the key with --list-attachments. +.SH JSON (JSON output for PDF information) +Show information about the PDF file in JSON format. Please see the +JSON chapter in the qpdf manual for details. +.PP +Related Options: +.TP +.B --json \-\- show file in JSON format +--json[=version] + +Generate a JSON representation of the file. This is described in +depth in the JSON section of the manual. "version" may be a +specific version or "latest" (the default). Run qpdf --json-help +for a description of the generated JSON object. +.TP +.B --json-help \-\- show format of JSON output +--json-help[=version] + +Describe the format of the JSON output by writing to standard +output a JSON object with the same keys and with values +containing descriptive text. +.TP +.B --json-key \-\- limit which keys are in JSON output +--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. With --json-output, when not given, +only the "qpdf" key will appear in the output. +.TP +.B --json-object \-\- limit which objects are in JSON +--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. +.TP +.B --json-stream-data \-\- how to handle streams in json output +--json-stream-data={none|inline|file} + +When used with --json, this option controls whether streams in +json output should be omitted, written inline (base64-encoded) +or written to a file. If "file" is chosen, the file will be the +name of the output file appended with -nnn where nnn is the +object number. The prefix can be overridden with +--json-stream-prefix. The default is "none", except +when --json-output is specified, in which case the default is +"inline". +.TP +.B --json-stream-prefix \-\- prefix for json stream data files +--json-stream-prefix=file-prefix + +When used with --json-stream-data=file, --json-stream-data=file-prefix +sets the prefix for stream data files, overriding the default, +which is to use the output file name. Whatever is given here +will be appended with -nnn to create the name of the file that +will contain the data for the stream stream in object nnn. +.TP +.B --json-output \-\- apply defaults for JSON serialization +--json-output[=version] + +Implies --json=version. Changes default values for certain +options so that the JSON output written is the most faithful +representation of the original PDF and contains no additional +JSON keys. See also --json-stream-data, --json-stream-prefix, +and --decode-level. +.TP +.B --json-input \-\- input file is qpdf JSON +Treat the input file as a JSON file in qpdf JSON format. See the +"qpdf JSON Format" section of the manual for information about +how to use this option. +.TP +.B --update-from-json \-\- update a PDF from qpdf JSON +--update-from-json=qpdf-json-file + +Update a PDF file from a JSON file. Please see the "qpdf JSON" +chapter of the manual for information about how to use this +option. +.SH TESTING (options for testing or debugging) +The options below are useful when writing automated test code that +includes files created by qpdf or when testing qpdf itself. +.PP +Related Options: +.TP +.B --static-id \-\- use a fixed document ID +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. +.TP +.B --static-aes-iv \-\- use a fixed AES vector +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. +.TP +.B --linearize-pass1 \-\- save pass 1 of linearization +--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. +.TP +.B --test-json-schema \-\- test generated json against schema +This is used by qpdf's test suite to check consistency between +the output of qpdf --json and the output of qpdf --json-help. +.TP +.B --report-memory-usage \-\- best effort report of memory usage +This is used by qpdf's performance test suite to report the +maximum amount of memory used in supported environments. +.SH SEE ALSO +.PP +For a summary of qpdf's options, please run \fBqpdf \-\-help\fR. +A complete manual can be found at https://qpdf.readthedocs.io. diff --git a/manual/release-notes.rst b/manual/release-notes.rst index 50b0aec3..443a4b01 100644 --- a/manual/release-notes.rst +++ b/manual/release-notes.rst @@ -80,6 +80,11 @@ Planned changes for future 12.x (subject to change): ``README-maintainer.md`` for a detailed explanation of how to maintain this. + - Package Enhancements: + + - A UNIX man page is now automatically generated from the + documentation. It contains the same text as ``qpdf --help=all``. + - Library Enhancements: - Add C++ functions ``qpdf_c_wrap`` and ``qpdf_c_get_qpdf`` to the