Commit c1627d04385a02045e7a1a5462f2a632fc529a2e

Authored by Jay Berkenbilt
1 parent fc4c82a9

Add QPDFWriter::setExtraHeaderText

ChangeLog
1 2012-09-06 Jay Berkenbilt <ejb@ql.org> 1 2012-09-06 Jay Berkenbilt <ejb@ql.org>
2 2
  3 + * Add new method QPDFWriter::setExtraHeaderText to add extra text,
  4 + such as application-specific comments, to near the beginning of a
  5 + PDF file. For linearized files, this appears after the
  6 + linearization parameter dictionary. For non-linearized files, it
  7 + appears right after the PDF header and non-ASCII comment.
  8 +
3 * Make it possible to write the same QPDF object with two 9 * Make it possible to write the same QPDF object with two
4 different QPDFWriter objects that have both called 10 different QPDFWriter objects that have both called
5 setLinearization(true) by making private method 11 setLinearization(true) by making private method
include/qpdf/QPDFWriter.hh
@@ -163,6 +163,18 @@ class QPDFWriter @@ -163,6 +163,18 @@ class QPDFWriter
163 QPDF_DLL 163 QPDF_DLL
164 void forcePDFVersion(std::string const&); 164 void forcePDFVersion(std::string const&);
165 165
  166 + // Provide additional text to insert in the PDF file somewhere
  167 + // near the beginning of the file. This can be used to add
  168 + // comments to the beginning of a PDF file, for example, if those
  169 + // comments are to be consumed by some other application. No
  170 + // checks are performed to ensure that the text inserted here is
  171 + // valid PDF. If you want to insert multiline comments, you will
  172 + // need to include \n in the string yourself and start each line
  173 + // with %. An extra newline will be appended if one is not
  174 + // already present at the end of your text.
  175 + QPDF_DLL
  176 + void setExtraHeaderText(std::string const&);
  177 +
166 // Cause a static /ID value to be generated. Use only in test 178 // Cause a static /ID value to be generated. Use only in test
167 // suites. 179 // suites.
168 QPDF_DLL 180 QPDF_DLL
@@ -354,6 +366,7 @@ class QPDFWriter @@ -354,6 +366,7 @@ class QPDFWriter
354 std::string id2; // trailer dictionary 366 std::string id2; // trailer dictionary
355 std::string min_pdf_version; 367 std::string min_pdf_version;
356 std::string forced_pdf_version; 368 std::string forced_pdf_version;
  369 + std::string extra_header_text;
357 int encryption_dict_objid; 370 int encryption_dict_objid;
358 std::string cur_data_key; 371 std::string cur_data_key;
359 std::list<PointerHolder<Pipeline> > to_delete; 372 std::list<PointerHolder<Pipeline> > to_delete;
libqpdf/QPDFWriter.cc
@@ -196,6 +196,22 @@ QPDFWriter::forcePDFVersion(std::string const&amp; version) @@ -196,6 +196,22 @@ QPDFWriter::forcePDFVersion(std::string const&amp; version)
196 } 196 }
197 197
198 void 198 void
  199 +QPDFWriter::setExtraHeaderText(std::string const& text)
  200 +{
  201 + this->extra_header_text = text;
  202 + if ((this->extra_header_text.length() > 0) &&
  203 + (*(this->extra_header_text.rbegin()) != '\n'))
  204 + {
  205 + QTC::TC("qpdf", "QPDFWriter extra header text add newline");
  206 + this->extra_header_text += "\n";
  207 + }
  208 + else
  209 + {
  210 + QTC::TC("qpdf", "QPDFWriter extra header text no newline");
  211 + }
  212 +}
  213 +
  214 +void
