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);