2018-06-18 19:05:53 +00:00
|
|
|
#include <qpdf/QPDFPageDocumentHelper.hh>
|
2022-02-04 21:31:31 +00:00
|
|
|
|
2019-01-01 04:08:57 +00:00
|
|
|
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
|
|
|
|
#include <qpdf/QTC.hh>
|
2022-04-02 21:14:10 +00:00
|
|
|
#include <qpdf/QUtil.hh>
|
2018-06-18 19:05:53 +00:00
|
|
|
|
|
|
|
QPDFPageDocumentHelper::QPDFPageDocumentHelper(QPDF& qpdf) :
|
|
|
|
QPDFDocumentHelper(qpdf)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<QPDFPageObjectHelper>
|
|
|
|
QPDFPageDocumentHelper::getAllPages()
|
|
|
|
{
|
|
|
|
std::vector<QPDFPageObjectHelper> pages;
|
2022-05-21 14:18:15 +00:00
|
|
|
for (auto const& iter: this->qpdf.getAllPages()) {
|
2023-05-27 22:49:18 +00:00
|
|
|
pages.emplace_back(iter);
|
2018-06-18 19:05:53 +00:00
|
|
|
}
|
|
|
|
return pages;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
QPDFPageDocumentHelper::pushInheritedAttributesToPage()
|
|
|
|
{
|
|
|
|
this->qpdf.pushInheritedAttributesToPage();
|
|
|
|
}
|
|
|
|
|
2018-06-22 00:16:05 +00:00
|
|
|
void
|
|
|
|
QPDFPageDocumentHelper::removeUnreferencedResources()
|
|
|
|
{
|
2022-05-21 14:18:15 +00:00
|
|
|
for (auto& ph: getAllPages()) {
|
2022-04-30 17:23:18 +00:00
|
|
|
ph.removeUnreferencedResources();
|
2018-06-22 00:16:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-18 19:05:53 +00:00
|
|
|
void
|
|
|
|
QPDFPageDocumentHelper::addPage(QPDFPageObjectHelper newpage, bool first)
|
|
|
|
{
|
|
|
|
this->qpdf.addPage(newpage.getObjectHandle(), first);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2022-04-02 21:14:10 +00:00
|
|
|
QPDFPageDocumentHelper::addPageAt(
|
|
|
|
QPDFPageObjectHelper newpage, bool before, QPDFPageObjectHelper refpage)
|
2018-06-18 19:05:53 +00:00
|
|
|
{
|
2023-05-21 17:35:09 +00:00
|
|
|
this->qpdf.addPageAt(newpage.getObjectHandle(), before, refpage.getObjectHandle());
|
2018-06-18 19:05:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
QPDFPageDocumentHelper::removePage(QPDFPageObjectHelper page)
|
|
|
|
{
|
|
|
|
this->qpdf.removePage(page.getObjectHandle());
|
|
|
|
}
|
2019-01-01 04:08:57 +00:00
|
|
|
|
|
|
|
void
|
2023-05-21 17:35:09 +00:00
|
|
|
QPDFPageDocumentHelper::flattenAnnotations(int required_flags, int forbidden_flags)
|
2019-01-01 04:08:57 +00:00
|
|
|
{
|
|
|
|
QPDFAcroFormDocumentHelper afdh(this->qpdf);
|
2022-04-02 21:14:10 +00:00
|
|
|
if (afdh.getNeedAppearances()) {
|
|
|
|
this->qpdf.getRoot()
|
|
|
|
.getKey("/AcroForm")
|
2023-05-27 17:19:52 +00:00
|
|
|
.warnIfPossible("document does not have updated appearance streams, so form fields "
|
|
|
|
"will not be flattened");
|
2019-01-01 04:08:57 +00:00
|
|
|
}
|
2022-05-21 14:18:15 +00:00
|
|
|
for (auto& ph: getAllPages()) {
|
2019-01-25 11:55:31 +00:00
|
|
|
QPDFObjectHandle resources = ph.getAttribute("/Resources", true);
|
2022-04-02 21:14:10 +00:00
|
|
|
if (!resources.isDictionary()) {
|
2023-05-21 17:35:09 +00:00
|
|
|
QTC::TC("qpdf", "QPDFPageDocumentHelper flatten resources missing or invalid");
|
2023-01-09 12:31:39 +00:00
|
|
|
resources = ph.getObjectHandle().replaceKeyAndGetNew(
|
|
|
|
"/Resources", QPDFObjectHandle::newDictionary());
|
2019-01-01 04:08:57 +00:00
|
|
|
}
|
2023-05-21 17:35:09 +00:00
|
|
|
flattenAnnotationsForPage(ph, resources, afdh, required_flags, forbidden_flags);
|
2019-01-01 04:08:57 +00:00
|
|
|
}
|
2022-04-02 21:14:10 +00:00
|
|
|
if (!afdh.getNeedAppearances()) {
|
2019-01-01 04:08:57 +00:00
|
|
|
this->qpdf.getRoot().removeKey("/AcroForm");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
QPDFPageDocumentHelper::flattenAnnotationsForPage(
|
|
|
|
QPDFPageObjectHelper& page,
|
|
|
|
QPDFObjectHandle& resources,
|
2019-01-03 16:51:58 +00:00
|
|
|
QPDFAcroFormDocumentHelper& afdh,
|
|
|
|
int required_flags,
|
|
|
|
int forbidden_flags)
|
2019-01-01 04:08:57 +00:00
|
|
|
{
|
|
|
|
bool need_appearances = afdh.getNeedAppearances();
|
|
|
|
std::vector<QPDFAnnotationObjectHelper> annots = page.getAnnotations();
|
|
|
|
std::vector<QPDFObjectHandle> new_annots;
|
|
|
|
std::string new_content;
|
|
|
|
int rotate = 0;
|
2022-04-02 21:14:10 +00:00
|
|
|
QPDFObjectHandle rotate_obj = page.getObjectHandle().getKey("/Rotate");
|
|
|
|
if (rotate_obj.isInteger() && rotate_obj.getIntValue()) {
|
2019-06-21 03:35:23 +00:00
|
|
|
rotate = rotate_obj.getIntValueAsInt();
|
2019-01-01 04:08:57 +00:00
|
|
|
}
|
2019-01-03 02:44:10 +00:00
|
|
|
int next_fx = 1;
|
2022-04-30 17:23:18 +00:00
|
|
|
for (auto& aoh: annots) {
|
2019-01-01 04:08:57 +00:00
|
|
|
QPDFObjectHandle as = aoh.getAppearanceStream("/N");
|
|
|
|
bool is_widget = (aoh.getSubtype() == "/Widget");
|
|
|
|
bool process = true;
|
2022-04-02 21:14:10 +00:00
|
|
|
if (need_appearances && is_widget) {
|
2023-05-21 17:35:09 +00:00
|
|
|
QTC::TC("qpdf", "QPDFPageDocumentHelper skip widget need appearances");
|
2019-01-01 04:08:57 +00:00
|
|
|
process = false;
|
|
|
|
}
|
2022-04-02 21:14:10 +00:00
|
|
|
if (process && as.isStream()) {
|
|
|
|
if (is_widget) {
|
2019-01-01 04:08:57 +00:00
|
|
|
QTC::TC("qpdf", "QPDFPageDocumentHelper merge DR");
|
|
|
|
QPDFFormFieldObjectHelper ff = afdh.getFieldForAnnotation(aoh);
|
2023-05-21 17:35:09 +00:00
|
|
|
QPDFObjectHandle as_resources = as.getDict().getKey("/Resources");
|
2022-04-02 21:14:10 +00:00
|
|
|
if (as_resources.isIndirect()) {
|
2023-05-21 17:35:09 +00:00
|
|
|
QTC::TC("qpdf", "QPDFPageDocumentHelper indirect as resources");
|
|
|
|
as.getDict().replaceKey("/Resources", as_resources.shallowCopy());
|
2019-01-03 02:44:10 +00:00
|
|
|
as_resources = as.getDict().getKey("/Resources");
|
|
|
|
}
|
2021-02-26 19:47:52 +00:00
|
|
|
as_resources.mergeResources(ff.getDefaultResources());
|
2022-04-02 21:14:10 +00:00
|
|
|
} else {
|
2019-01-01 04:08:57 +00:00
|
|
|
QTC::TC("qpdf", "QPDFPageDocumentHelper non-widget annotation");
|
|
|
|
}
|
2022-04-02 21:14:10 +00:00
|
|
|
std::string name = resources.getUniqueResourceName("/Fxo", next_fx);
|
2023-05-21 17:35:09 +00:00
|
|
|
std::string content =
|
|
|
|
aoh.getPageContentForAppearance(name, rotate, required_flags, forbidden_flags);
|
2022-04-02 21:14:10 +00:00
|
|
|
if (!content.empty()) {
|
2022-02-05 14:18:58 +00:00
|
|
|
resources.mergeResources("<< /XObject << >> >>"_qpdf);
|
2019-01-03 16:51:58 +00:00
|
|
|
resources.getKey("/XObject").replaceKey(name, as);
|
|
|
|
++next_fx;
|
|
|
|
}
|
|
|
|
new_content += content;
|
2022-04-02 21:14:10 +00:00
|
|
|
} else if (process) {
|
2023-05-27 17:19:52 +00:00
|
|
|
// If an annotation has no appearance stream, just drop the annotation when flattening.
|
|
|
|
// This can happen for unchecked checkboxes and radio buttons, popup windows associated
|
|
|
|
// with comments that aren't visible, and other types of annotations that aren't
|
|
|
|
// visible.
|
2023-05-21 17:35:09 +00:00
|
|
|
QTC::TC("qpdf", "QPDFPageDocumentHelper ignore annotation with no appearance");
|
2022-04-02 21:14:10 +00:00
|
|
|
} else {
|
2019-01-01 04:08:57 +00:00
|
|
|
new_annots.push_back(aoh.getObjectHandle());
|
|
|
|
}
|
|
|
|
}
|
2022-04-02 21:14:10 +00:00
|
|
|
if (new_annots.size() != annots.size()) {
|
2019-01-01 04:08:57 +00:00
|
|
|
QPDFObjectHandle page_oh = page.getObjectHandle();
|
2022-04-02 21:14:10 +00:00
|
|
|
if (new_annots.empty()) {
|
2019-01-01 04:08:57 +00:00
|
|
|
QTC::TC("qpdf", "QPDFPageDocumentHelper remove annots");
|
|
|
|
page_oh.removeKey("/Annots");
|
2022-04-02 21:14:10 +00:00
|
|
|
} else {
|
2019-01-01 04:08:57 +00:00
|
|
|
QPDFObjectHandle old_annots = page_oh.getKey("/Annots");
|
2023-05-21 17:35:09 +00:00
|
|
|
QPDFObjectHandle new_annots_oh = QPDFObjectHandle::newArray(new_annots);
|
2022-04-02 21:14:10 +00:00
|
|
|
if (old_annots.isIndirect()) {
|
2023-05-21 17:35:09 +00:00
|
|
|
QTC::TC("qpdf", "QPDFPageDocumentHelper replace indirect annots");
|
2022-04-02 21:14:10 +00:00
|
|
|
this->qpdf.replaceObject(old_annots.getObjGen(), new_annots_oh);
|
|
|
|
} else {
|
2019-01-01 04:08:57 +00:00
|
|
|
QTC::TC("qpdf", "QPDFPageDocumentHelper replace direct annots");
|
|
|
|
page_oh.replaceKey("/Annots", new_annots_oh);
|
|
|
|
}
|
|
|
|
}
|
2022-09-26 19:11:27 +00:00
|
|
|
page.addPageContents(qpdf.newStream("q\n"), true);
|
|
|
|
page.addPageContents(qpdf.newStream("\nQ\n" + new_content), false);
|
2019-01-01 04:08:57 +00:00
|
|
|
}
|
|
|
|
}
|