199 QPDFWriter::setStaticID(bool val) 215 QPDFWriter::setStaticID(bool val)
200 { 216 {
201 this->static_id = val; 217 this->static_id = val;
@@ -1832,6 +1848,12 @@ QPDFWriter::writeHeader() @@ -1832,6 +1848,12 @@ QPDFWriter::writeHeader()
1832 // it really should be treated as binary. 1848 // it really should be treated as binary.
1833 writeString("\n%\xbf\xf7\xa2\xfe\n"); 1849 writeString("\n%\xbf\xf7\xa2\xfe\n");
1834 writeStringQDF("%QDF-1.0\n\n"); 1850 writeStringQDF("%QDF-1.0\n\n");
  1851 +
  1852 + // Note: do not write extra header text here. Linearized PDFs
  1853 + // must include the entire linearization parameter dictionary
  1854 + // within the first 1024 characters of the PDF file, so for
  1855 + // linearized files, we have to write extra header text after the
  1856 + // linearization parameter dictionary.
1835 } 1857 }
1836 1858
1837 void 1859 void
@@ -2189,7 +2211,9 @@ QPDFWriter::writeLinearized() @@ -2189,7 +2211,9 @@ QPDFWriter::writeLinearized()
2189 // space to write real dictionary. 200 characters is enough 2211 // space to write real dictionary. 200 characters is enough
2190 // space if all numerical values in the parameter dictionary 2212 // space if all numerical values in the parameter dictionary
2191 // that contain offsets are 20 digits long plus a few extra 2213 // that contain offsets are 20 digits long plus a few extra
2192 - // characters for safety. 2214 + // characters for safety. The entire linearization parameter
  2215 + // dictionary must appear within the first 1024 characters of
  2216 + // the file.
2193 2217
2194 qpdf_offset_t pos = this->pipeline->getCount(); 2218 qpdf_offset_t pos = this->pipeline->getCount();
2195 openObject(lindict_id); 2219 openObject(lindict_id);
@@ -2225,6 +2249,10 @@ QPDFWriter::writeLinearized() @@ -2225,6 +2249,10 @@ QPDFWriter::writeLinearized()
2225 writePad(spaces); 2249 writePad(spaces);
2226 writeString("\n"); 2250 writeString("\n");
2227 2251
  2252 + // If the user supplied any additional header text, write it
  2253 + // here after the linearization parameter dictionary.
  2254 + writeString(this->extra_header_text);
  2255 +
2228 // Part 3: first page cross reference table and trailer. 2256 // Part 3: first page cross reference table and trailer.
2229 2257
2230 qpdf_offset_t first_xref_offset = this->pipeline->getCount(); 2258 qpdf_offset_t first_xref_offset = this->pipeline->getCount();
@@ -2396,6 +2424,7 @@ QPDFWriter::writeStandard() @@ -2396,6 +2424,7 @@ QPDFWriter::writeStandard()
2396 // Start writing 2424 // Start writing
2397 2425
2398 writeHeader(); 2426 writeHeader();
  2427 + writeString(this->extra_header_text);
2399 2428
2400 // Put root first on queue. 2429 // Put root first on queue.
2401 QPDFObjectHandle trailer = pdf.getTrailer(); 2430 QPDFObjectHandle trailer = pdf.getTrailer();
qpdf/qpdf.testcov
@@ -240,3 +240,5 @@ QPDFObjectHandle trailing data in parse 0 @@ -240,3 +240,5 @@ QPDFObjectHandle trailing data in parse 0
240 qpdf pages encryption password 0 240 qpdf pages encryption password 0
241 QPDF_Tokenizer EOF reading token 0 241 QPDF_Tokenizer EOF reading token 0
242 QPDF_Tokenizer EOF reading appendable token 0 242 QPDF_Tokenizer EOF reading appendable token 0
  243 +QPDFWriter extra header text no newline 0
  244 +QPDFWriter extra header text add newline 0
