summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--TODO13
-rw-r--r--include/qpdf/QPDFObjectHandle.hh18
-rw-r--r--libqpdf/QPDFObjectHandle.cc29
-rw-r--r--qpdf/qpdf.testcov2
-rw-r--r--qpdf/qtest/qpdf.test52
-rw-r--r--qpdf/qtest/qpdf/add-contents.pdf54
-rw-r--r--qpdf/test_driver.cc18
8 files changed, 161 insertions, 32 deletions
diff --git a/ChangeLog b/ChangeLog
index 1caf6ed7..1592daaf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2010-08-05 Jay Berkenbilt <ejb@ql.org>
+
+ * Add new methods to QPDFObjectHandle: replaceStreamData and
+ newStream. These methods allow users of the qpdf library to add
+ new streams and to replace data of existing streams. Examples are
+ provided.
+
2010-06-06 Jay Berkenbilt <ejb@ql.org>
* Fix memory leak for QPDF objects whose underlying PDF objects
diff --git a/TODO b/TODO
index 403bc979..55980081 100644
--- a/TODO
+++ b/TODO
@@ -7,20 +7,19 @@ Next
in August, 2009. He seems to like to send encrypted mail (key
01FCC336). Tell him about newStream and replaceStreamData.
+ * Tell stronghorse@tom.com about QPDFObjectHandle::addPageContents.
+ See message from stronghorse@tom.com ("Suggestion for qpdf") from
+ 2010-06-09 and my response.
+
2.2
===
- * Add helper routines for manipulating page content streams.
- Operations should include ability to convert page contents from a
- stream to an array of streams and to append or prepend to the page
- contents. See message from stronghorse@tom.com ("Suggestion for
- qpdf") from 2010-06-09 and my response. Consider providing an
- example program that adds the line he suggests.
-
* Create an example that does some kind of manipulation on every
image. Use QPDF::getAllPages and QPDFObjectHandle::getPageImages
along with new stream data and dictionary manipulation.
+ * Add example program for addPageContents using suggestion from
+ stronghorse@tom.com's message above.
General
=======
diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh
index 298e480b..b7df3750 100644
--- a/include/qpdf/QPDFObjectHandle.hh
+++ b/include/qpdf/QPDFObjectHandle.hh
@@ -288,14 +288,22 @@ class QPDFObjectHandle
QPDF_DLL
std::map<std::string, QPDFObjectHandle> getPageImages();
- // Throws an exception if this is not a Page object. Returns a
- // vector of stream objects representing the content streams for
- // the given page. This routine allows the caller to not care
- // whether there are one or more than one content streams for a
- // page.
+ // Returns a vector of stream objects representing the content
+ // streams for the given page. This routine allows the caller to
+ // not care whether there are one or more than one content streams
+ // for a page. Throws an exception if this is not a Page object.
QPDF_DLL
std::vector<QPDFObjectHandle> getPageContents();
+ // Add the given object as a new content stream for this page. If
+ // parameter 'first' is true, add to the beginning. Otherwise,
+ // add to the end. This routine automatically converts the page
+ // contents to an array if it is a scalar, allowing the caller not
+ // to care what the initial structure is. Throws an exception if
+ // this is not a Page object.
+ QPDF_DLL
+ void addPageContents(QPDFObjectHandle contents, bool first);
+
// Initializers for objects. This Factory class gives the QPDF
// class specific permission to call factory methods without
// making it a friend of the whole QPDFObjectHandle class.
diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc
index 19b4f94e..143a3f42 100644
--- a/libqpdf/QPDFObjectHandle.cc
+++ b/libqpdf/QPDFObjectHandle.cc
@@ -472,6 +472,35 @@ QPDFObjectHandle::getPageContents()
return result;
}
+void
+QPDFObjectHandle::addPageContents(QPDFObjectHandle new_contents, bool first)
+{
+ assertPageObject();
+ new_contents.assertType("Stream", new_contents.isStream());
+
+ std::vector<QPDFObjectHandle> orig_contents = getPageContents();
+
+ std::vector<QPDFObjectHandle> content_streams;
+ if (first)
+ {
+ QTC::TC("qpdf", "QPDFObjectHandle prepend page contents");
+ content_streams.push_back(new_contents);
+ }
+ for (std::vector<QPDFObjectHandle>::iterator iter = orig_contents.begin();
+ iter != orig_contents.end(); ++iter)
+ {
+ QTC::TC("qpdf", "QPDFObjectHandle append page contents");
+ content_streams.push_back(*iter);
+ }
+ if (! first)
+ {
+ content_streams.push_back(new_contents);
+ }
+
+ QPDFObjectHandle contents = QPDFObjectHandle::newArray(content_streams);
+ this->replaceKey("/Contents", contents);
+}
+
std::string
QPDFObjectHandle::unparse()
{
diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov
index 49b03c42..4a868227 100644
--- a/qpdf/qpdf.testcov
+++ b/qpdf/qpdf.testcov
@@ -181,3 +181,5 @@ QPDF_Stream provider length mismatch 0
QPDFObjectHandle newStream 0
QPDFObjectHandle newStream with data 0
QPDF_Stream pipe no stream data 0
+QPDFObjectHandle prepend page contents 0
+QPDFObjectHandle append page contents 0
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index d40822cd..7d14722e 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -76,26 +76,8 @@ flush_tiff_cache();
show_ntests();
# ----------
-$td->notify("--- Miscellaneous Tests ---");
-$n_tests += 28;
-
-$td->runtest("qpdf version",
- {$td->COMMAND => "qpdf --version"},
- {$td->REGEXP => "qpdf version \\S+\n.*", $td->EXIT_STATUS => 0},
- $td->NORMALIZE_NEWLINES);
-$td->runtest("C API: qpdf version",
- {$td->COMMAND => "qpdf-ctest --version"},
- {$td->REGEXP => "qpdf-ctest version \\S+\n",
- $td->EXIT_STATUS => 0},
- $td->NORMALIZE_NEWLINES);
-
-foreach (my $i = 1; $i <= 3; ++$i)
-{
- $td->runtest("misc tests",
- {$td->COMMAND => "test_driver 5 misc-$i.pdf"},
- {$td->FILE => "misc-$i.out", $td->EXIT_STATUS => 0},
- $td->NORMALIZE_NEWLINES);
-}
+$td->notify("--- Stream Replacement Tests ---");
+$n_tests += 8;
$td->runtest("replace stream data",
{$td->COMMAND => "test_driver 7 qstream.pdf"},
@@ -118,6 +100,36 @@ $td->runtest("new streams",
$td->runtest("new stream",
{$td->FILE => "a.pdf"},
{$td->FILE => "new-streams.pdf"});
+$td->runtest("add page contents",
+ {$td->COMMAND => "test_driver 10 minimal.pdf"},
+ {$td->STRING => "test 10 done\n", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("new stream",
+ {$td->FILE => "a.pdf"},
+ {$td->FILE => "add-contents.pdf"});
+
+show_ntests();
+# ----------
+$td->notify("--- Miscellaneous Tests ---");
+$n_tests += 22;
+
+$td->runtest("qpdf version",
+ {$td->COMMAND => "qpdf --version"},
+ {$td->REGEXP => "qpdf version \\S+\n.*", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("C API: qpdf version",
+ {$td->COMMAND => "qpdf-ctest --version"},
+ {$td->REGEXP => "qpdf-ctest version \\S+\n",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+foreach (my $i = 1; $i <= 3; ++$i)
+{
+ $td->runtest("misc tests",
+ {$td->COMMAND => "test_driver 5 misc-$i.pdf"},
+ {$td->FILE => "misc-$i.out", $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+}
# Make sure we ignore decode parameters that we don't understand
$td->runtest("unknown decode parameters",
diff --git a/qpdf/qtest/qpdf/add-contents.pdf b/qpdf/qtest/qpdf/add-contents.pdf
new file mode 100644
index 00000000..06b683ac
--- /dev/null
+++ b/qpdf/qtest/qpdf/add-contents.pdf
@@ -0,0 +1,54 @@
+%PDF-1.3
+%¿÷¢þ
+1 0 obj
+<< /Pages 2 0 R /Type /Catalog >>
+endobj
+2 0 obj
+<< /Count 1 /Kids [ 3 0 R ] /Type /Pages >>
+endobj
+3 0 obj
+<< /Contents [ 4 0 R 5 0 R 6 0 R ] /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 7 0 R >> /ProcSet 8 0 R >> /Type /Page >>
+endobj
+4 0 obj
+<< /Length 37 >>
+stream
+BT /F1 12 Tf 72 620 Td (Baked) Tj ET
+endstream
+endobj
+5 0 obj
+<< /Length 44 >>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+6 0 obj
+<< /Length 38 >>
+stream
+BT /F1 18 Tf 72 520 Td (Mashed) Tj ET
+endstream
+endobj
+7 0 obj
+<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >>
+endobj
+8 0 obj
+[ /PDF /Text ]
+endobj
+xref
+0 9
+0000000000 65535 f
+0000000015 00000 n
+0000000064 00000 n
+0000000123 00000 n
+0000000282 00000 n
+0000000368 00000 n
+0000000461 00000 n
+0000000548 00000 n
+0000000655 00000 n
+trailer << /Root 1 0 R /Size 9 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >>
+startxref
+685
+%%EOF
diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc
index 09458227..710f4c6e 100644
--- a/qpdf/test_driver.cc
+++ b/qpdf/test_driver.cc
@@ -427,6 +427,24 @@ void runtest(int n, char const* filename)
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
+ else if (n == 10)
+ {
+ PointerHolder<Buffer> b1 = new Buffer(37);
+ unsigned char* bp = b1.getPointer()->getBuffer();
+ memcpy(bp, (char*)"BT /F1 12 Tf 72 620 Td (Baked) Tj ET\n", 37);
+ PointerHolder<Buffer> b2 = new Buffer(38);
+ bp = b2.getPointer()->getBuffer();
+ memcpy(bp, (char*)"BT /F1 18 Tf 72 520 Td (Mashed) Tj ET\n", 38);
+
+ std::vector<QPDFObjectHandle> pages = pdf.getAllPages();
+ pages[0].addPageContents(QPDFObjectHandle::newStream(&pdf, b1), true);
+ pages[0].addPageContents(QPDFObjectHandle::newStream(&pdf, b2), false);
+
+ QPDFWriter w(pdf, "a.pdf");
+ w.setStaticID(true);
+ w.setStreamDataMode(qpdf_s_preserve);
+ w.write();
+ }
else
{
throw std::runtime_error(std::string("invalid test ") +