poppler/Form.cc | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++-- poppler/Form.h | 2 +- poppler/Page.h | 2 ++ 3 files changed, 53 insertions(+), 3 deletions(-)
New commits: commit 40cc13db621f2f49f55c4e4390bf2e862cc05d41 Author: Nelson Benítez León <[email protected]> Date: Tue Mar 9 16:47:27 2021 -0400 Forms: fix unclicking standalone form buttons We can find pdf forms that eg. have three related radio buttons where their *only* relationship is having the same full qualified name. Such a form is supported by Adobe Reader and produced by LaTeX through TeXStudio. So, when clicking one such radio button, Poppler failed to unclick the other radio buttons as they are not children nor parent of the clicked one (which is the ordinary way to set radio button groups as per the PDF standard). So, when clicking a radio button, let's add support to find other radio buttons in the same page that are only related by having same fully qualified name (these may be in standalone or normal fields). Where trying to find where PDF standard covers this special case of related radio buttons, the closest thing we can find is section "Field names" inside chapter "8.6.2 Field Dictionaries" in 1.7 PDF spec. Issue #1034 diff --git a/poppler/Form.cc b/poppler/Form.cc index 4d42aedf..70bfb3c6 100644 --- a/poppler/Form.cc +++ b/poppler/Form.cc @@ -269,6 +269,54 @@ void FormWidgetButton::setState(bool astate) parent()->setState(astate ? getOnStr() : (char *)"Off"); // Parent will call setAppearanceState() + + // Now handle standAlone fields which are related to this one by having the same + // fully qualified name. This is *partially* by spec, as seen in "Field names" + // section inside "8.6.2 Field Dictionaries" in 1.7 PDF spec. Issue #1034 + + if (!astate) // We're only interested when this field is being set to ON, + return; // to check if it has related fields and then set them OFF + + unsigned this_page_num, this_field_num; + decodeID(getID(), &this_page_num, &this_field_num); + Page *this_page = doc->getCatalog()->getPage(this_page_num); + const FormField *this_field = getField(); + if (!this_page->hasStandaloneFields() || this_field == nullptr) + return; + + auto this_page_widgets = this_page->getFormWidgets(); + const FormButtonType this_button_type = getButtonType(); + + const int tot = this_page_widgets->getNumWidgets(); + for (int i = 0; i < tot; i++) { + bool found_related = false; + FormWidget *wid = this_page_widgets->getWidget(i); + const bool same_fqn = wid->getFullyQualifiedName()->cmp(getFullyQualifiedName()) == 0; + const bool same_button_type = wid->getType() == formButton && static_cast<const FormWidgetButton *>(wid)->getButtonType() == this_button_type; + + if (same_fqn && same_button_type) { + if (this_field->isStandAlone()) { + //'this_field' is standAlone, so we need to search in both standAlone fields and normal fields + if (this_field != wid->getField()) { // so take care to not choose our same field + found_related = true; + } + } else { + //'this_field' is not standAlone, so we just need to search in standAlone fields + if (wid->getField()->isStandAlone()) { + found_related = true; + } + } + } + + if (found_related) { + FormFieldButton *ffb = static_cast<FormFieldButton *>(wid->getField()); + if (ffb == nullptr) { + error(errInternal, -1, "FormWidgetButton::setState : FormFieldButton expected\n"); + continue; + } + ffb->setState((char *)"Off", true); + } + } } bool FormWidgetButton::getState() const @@ -1285,7 +1333,7 @@ void FormFieldButton::fillChildrenSiblingsID() } } -bool FormFieldButton::setState(const char *state) +bool FormFieldButton::setState(const char *state, bool ignoreToggleOff) { // A check button could behave as a radio button // when it's in a set of more than 1 buttons @@ -1302,7 +1350,7 @@ bool FormFieldButton::setState(const char *state) bool isOn = strcmp(state, "Off") != 0; - if (!isOn && noAllOff) + if (!isOn && noAllOff && !ignoreToggleOff) return false; // Don't allow to set all radio to off const char *current = getAppearanceState(); diff --git a/poppler/Form.h b/poppler/Form.h index f978958d..39786ed9 100644 --- a/poppler/Form.h +++ b/poppler/Form.h @@ -425,7 +425,7 @@ public: bool noToggleToOff() const { return noAllOff; } // returns true if the state modification is accepted - bool setState(const char *state); + bool setState(const char *state, bool ignoreToggleOff = false); bool getState(const char *state) const; const char *getAppearanceState() const { return appearanceState.isName() ? appearanceState.getName() : nullptr; } diff --git a/poppler/Page.h b/poppler/Page.h index 4f8a3d3d..228e31e2 100644 --- a/poppler/Page.h +++ b/poppler/Page.h @@ -240,6 +240,8 @@ public: // Get the page's default CTM. void getDefaultCTM(double *ctm, double hDPI, double vDPI, int rotate, bool useMediaBox, bool upsideDown); + bool hasStandaloneFields() const { return !standaloneFields.empty(); } + private: // replace xref void replaceXRef(XRef *xrefA); _______________________________________________ poppler mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/poppler