qpdf/qtest/qpdf.test
@@ -149,7 +149,7 @@ $td-&gt;runtest(&quot;remove page we don&#39;t have&quot;, @@ -149,7 +149,7 @@ $td-&gt;runtest(&quot;remove page we don&#39;t have&quot;,
149 $td->NORMALIZE_NEWLINES); 149 $td->NORMALIZE_NEWLINES);
150 # ---------- 150 # ----------
151 $td->notify("--- Miscellaneous Tests ---"); 151 $td->notify("--- Miscellaneous Tests ---");
152 -$n_tests += 48; 152 +$n_tests += 53;
153 153
154 $td->runtest("qpdf version", 154 $td->runtest("qpdf version",
155 {$td->COMMAND => "qpdf --version"}, 155 {$td->COMMAND => "qpdf --version"},
@@ -387,6 +387,22 @@ $td-&gt;runtest(&quot;EOF reading token&quot;, @@ -387,6 +387,22 @@ $td-&gt;runtest(&quot;EOF reading token&quot;,
387 {$td->COMMAND => "qpdf --check eof-reading-token.pdf"}, 387 {$td->COMMAND => "qpdf --check eof-reading-token.pdf"},
388 {$td->FILE => "eof-reading-token.out", $td->EXIT_STATUS => 0}, 388 {$td->FILE => "eof-reading-token.out", $td->EXIT_STATUS => 0},
389 $td->NORMALIZE_NEWLINES); 389 $td->NORMALIZE_NEWLINES);
  390 +$td->runtest("extra header text",
  391 + {$td->COMMAND => "test_driver 32 minimal.pdf"},
  392 + {$td->FILE => "test-32.out", $td->EXIT_STATUS => 0},
  393 + $td->NORMALIZE_NEWLINES);
  394 +$td->runtest("check output",
  395 + {$td->FILE => "a.pdf"},
  396 + {$td->FILE => "extra-header-no-newline.pdf"});
  397 +$td->runtest("check output",
  398 + {$td->FILE => "b.pdf"},
  399 + {$td->FILE => "extra-header-lin-no-newline.pdf"});
  400 +$td->runtest("check output",
  401 + {$td->FILE => "c.pdf"},
  402 + {$td->FILE => "extra-header-newline.pdf"});
  403 +$td->runtest("check output",
  404 + {$td->FILE => "d.pdf"},
  405 + {$td->FILE => "extra-header-lin-newline.pdf"});
390 406
391 show_ntests(); 407 show_ntests();
392 # ---------- 408 # ----------
qpdf/qtest/qpdf/extra-header-lin-newline.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/extra-header-lin-no-newline.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/extra-header-newline.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/extra-header-no-newline.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/test-32.out 0 → 100644
  1 +file: a.pdf
  2 +linearized: no
  3 +newline: no
  4 +file: b.pdf
  5 +linearized: yes
  6 +newline: no
  7 +file: c.pdf
  8 +linearized: no
  9 +newline: yes
  10 +file: d.pdf
  11 +linearized: yes
  12 +newline: yes
  13 +test 32 done
qpdf/test_driver.cc
@@ -1091,6 +1091,27 @@ void runtest(int n, char const* filename1, char const* filename2) @@ -1091,6 +1091,27 @@ void runtest(int n, char const* filename1, char const* filename2)
1091 << std::endl; 1091 << std::endl;
1092 } 1092 }
1093 } 1093 }
  1094 + else if (n == 32)
  1095 + {
  1096 + // Extra header text
  1097 + char const* filenames[] = {"a.pdf", "b.pdf", "c.pdf", "d.pdf"};
  1098 + for (int i = 0; i < 4; ++i)
  1099 + {
  1100 + bool linearized = ((i & 1) != 0);
  1101 + bool newline = ((i & 2) != 0);
  1102 + QPDFWriter w(pdf, filenames[i]);
  1103 + w.setStaticID(true);
  1104 + std::cout
  1105 + << "file: " << filenames[i] << std::endl
  1106 + << "linearized: " << (linearized ? "yes" : "no") << std::endl
  1107 + << "newline: " << (newline ? "yes" : "no") << std::endl;
  1108 + w.setLinearization(linearized);
  1109 + w.setExtraHeaderText(newline
  1110 + ? "%% Comment with newline\n"
  1111 + : "%% Comment\n% No newline");
  1112 + w.write();
  1113 + }
  1114 + }
1094 else 1115 else
1095 { 1116 {
1096 throw std::runtime_error(std::string("invalid test ") + 1117 throw std::runtime_error(std::string("invalid test ") +