From a0e70b370afcb628caabb3e8610d600fea6966f3 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Fri, 5 Jan 2024 12:16:13 -0500 Subject: [PATCH] Wiring for --set-page-labels: manual (non-bisectable commit) This commit contains only the manual changes. It is separated for clarity. This commit would not pass CI because it lacks the automated changes, which appear in the next commit. --- include/qpdf/QPDFJob.hh | 20 +++++++ job.yml | 7 +++ libqpdf/QPDFJob_argv.cc | 20 +++++++ libqpdf/QPDFJob_config.cc | 22 ++++++++ libqpdf/QPDFJob_json.cc | 21 ++++++++ manual/cli.rst | 110 ++++++++++++++++++++++++++++++++++++++ qpdf/sizes.cc | 1 + 7 files changed, 201 insertions(+) diff --git a/include/qpdf/QPDFJob.hh b/include/qpdf/QPDFJob.hh index 01fe35ae..9a7afb0f 100644 --- a/include/qpdf/QPDFJob.hh +++ b/include/qpdf/QPDFJob.hh @@ -296,6 +296,23 @@ class QPDFJob Config* config; }; + class PageLabelsConfig { + friend class QPDFJob; + friend class Config; + + public: + QPDF_DLL + Config* endSetPageLabels(); + +#include + + private: + PageLabelsConfig(Config*); + PageLabelsConfig(PagesConfig const&) = delete; + + Config* config; + }; + class Config { friend class QPDFJob; @@ -313,6 +330,8 @@ class QPDFJob Config* outputFile(std::string const& filename); QPDF_DLL Config* replaceInput(); + QPDF_DLL + Config* setPageLabels(std::vector const& specs); QPDF_DLL std::shared_ptr copyAttachmentsFrom(); @@ -675,6 +694,7 @@ class QPDFJob bool json_output{false}; std::string update_from_json; bool report_mem_usage{false}; + std::vector page_label_specs; }; std::shared_ptr m; }; diff --git a/job.yml b/job.yml index d342e7a2..3d918385 100644 --- a/job.yml +++ b/job.yml @@ -94,6 +94,7 @@ options: - underlay - empty - replace-input + - set-page-labels positional: true bare: - add-attachment @@ -134,6 +135,7 @@ options: - report-memory-usage - requires-password - remove-restrictions + - set-page-labels - show-encryption - show-encryption-key - show-linearization @@ -282,6 +284,9 @@ options: required_parameter: prefix: prefix password: password + - table: set page labels + prefix: PageLabels + positional: true json: # The structure of this section defines what the json input to # QPDFJob looks like. If a key starts with underscore, it does not @@ -437,6 +442,8 @@ json: remove-page-labels: report-memory-usage: rotate: + set-page-labels: + - null overlay: _file: "source file for overlay" UO.password: diff --git a/libqpdf/QPDFJob_argv.cc b/libqpdf/QPDFJob_argv.cc index eadf9a33..737059f0 100644 --- a/libqpdf/QPDFJob_argv.cc +++ b/libqpdf/QPDFJob_argv.cc @@ -408,6 +408,26 @@ ArgParser::argEndCopyAttachment() c_copy_att = nullptr; } +void +ArgParser::argSetPageLabels() +{ + this->ap.selectOptionTable(O_SET_PAGE_LABELS); + accumulated_args.clear(); +} + +void +ArgParser::argPageLabelsPositional(std::string const& arg) +{ + accumulated_args.push_back(arg); +} + +void +ArgParser::argEndSetPageLabels() +{ + c_main->setPageLabels(accumulated_args); + accumulated_args.clear(); +} + void ArgParser::argJobJsonHelp() { diff --git a/libqpdf/QPDFJob_config.cc b/libqpdf/QPDFJob_config.cc index a0dc10f6..9651c3b9 100644 --- a/libqpdf/QPDFJob_config.cc +++ b/libqpdf/QPDFJob_config.cc @@ -1059,6 +1059,17 @@ QPDFJob::Config::encrypt( return std::shared_ptr(new EncConfig(this)); } +QPDFJob::Config* +QPDFJob::Config::setPageLabels(const std::vector& specs) +{ + // XXX validate + for (auto const& xxx: specs) { + std::cout << "XXX config: spec: " << xxx << std::endl; + } + o.m->page_label_specs = specs; + return this; +} + QPDFJob::EncConfig::EncConfig(Config* c) : config(c) { @@ -1213,3 +1224,14 @@ QPDFJob::EncConfig::forceR5() config->o.m->force_R5 = true; return this; } + +QPDFJob::PageLabelsConfig::PageLabelsConfig(Config* c) : + config(c) +{ +} + +QPDFJob::Config* +QPDFJob::PageLabelsConfig::endSetPageLabels() +{ + return this->config; +} diff --git a/libqpdf/QPDFJob_json.cc b/libqpdf/QPDFJob_json.cc index d44652f4..754cb81b 100644 --- a/libqpdf/QPDFJob_json.cc +++ b/libqpdf/QPDFJob_json.cc @@ -66,6 +66,7 @@ namespace std::shared_ptr c_pages; std::shared_ptr c_uo; std::shared_ptr c_enc; + std::vector accumulated_args; }; } // namespace @@ -564,6 +565,26 @@ Handlers::setupUnderlayPassword() addParameter([this](char const* p) { c_uo->password(p); }); } +void +Handlers::setupSetPageLabels() +{ + accumulated_args.clear(); + addParameter([this](char const* p) { accumulated_args.push_back(p); }); +} + +void +Handlers::endSetPageLabelsArray() +{ + c_main->setPageLabels(accumulated_args); + accumulated_args.clear(); +} + +void +Handlers::beginSetPageLabelsArray(JSON) +{ + // nothing needed +} + void QPDFJob::initializeFromJson(std::string const& json, bool partial) { diff --git a/manual/cli.rst b/manual/cli.rst index 1d530641..6495829f 100644 --- a/manual/cli.rst +++ b/manual/cli.rst @@ -1748,6 +1748,116 @@ Related Options Exclude page labels (explicit page numbers) from the output file. Exclude page labels (explicit page numbers) from the output file. + See also :qpdf:ref:`--set-page-labels`. + +.. qpdf:option:: --set-page-labels label-spec ... -- + + .. help: number pages for the entire document + + Set page labels (explicit page numbers) for the entire file. + Each label-spec has the form + + first-page:[type][/start[/prefix]] + + where + + - "first-page" represents a sequential page number using the + same format as page ranges: a number, a number preceded by "r" + to indicate counting from the end, or "z" indicating the last + page + - "type" is one of + - D: Arabic numerals (digits) + - A: Upper-case alphabetic characters + - a: Lower-case alphabetic characters + - R: Upper-case Roman numerals + - r: Lower-case Roman numerals + - omitted: the page number does not appear, though the prefix, + if specified will still appear + - "prefix"` may be any string and is prepended to each page + label + + A given page label spec causes pages to be numbered according to + that scheme starting with first-page and continuing until the + next label spec or the end of the document. If you want to omit + numbering starting at a certain page, you can use first-page: as + the spec. + + Example: "1:r 5:D" would number the first four pages i through + iv, then the remaining pages with Arabic numerals starting with + 1 and continuing sequentially until the end of the document. For + additional examples, please consult the manual. + + Set page labels (explicit page numbers) for the entire file. A PDF + file's pages can be explicitly numbered using page labels. Page + labels in a PDF file have an optional type (Arabic numerals, + upper/lower-case alphabetic characters, upper/lower-case Roman + numerals), an optional prefix, and an optional starting value, + which defaults to 1. A qpdf page label spec has the form + + :samp:`{first-page}:[{type}][/{start}[/{prefix}]]` + + where + + - :samp:`{first-page}` represents a sequential page number using + the same format as page ranges (see :ref:`page-ranges`): a + number, a number preceded by ``r`` to indicate counting from the + end, or ``z`` indicating the last page + + - :samp:`{type}` may be one of + + - ``D``: Arabic numerals (digits) + + - ``A``: Upper-case alphabetic characters + + - ``a``: Lower-case alphabetic characters + + - ``R``: Upper-case Roman numerals + + - ``r``: Lower-case Roman numerals + + - omitted: the page number does not appear, though the prefix, if + specified will still appear + + - :samp:`{prefix}` may be any string and is prepended to each page + label + + A given page label spec causes pages to be numbered according to + that scheme starting with :samp:`{first-page}` and continuing until + the next label spec or the end of the document. If you want to omit + numbering starting at a certain page, you can use + :samp:`{first-page}:` as the spec. + + Here are some example page labeling schemes. First these examples, + assume a 50-page document. + + - ``1:a 5:D`` + + - The first four pages will be numbered ``a`` through ``d``, then + the remaining pages will numbered ``1`` through ``46``. + + - ``1:r 5:D 12: 14:D/10 r5:D//A- z://"end note"``: + + - The first four pages are numbered ``i`` through ``iv`` + + - The 5th page is numbered ``1``, and pages are numbered + sequentially through the 11th page, which will be numbered + ``7`` + + - The 12th and 13th pages will not have labels + + - The 14th page is numbered ``10``. Pages will be numered + sequentially up through the 45th page, which will be numbered + ``41`` + + - Starting with the 46th page (the fifth to last page) and going + to the 49th page, pages will be labeled ``A-1`` through ``A-4`` + + - The 50th page (the last page) will be labeled ``end note``. + + The limitations on the range of formats for page labels are as + specified in Section 12.4.2 of the PDF spec, ISO 32000. + + See also :qpdf:ref:`--remove-page-labels`. .. _encryption-options: diff --git a/qpdf/sizes.cc b/qpdf/sizes.cc index cbec5cd5..65121872 100644 --- a/qpdf/sizes.cc +++ b/qpdf/sizes.cc @@ -101,6 +101,7 @@ main() print_size(QPDFJob::EncConfig); print_size(QPDFJob::PagesConfig); print_size(QPDFJob::UOConfig); + print_size(QPDFJob::PageLabelsConfig); print_size(QPDFLogger); print_size(QPDFMatrix); print_size(QPDFNameTreeObjectHelper);