aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf/QPDFPageDocumentHelper.cc
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2019-01-01 05:08:57 +0100
committerJay Berkenbilt <ejb@ql.org>2019-01-01 14:14:15 +0100
commit3b8ce4f12a75b34d890cb061a721e1a1240cddd1 (patch)
tree8975df54f2d487a6fd0e2bb78e607e8024b0c5b0 /libqpdf/QPDFPageDocumentHelper.cc
parent95d6b17a8904b110c3625e66538368c433f48042 (diff)
downloadqpdf-3b8ce4f12a75b34d890cb061a721e1a1240cddd1.tar.zst
Annotation flattening including form fields
Flatten annotations by integrating their appearance streams into the content stream of the containing page. In the case of form fields, only flatten if /NeedAppearance is false (or equivalently absent). If flattening form fields, also remove /AcroForm from the document catalog.
Diffstat (limited to 'libqpdf/QPDFPageDocumentHelper.cc')
-rw-r--r--libqpdf/QPDFPageDocumentHelper.cc127
1 files changed, 127 insertions, 0 deletions
diff --git a/libqpdf/QPDFPageDocumentHelper.cc b/libqpdf/QPDFPageDocumentHelper.cc
index f4774896..4d117017 100644
--- a/libqpdf/QPDFPageDocumentHelper.cc
+++ b/libqpdf/QPDFPageDocumentHelper.cc
@@ -1,4 +1,6 @@
#include <qpdf/QPDFPageDocumentHelper.hh>
+#include <qpdf/QPDFAcroFormDocumentHelper.hh>
+#include <qpdf/QTC.hh>
QPDFPageDocumentHelper::Members::~Members()
{
@@ -62,3 +64,128 @@ QPDFPageDocumentHelper::removePage(QPDFPageObjectHelper page)
{
this->qpdf.removePage(page.getObjectHandle());
}
+
+
+void
+QPDFPageDocumentHelper::flattenAnnotations()
+{
+ QPDFAcroFormDocumentHelper afdh(this->qpdf);
+ if (afdh.getNeedAppearances())
+ {
+ this->qpdf.getRoot().getKey("/AcroForm").warnIfPossible(
+ "document does not have updated appearance streams,"
+ " so form fields will not be flattened");
+ }
+ pushInheritedAttributesToPage();
+ std::vector<QPDFPageObjectHelper> pages = getAllPages();
+ for (std::vector<QPDFPageObjectHelper>::iterator iter = pages.begin();
+ iter != pages.end(); ++iter)
+ {
+ QPDFPageObjectHelper ph(*iter);
+ QPDFObjectHandle page_oh = ph.getObjectHandle();
+ if (page_oh.getKey("/Resources").isIndirect())
+ {
+ QTC::TC("qpdf", "QPDFPageDocumentHelper indirect resources");
+ page_oh.replaceKey("/Resources",
+ page_oh.getKey("/Resources").shallowCopy());
+ }
+ QPDFObjectHandle resources = ph.getObjectHandle().getKey("/Resources");
+ if (! resources.isDictionary())
+ {
+ // This should never happen and is not exercised in the
+ // test suite
+ resources = QPDFObjectHandle::newDictionary();
+ }
+ flattenAnnotationsForPage(ph, resources, afdh);
+ }
+ if (! afdh.getNeedAppearances())
+ {
+ this->qpdf.getRoot().removeKey("/AcroForm");
+ }
+}
+
+void
+QPDFPageDocumentHelper::flattenAnnotationsForPage(
+ QPDFPageObjectHelper& page,
+ QPDFObjectHandle& resources,
+ QPDFAcroFormDocumentHelper& afdh)
+{
+ bool need_appearances = afdh.getNeedAppearances();
+ std::vector<QPDFAnnotationObjectHelper> annots = page.getAnnotations();
+ std::vector<QPDFObjectHandle> new_annots;
+ std::string new_content;
+ int rotate = 0;
+ QPDFObjectHandle rotate_obj =
+ page.getObjectHandle().getKey("/Rotate");
+ if (rotate_obj.isInteger() && rotate_obj.getIntValue())
+ {
+ rotate = rotate_obj.getIntValue();
+ }
+ for (std::vector<QPDFAnnotationObjectHelper>::iterator iter =
+ annots.begin();
+ iter != annots.end(); ++iter)
+ {
+ QPDFAnnotationObjectHelper& aoh(*iter);
+ QPDFObjectHandle as = aoh.getAppearanceStream("/N");
+ bool is_widget = (aoh.getSubtype() == "/Widget");
+ bool process = true;
+ if (need_appearances && is_widget)
+ {
+ QTC::TC("qpdf", "QPDFPageDocumentHelper skip widget need appearances");
+ process = false;
+ }
+ if (process && (! as.isStream()))
+ {
+ process = false;
+ }
+ if (process)
+ {
+ resources.mergeDictionary(as.getDict().getKey("/Resources"));
+ if (is_widget)
+ {
+ QTC::TC("qpdf", "QPDFPageDocumentHelper merge DR");
+ QPDFFormFieldObjectHelper ff = afdh.getFieldForAnnotation(aoh);
+ resources.mergeDictionary(ff.getInheritableFieldValue("/DR"));
+ }
+ else
+ {
+ QTC::TC("qpdf", "QPDFPageDocumentHelper non-widget annotation");
+ }
+ new_content += aoh.getPageContentForAppearance(rotate);
+ }
+ else
+ {
+ new_annots.push_back(aoh.getObjectHandle());
+ }
+ }
+ if (new_annots.size() != annots.size())
+ {
+ QPDFObjectHandle page_oh = page.getObjectHandle();
+ if (new_annots.empty())
+ {
+ QTC::TC("qpdf", "QPDFPageDocumentHelper remove annots");
+ page_oh.removeKey("/Annots");
+ }
+ else
+ {
+ QPDFObjectHandle old_annots = page_oh.getKey("/Annots");
+ QPDFObjectHandle new_annots_oh =
+ QPDFObjectHandle::newArray(new_annots);
+ if (old_annots.isIndirect())
+ {
+ QTC::TC("qpdf", "QPDFPageDocumentHelper replace indirect annots");
+ this->qpdf.replaceObject(
+ old_annots.getObjGen(), new_annots_oh);
+ }
+ else
+ {
+ QTC::TC("qpdf", "QPDFPageDocumentHelper replace direct annots");
+ page_oh.replaceKey("/Annots", new_annots_oh);
+ }
+ }
+ page.addPageContents(
+ QPDFObjectHandle::newStream(&qpdf, "q\n"), true);
+ page.addPageContents(
+ QPDFObjectHandle::newStream(&qpdf, "\nQ\n" + new_content), false);
+ }
+}