From c1627d04385a02045e7a1a5462f2a632fc529a2e Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Thu, 6 Sep 2012 15:30:13 -0400 Subject: [PATCH] Add QPDFWriter::setExtraHeaderText --- ChangeLog | 6 ++++ include/qpdf/QPDFWriter.hh | 13 ++++++++ libqpdf/QPDFWriter.cc | 31 +++++++++++++++++- qpdf/qpdf.testcov | 2 ++ qpdf/qtest/qpdf.test | 18 +++++++++- qpdf/qtest/qpdf/extra-header-lin-newline.pdf | Bin 0 -> 1334 bytes .../qpdf/extra-header-lin-no-newline.pdf | Bin 0 -> 1334 bytes qpdf/qtest/qpdf/extra-header-newline.pdf | Bin 0 -> 823 bytes qpdf/qtest/qpdf/extra-header-no-newline.pdf | Bin 0 -> 823 bytes qpdf/qtest/qpdf/test-32.out | 13 ++++++++ qpdf/test_driver.cc | 21 ++++++++++++ 11 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 qpdf/qtest/qpdf/extra-header-lin-newline.pdf create mode 100644 qpdf/qtest/qpdf/extra-header-lin-no-newline.pdf create mode 100644 qpdf/qtest/qpdf/extra-header-newline.pdf create mode 100644 qpdf/qtest/qpdf/extra-header-no-newline.pdf create mode 100644 qpdf/qtest/qpdf/test-32.out diff --git a/ChangeLog b/ChangeLog index ad86b396..ce6018dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2012-09-06 Jay Berkenbilt + * Add new method QPDFWriter::setExtraHeaderText to add extra text, + such as application-specific comments, to near the beginning of a + PDF file. For linearized files, this appears after the + linearization parameter dictionary. For non-linearized files, it + appears right after the PDF header and non-ASCII comment. + * Make it possible to write the same QPDF object with two different QPDFWriter objects that have both called setLinearization(true) by making private method diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh index 5ee90262..2c1c32f6 100644 --- a/include/qpdf/QPDFWriter.hh +++ b/include/qpdf/QPDFWriter.hh @@ -163,6 +163,18 @@ class QPDFWriter QPDF_DLL void forcePDFVersion(std::string const&); + // Provide additional text to insert in the PDF file somewhere + // near the beginning of the file. This can be used to add + // comments to the beginning of a PDF file, for example, if those + // comments are to be consumed by some other application. No + // checks are performed to ensure that the text inserted here is + // valid PDF. If you want to insert multiline comments, you will + // need to include \n in the string yourself and start each line + // with %. An extra newline will be appended if one is not + // already present at the end of your text. + QPDF_DLL + void setExtraHeaderText(std::string const&); + // Cause a static /ID value to be generated. Use only in test // suites. QPDF_DLL @@ -354,6 +366,7 @@ class QPDFWriter std::string id2; // trailer dictionary std::string min_pdf_version; std::string forced_pdf_version; + std::string extra_header_text; int encryption_dict_objid; std::string cur_data_key; std::list > to_delete; diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index 19ab1359..eb08488a 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -195,6 +195,22 @@ QPDFWriter::forcePDFVersion(std::string const& version) this->forced_pdf_version = version; } +void +QPDFWriter::setExtraHeaderText(std::string const& text) +{ + this->extra_header_text = text; + if ((this->extra_header_text.length() > 0) && + (*(this->extra_header_text.rbegin()) != '\n')) + { + QTC::TC("qpdf", "QPDFWriter extra header text add newline"); + this->extra_header_text += "\n"; + } + else + { + QTC::TC("qpdf", "QPDFWriter extra header text no newline"); + } +} + void QPDFWriter::setStaticID(bool val) { @@ -1832,6 +1848,12 @@ QPDFWriter::writeHeader() // it really should be treated as binary. writeString("\n%\xbf\xf7\xa2\xfe\n"); writeStringQDF("%QDF-1.0\n\n"); + + // Note: do not write extra header text here. Linearized PDFs + // must include the entire linearization parameter dictionary + // within the first 1024 characters of the PDF file, so for + // linearized files, we have to write extra header text after the + // linearization parameter dictionary. } void @@ -2189,7 +2211,9 @@ QPDFWriter::writeLinearized() // space to write real dictionary. 200 characters is enough // space if all numerical values in the parameter dictionary // that contain offsets are 20 digits long plus a few extra - // characters for safety. + // characters for safety. The entire linearization parameter + // dictionary must appear within the first 1024 characters of + // the file. qpdf_offset_t pos = this->pipeline->getCount(); openObject(lindict_id); @@ -2225,6 +2249,10 @@ QPDFWriter::writeLinearized() writePad(spaces); writeString("\n"); + // If the user supplied any additional header text, write it + // here after the linearization parameter dictionary. + writeString(this->extra_header_text); + // Part 3: first page cross reference table and trailer. qpdf_offset_t first_xref_offset = this->pipeline->getCount(); @@ -2396,6 +2424,7 @@ QPDFWriter::writeStandard() // Start writing writeHeader(); + writeString(this->extra_header_text); // Put root first on queue. QPDFObjectHandle trailer = pdf.getTrailer(); diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index 0e062af4..f9663375 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -240,3 +240,5 @@ QPDFObjectHandle trailing data in parse 0 qpdf pages encryption password 0 QPDF_Tokenizer EOF reading token 0 QPDF_Tokenizer EOF reading appendable token 0 +QPDFWriter extra header text no newline 0 +QPDFWriter extra header text add newline 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index b2ee28eb..d12f0644 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -149,7 +149,7 @@ $td->runtest("remove page we don't have", $td->NORMALIZE_NEWLINES); # ---------- $td->notify("--- Miscellaneous Tests ---"); -$n_tests += 48; +$n_tests += 53; $td->runtest("qpdf version", {$td->COMMAND => "qpdf --version"}, @@ -387,6 +387,22 @@ $td->runtest("EOF reading token", {$td->COMMAND => "qpdf --check eof-reading-token.pdf"}, {$td->FILE => "eof-reading-token.out", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); +$td->runtest("extra header text", + {$td->COMMAND => "test_driver 32 minimal.pdf"}, + {$td->FILE => "test-32.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +$td->runtest("check output", + {$td->FILE => "a.pdf"}, + {$td->FILE => "extra-header-no-newline.pdf"}); +$td->runtest("check output", + {$td->FILE => "b.pdf"}, + {$td->FILE => "extra-header-lin-no-newline.pdf"}); +$td->runtest("check output", + {$td->FILE => "c.pdf"}, + {$td->FILE => "extra-header-newline.pdf"}); +$td->runtest("check output", + {$td->FILE => "d.pdf"}, + {$td->FILE => "extra-header-lin-newline.pdf"}); show_ntests(); # ---------- diff --git a/qpdf/qtest/qpdf/extra-header-lin-newline.pdf b/qpdf/qtest/qpdf/extra-header-lin-newline.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7a5ff3e660096b540e88814b5a06fdf1ea4c2f59 GIT binary patch literal 1334 zcmc&!J#Q015H*QF;UsB%eSmj=G4)7^GqkQF?|sUep2o1~KF@$F}EV2>JJO9;I_J z-S(;Cz?XJRttQCFm(Sg$3C1gr%$Aax`kkk@J|4v|&YpJ$S+S73XYQJXq} zHq%)&7!k=6Sqpn0)B!;rjP8YtN(>HfFnr}}Fg{|j{@6}{wQr{WR7>YQlT25k#x@X{yI(;PC z5no9LIJhQ0E=BnROAT+8r)ogPh-xBB`e_RxDjh=1GJ2ul>qMoJQX*;9g#>RB8S$W~ znIS<{&RNTm`k3<%d}!9k@H6A0R{eCu8m6_iw7z)q@x}4*-i@1=ho6^*Wc}9Jm&VfB z*Yw)OLgD<<)!`foswq`<8~dz;=tMD)u#?+RfyjGW;hE3?$(raM3K_LHx;RFGqPPR( zaTMQ=vuGOF;5}3>M0eNwyHYE!a4Wa#jm|Z7ydpF&RZjYvV)7trXX@X9-)f5dDRJ2h lbEDw{EBB&ujqBiC!(Q%B{jZt$Y1TX1FBbeOPgr*DieG4bR>=SW literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/extra-header-lin-no-newline.pdf b/qpdf/qtest/qpdf/extra-header-lin-no-newline.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e115c46218cf923edddc935e7d0fd216bb7330fd GIT binary patch literal 1334 zcmc&!J#Q015H*QF;Us%pgFcKAFueqJuotgLEyerl!;ca?{8O3)$UjH;K zFd^C5GnSWuJd9$&)99J#0L2|p#%v&Kumw)QfKnf}foy;SWEFR20Hmr8HgV&*P%0TB z?xb!&K}dO3yGa7Ii?sK@54!vF-U7LjL`nN9kP5 zcjoT~9@47Tv<5ZJnV3v@)DtNv*6T?k0c$}WNCU|SLeeC8t;k>CDy$)pY97F zWiELy>CUK)@Fy3-s3#E?342_MifAVtywU*X;y8)8E1!T($7NH&`^NB9yVYv(R?FxV z7Cw}&_(#Vtub+J1KUw&8M&I16w%(o;3Y*Ut->b>Wq`q@8V<_UJYB`A|>Yjl+?(~u9 zM0_O~;^3P2xD@3NEH%6}p5nnAQC(!oAZ;T=r9+5W#xE3nov2h&N+j)ukl;-sV;&SW zGbE_WIcqsmA9MbJ56$`o{LDD7*FGJxrfF?1tuG#bd~r0ocjM;e(dVTRS-*AqrMYza zHNAGeP&m7Ebu>qUYD!hz!age@I#CQIZ09yqAo8A8cqTMJvL<>5LPl+lE{;*4DDDDz z9L4wJESd(YypPI-=_DklR?F?kSmGWGAkZ*@ifl(=k$ lx$*FUm3vXS#&vM6VK?`u{?|QN6G?wVY{l{t5>hu&3-p~;60?I`Ss7|zoglOj#@0Z)UXoz zHAue7h2+Eo7u4w+(rSUzXLDYHkC6d5qxWw)I31>0uIA8g6E0__(4;FsM=g=h1LujD zl`sZsp_2-{<*T74T|=ctF(m}scF*`sup_lb78mcx^C3=sK(0P3jOv}N0Wa0En4(Uj zI8&IhFCK)JsNF_Hp{7HwA=*c_!x%kRCNZ5WIZwHqqboszoFdn}z-P`h@A9ddSx~79 z&K6|-sU(+!{*M(Mx&F&W@8*~9A3tm!-Mx2f^RuyWdiO7WjT#rfi#wM$u6?_HdxNg+ zkURg3%&O}VE4dxPIpO&`uEmsrb1bo?kaKWe3Hex-q7FP~3*1SDh7Xsg+AhdUhTq!( zEm!TgncuV;qb1!8i}+f@q*~;<;Ag~zn7I23qA;W(oWVXuQ?OZ(bu38^VqOp*#QYQ& zX|5~Sz&*r<9$He>>4J$I-8VZOC}j?pJ&i+g&LIY;*M)J5dVv?FeuP?+G)}2clOXb= efYOBeaT>P&UokP~pwz6;)`l<$@eetEk&%DLCgJA* literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/extra-header-no-newline.pdf b/qpdf/qtest/qpdf/extra-header-no-newline.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1df11d302ac1e9e84243250ffec379d49c33d1e0 GIT binary patch literal 823 zcmah|OODe(5alDJ&=PkjB)kLsX@8|Cf=R|A0)~)8Vi9H0lk|i}PWQ;}Fo6{p;0CNX z0vBML5kg`G>){gY5Y?S=JW>|e3)`>iRlR!Eb_Tutf&Y*a=j)%(e~IHjS1p%ZYU03= z0?E$`Avy8E19kj{wA)ka-?r>orspZm3eDm$C? za<(MvPZhZs4t|``u@}5-^>2Rp{_(@+(cOEuHa}Y%w}1cq*SK~5yS#I8Rk8 zCG*lhBeU*$z$$J>aG&tvE!Sekz&(`MQpg3kuY`OoE71T)Y>7L`(eTmgSlb1e$?$tS zpsm&TZRR(vCTK}7$0WYiFs&E4F8K-ZAR*rFf;ft31Shb=Xa+V4vcb}H5A(xh4+}C} zq`9teLvNoO`DjUcBsFXQe_B4*fdkzV>{T@u()DQhA3u4roW=Tc^nuc)@ fhm@u?NV2H&|BR_Q2bE@}wl+jzgn!84i=6xeKjPsR literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/test-32.out b/qpdf/qtest/qpdf/test-32.out new file mode 100644 index 00000000..ff8d922b --- /dev/null +++ b/qpdf/qtest/qpdf/test-32.out @@ -0,0 +1,13 @@ +file: a.pdf +linearized: no +newline: no +file: b.pdf +linearized: yes +newline: no +file: c.pdf +linearized: no +newline: yes +file: d.pdf +linearized: yes +newline: yes +test 32 done diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index b277cdf1..311097f6 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -1091,6 +1091,27 @@ void runtest(int n, char const* filename1, char const* filename2) << std::endl; } } + else if (n == 32) + { + // Extra header text + char const* filenames[] = {"a.pdf", "b.pdf", "c.pdf", "d.pdf"}; + for (int i = 0; i < 4; ++i) + { + bool linearized = ((i & 1) != 0); + bool newline = ((i & 2) != 0); + QPDFWriter w(pdf, filenames[i]); + w.setStaticID(true); + std::cout + << "file: " << filenames[i] << std::endl + << "linearized: " << (linearized ? "yes" : "no") << std::endl + << "newline: " << (newline ? "yes" : "no") << std::endl; + w.setLinearization(linearized); + w.setExtraHeaderText(newline + ? "%% Comment with newline\n" + : "%% Comment\n% No newline"); + w.write(); + } + } else { throw std::runtime_error(std::string("invalid test ") +