#include #include #include QPDFFormFieldObjectHelper::Members::~Members() { } QPDFFormFieldObjectHelper::Members::Members() { } QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper(QPDFObjectHandle oh) : QPDFObjectHelper(oh), m(new Members()) { } QPDFFormFieldObjectHelper::QPDFFormFieldObjectHelper() : QPDFObjectHelper(QPDFObjectHandle::newNull()), m(new Members()) { } bool QPDFFormFieldObjectHelper::isNull() { return this->oh.isNull(); } QPDFFormFieldObjectHelper QPDFFormFieldObjectHelper::getParent() { return this->oh.getKey("/Parent"); // may be null } QPDFObjectHandle QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name) { QPDFObjectHandle node = this->oh; QPDFObjectHandle result(node.getKey(name)); std::set seen; while (result.isNull() && node.hasKey("/Parent")) { seen.insert(node.getObjGen()); node = node.getKey("/Parent"); if (seen.count(node.getObjGen())) { break; } result = node.getKey(name); if (! result.isNull()) { QTC::TC("qpdf", "QPDFFormFieldObjectHelper non-trivial inheritance"); } } return result; } std::string QPDFFormFieldObjectHelper::getInheritableFieldValueAsString( std::string const& name) { QPDFObjectHandle fv = getInheritableFieldValue(name); std::string result; if (fv.isString()) { result = fv.getUTF8Value(); } return result; } std::string QPDFFormFieldObjectHelper::getInheritableFieldValueAsName( std::string const& name) { QPDFObjectHandle fv = getInheritableFieldValue(name); std::string result; if (fv.isName()) { result = fv.getName(); } return result; } std::string QPDFFormFieldObjectHelper::getFieldType() { return getInheritableFieldValueAsName("/FT"); } std::string QPDFFormFieldObjectHelper::getFullyQualifiedName() { std::string result; QPDFObjectHandle node = this->oh; std::set seen; while ((! node.isNull()) && (seen.count(node.getObjGen()) == 0)) { if (node.getKey("/T").isString()) { if (! result.empty()) { QTC::TC("qpdf", "QPDFFormFieldObjectHelper non-trivial qualified name"); result = "." + result; } result = node.getKey("/T").getUTF8Value() + result; } seen.insert(node.getObjGen()); node = node.getKey("/Parent"); } return result; } std::string QPDFFormFieldObjectHelper::getPartialName() { std::string result; if (this->oh.getKey("/T").isString()) { result = this->oh.getKey("/T").getUTF8Value(); } return result; } std::string QPDFFormFieldObjectHelper::getAlternativeName() { if (this->oh.getKey("/TU").isString()) { QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU present"); return this->oh.getKey("/TU").getUTF8Value(); } QTC::TC("qpdf", "QPDFFormFieldObjectHelper TU absent"); return getFullyQualifiedName(); } std::string QPDFFormFieldObjectHelper::getMappingName() { if (this->oh.getKey("/TM").isString()) { QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM present"); return this->oh.getKey("/TM").getUTF8Value(); } QTC::TC("qpdf", "QPDFFormFieldObjectHelper TM absent"); return getAlternativeName(); } QPDFObjectHandle QPDFFormFieldObjectHelper::getValue() { return getInheritableFieldValue("/V"); } std::string QPDFFormFieldObjectHelper::getValueAsString() { return getInheritableFieldValueAsString("/V"); } QPDFObjectHandle QPDFFormFieldObjectHelper::getDefaultValue() { return getInheritableFieldValue("/DV"); } std::string QPDFFormFieldObjectHelper::getDefaultValueAsString() { return getInheritableFieldValueAsString("/DV"); } std::string QPDFFormFieldObjectHelper::getDefaultAppearance() { return getInheritableFieldValueAsString("/DA"); } int QPDFFormFieldObjectHelper::getQuadding() { int result = 0; QPDFObjectHandle fv = getInheritableFieldValue("/Q"); if (fv.isInteger()) { QTC::TC("qpdf", "QPDFFormFieldObjectHelper Q present"); result = static_cast(fv.getIntValue()); } return result; } int QPDFFormFieldObjectHelper::getFlags() { QPDFObjectHandle f = getInheritableFieldValue("/Ff"); return f.isInteger() ? f.getIntValue() : 0; } bool QPDFFormFieldObjectHelper::isText() { return (getFieldType() == "/Tx"); } bool QPDFFormFieldObjectHelper::isCheckbox() { return ((getFieldType() == "/Btn") && ((getFlags() & (ff_btn_radio | ff_btn_pushbutton)) == 0)); } bool QPDFFormFieldObjectHelper::isRadioButton() { return ((getFieldType() == "/Btn") && ((getFlags() & ff_btn_radio) == ff_btn_radio)); } bool QPDFFormFieldObjectHelper::isPushbutton() { return ((getFieldType() == "/Btn") && ((getFlags() & ff_btn_pushbutton) == ff_btn_pushbutton)); } bool QPDFFormFieldObjectHelper::isChoice() { return (getFieldType() == "/Ch"); } std::vector QPDFFormFieldObjectHelper::getChoices() { std::vector result; if (! isChoice()) { return result; } QPDFObjectHandle opt = getInheritableFieldValue("/Opt"); if (opt.isArray()) { size_t n = opt.getArrayNItems(); for (size_t i = 0; i < n; ++i) { QPDFObjectHandle item = opt.getArrayItem(i); if (item.isString()) { result.push_back(item.getUTF8Value()); } } } return result; } void QPDFFormFieldObjectHelper::setFieldAttribute( std::string const& key, QPDFObjectHandle value) { this->oh.replaceKey(key, value); } void QPDFFormFieldObjectHelper::setFieldAttribute( std::string const& key, std::string const& utf8_value) { this->oh.replaceKey(key, QPDFObjectHandle::newUnicodeString(utf8_value)); } void QPDFFormFieldObjectHelper::setV( QPDFObjectHandle value, bool need_appearances) { if (getFieldType() == "/Btn") { if (isCheckbox()) { bool okay = false; if (value.isName()) { std::string name = value.getName(); if ((name == "/Yes") || (name == "/Off")) { okay = true; setCheckBoxValue((name == "/Yes")); } } if (! okay) { this->oh.warnIfPossible( "ignoring attempt to set a checkbox field to a" " value of other than /Yes or /Off"); } } else if (isRadioButton()) { if (value.isName()) { setRadioButtonValue(value); } else { this->oh.warnIfPossible( "ignoring attempt to set a radio button field to" " an object that is not a name"); } } else if (isPushbutton()) { this->oh.warnIfPossible( "ignoring attempt set the value of a pushbutton field"); } return; } setFieldAttribute("/V", value); if (need_appearances) { QPDF* qpdf = this->oh.getOwningQPDF(); if (! qpdf) { throw std::logic_error( "QPDFFormFieldObjectHelper::setV called with" " need_appearances = true on an object that is" " not associated with an owning QPDF"); } QPDFAcroFormDocumentHelper(*qpdf).setNeedAppearances(true); } } void QPDFFormFieldObjectHelper::setV( std::string const& utf8_value, bool need_appearances) { setV(QPDFObjectHandle::newUnicodeString(utf8_value), need_appearances); } void QPDFFormFieldObjectHelper::setRadioButtonValue(QPDFObjectHandle name) { // Set the value of a radio button field. This has the following // specific behavior: // * If this is a radio button field that has a parent that is // also a radio button field and has no explicit /V, call itself // on the parent // * If this is a radio button field with childen, set /V to the // given value. Then, for each child, if the child has the // specified value as one of its keys in the /N subdictionary of // its /AP (i.e. its normal appearance stream dictionary), set // /AS to name; otherwise, if /Off is a member, set /AS to /Off. // Note that we never turn on /NeedAppearances when setting a // radio button field. QPDFObjectHandle parent = this->oh.getKey("/Parent"); if (parent.isDictionary() && parent.getKey("/Parent").isNull()) { QPDFFormFieldObjectHelper ph(parent); if (ph.isRadioButton()) { // This is most likely one of the individual buttons. Try // calling on the parent. QTC::TC("qpdf", "QPDFFormFieldObjectHelper set parent radio button"); ph.setRadioButtonValue(name); return; } } QPDFObjectHandle kids = this->oh.getKey("/Kids"); if (! (isRadioButton() && parent.isNull() && kids.isArray())) { this->oh.warnIfPossible("don't know how to set the value" " of this field as a radio button"); return; } setFieldAttribute("/V", name); int nkids = kids.getArrayNItems(); for (int i = 0; i < nkids; ++i) { QPDFObjectHandle kid = kids.getArrayItem(i); QPDFObjectHandle AP = kid.getKey("/AP"); QPDFObjectHandle annot; if (AP.isNull()) { // The widget may be below. If there is more than one, // just find the first one. QPDFObjectHandle grandkids = kid.getKey("/Kids"); if (grandkids.isArray()) { int ngrandkids = grandkids.getArrayNItems(); for (int j = 0; j < ngrandkids; ++j) { QPDFObjectHandle grandkid = grandkids.getArrayItem(j); AP = grandkid.getKey("/AP"); if (! AP.isNull()) { QTC::TC("qpdf", "QPDFFormFieldObjectHelper radio button grandkid widget"); annot = grandkid; break; } } } } else { annot = kid; } if (! annot.isInitialized()) { QTC::TC("qpdf", "QPDFObjectHandle broken radio button"); this->oh.warnIfPossible( "unable to set the value of this radio button"); continue; } if (AP.isDictionary() && AP.getKey("/N").isDictionary() && AP.getKey("/N").hasKey(name.getName())) { QTC::TC("qpdf", "QPDFFormFieldObjectHelper turn on radio button"); annot.replaceKey("/AS", name); } else { QTC::TC("qpdf", "QPDFFormFieldObjectHelper turn off radio button"); annot.replaceKey("/AS", QPDFObjectHandle::newName("/Off")); } } } void QPDFFormFieldObjectHelper::setCheckBoxValue(bool value) { // Set /AS to /Yes or /Off in addition to setting /V. QPDFObjectHandle name = QPDFObjectHandle::newName(value ? "/Yes" : "/Off"); setFieldAttribute("/V", name); QPDFObjectHandle AP = this->oh.getKey("/AP"); QPDFObjectHandle annot; if (AP.isNull()) { // The widget may be below. If there is more than one, just // find the first one. QPDFObjectHandle kids = this->oh.getKey("/Kids"); if (kids.isArray()) { int nkids = kids.getArrayNItems(); for (int i = 0; i < nkids; ++i) { QPDFObjectHandle kid = kids.getArrayItem(i); AP = kid.getKey("/AP"); if (! AP.isNull()) { QTC::TC("qpdf", "QPDFFormFieldObjectHelper checkbox kid widget"); annot = kid; break; } } } } else { annot = this->oh; } if (! annot.isInitialized()) { QTC::TC("qpdf", "QPDFObjectHandle broken checkbox"); this->oh.warnIfPossible( "unable to set the value of this checkbox"); return; } QTC::TC("qpdf", "QPDFFormFieldObjectHelper set checkbox AS"); annot.replaceKey("/AS", name); }