poppler/Form.cc | 136 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 48 deletions(-)
New commits: commit 401de95f5ab42ab0f5d8fd92d76b5def50f84a2b Author: Fabio D'Urso <[email protected]> Date: Wed Oct 31 19:43:51 2012 +0100 FormFieldChoice ctor: Look for selected options in /I instead of /V if /I is available Since /I stores the indices of the selected options, it can distinguish duplicate option (i.e. options with the same name/export value). diff --git a/poppler/Form.cc b/poppler/Form.cc index 9998240..586482d 100644 --- a/poppler/Form.cc +++ b/poppler/Form.cc @@ -1130,44 +1130,57 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object *aobj, const Ref& ref, For } obj1.free(); - // find selected items - // Note: According to PDF specs, /V should *never* contain the exportVal. - // However, if /Opt is an array of (exportVal,optionName) pairs, acroread - // seems to expect the exportVal instead of the optionName and so we do too. - if (Form::fieldLookup(dict, "V", &obj1)->isString()) { - for (int i = 0; i < numChoices; i++) { - if (choices[i].exportVal) { - if (choices[i].exportVal->cmp(obj1.getString()) == 0) { - choices[i].selected = true; - } - } else if (choices[i].optionName) { - if (choices[i].optionName->cmp(obj1.getString()) == 0) { - choices[i].selected = true; - } + // Find selected items + // Note: PDF specs say that /V has precedence over /I, but acroread seems to + // do the opposite. We do the same. + if (Form::fieldLookup(dict, "I", &obj1)->isArray()) { + for (int i = 0; i < obj1.arrayGetLength(); i++) { + Object obj2; + if (obj1.arrayGet(i, &obj2)->isInt() && obj2.getInt() >= 0 && obj2.getInt() < numChoices) { + choices[obj2.getInt()].selected = true; } + obj2.free(); } - } else if (obj1.isArray()) { - for (int i = 0; i < numChoices; i++) { - for (int j = 0; j < obj1.arrayGetLength(); j++) { - Object obj2; - obj1.arrayGet(j, &obj2); - GBool matches = gFalse; - + } else { + obj1.free(); + // Note: According to PDF specs, /V should *never* contain the exportVal. + // However, if /Opt is an array of (exportVal,optionName) pairs, acroread + // seems to expect the exportVal instead of the optionName and so we do too. + if (Form::fieldLookup(dict, "V", &obj1)->isString()) { + for (int i = 0; i < numChoices; i++) { if (choices[i].exportVal) { - if (choices[i].exportVal->cmp(obj2.getString()) == 0) { - matches = gTrue; + if (choices[i].exportVal->cmp(obj1.getString()) == 0) { + choices[i].selected = true; } } else if (choices[i].optionName) { - if (choices[i].optionName->cmp(obj2.getString()) == 0) { - matches = gTrue; + if (choices[i].optionName->cmp(obj1.getString()) == 0) { + choices[i].selected = true; } } + } + } else if (obj1.isArray()) { + for (int i = 0; i < numChoices; i++) { + for (int j = 0; j < obj1.arrayGetLength(); j++) { + Object obj2; + obj1.arrayGet(j, &obj2); + GBool matches = gFalse; - obj2.free(); + if (choices[i].exportVal) { + if (choices[i].exportVal->cmp(obj2.getString()) == 0) { + matches = gTrue; + } + } else if (choices[i].optionName) { + if (choices[i].optionName->cmp(obj2.getString()) == 0) { + matches = gTrue; + } + } - if (matches) { - choices[i].selected = true; - break; // We've determined that this option is selected. No need to keep on scanning + obj2.free(); + + if (matches) { + choices[i].selected = true; + break; // We've determined that this option is selected. No need to keep on scanning + } } } } commit cfd3a46a857100cb634e18192b762e7342165348 Author: Fabio D'Urso <[email protected]> Date: Wed Oct 31 15:44:32 2012 +0100 FormFieldChoice: Handle /V values containing the export value instead of the option name According to the PDF spec, /V should always contain an "option name" and never an "export value" if /Opt is an array of couples. However, it seems that acroread works the other way round: it is able to identify selected options only if they are referred by their export value instead of the option name. With this patch, we mimic this behavior. diff --git a/poppler/Form.cc b/poppler/Form.cc index 53e6167..9998240 100644 --- a/poppler/Form.cc +++ b/poppler/Form.cc @@ -1131,29 +1131,44 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object *aobj, const Ref& ref, For obj1.free(); // find selected items + // Note: According to PDF specs, /V should *never* contain the exportVal. + // However, if /Opt is an array of (exportVal,optionName) pairs, acroread + // seems to expect the exportVal instead of the optionName and so we do too. if (Form::fieldLookup(dict, "V", &obj1)->isString()) { for (int i = 0; i < numChoices; i++) { - if (!choices[i].optionName) - continue; - - if (choices[i].optionName->cmp(obj1.getString()) == 0) - choices[i].selected = true; + if (choices[i].exportVal) { + if (choices[i].exportVal->cmp(obj1.getString()) == 0) { + choices[i].selected = true; + } + } else if (choices[i].optionName) { + if (choices[i].optionName->cmp(obj1.getString()) == 0) { + choices[i].selected = true; + } + } } } else if (obj1.isArray()) { for (int i = 0; i < numChoices; i++) { - if (!choices[i].optionName) - continue; - for (int j = 0; j < obj1.arrayGetLength(); j++) { Object obj2; - obj1.arrayGet(j, &obj2); - if (choices[i].optionName->cmp(obj2.getString()) == 0) { - choices[i].selected = true; - obj2.free(); - break; + GBool matches = gFalse; + + if (choices[i].exportVal) { + if (choices[i].exportVal->cmp(obj2.getString()) == 0) { + matches = gTrue; + } + } else if (choices[i].optionName) { + if (choices[i].optionName->cmp(obj2.getString()) == 0) { + matches = gTrue; + } } + obj2.free(); + + if (matches) { + choices[i].selected = true; + break; // We've determined that this option is selected. No need to keep on scanning + } } } } @@ -1204,7 +1219,9 @@ void FormFieldChoice::updateSelection() { objI.arrayAdd(obj1.initInt(i)); } - if (choices[i].optionName) { + if (choices[i].exportVal) { + objV.initString(choices[i].exportVal->copy()); + } else if (choices[i].optionName) { objV.initString(choices[i].optionName->copy()); } @@ -1220,7 +1237,9 @@ void FormFieldChoice::updateSelection() { objI.arrayAdd(obj1.initInt(i)); } - if (choices[i].optionName) { + if (choices[i].exportVal) { + objV.arrayAdd(obj1.initString(choices[i].exportVal->copy())); + } else if (choices[i].optionName) { objV.arrayAdd(obj1.initString(choices[i].optionName->copy())); } } commit ce99940bcac0447f32ee2ad46efb09af93989c12 Author: Fabio D'Urso <[email protected]> Date: Sat Oct 13 00:13:33 2012 +0200 FormFieldChoice::updateSelection: Write /I too This improves handling of choice fields containing two or more entries with the same name, and also makes sure that the previous value of /I gets updated (failing to update it results in acroread still showing the old selection). diff --git a/poppler/Form.cc b/poppler/Form.cc index 4a3f30d..53e6167 100644 --- a/poppler/Form.cc +++ b/poppler/Form.cc @@ -1179,35 +1179,57 @@ void FormFieldChoice::print(int indent) #endif void FormFieldChoice::updateSelection() { - Object obj1; + Object objV, objI, obj1; + objI.initNull(); - //this is an editable combo-box with user-entered text if (edit && editedChoice) { - obj1.initString(editedChoice->copy()); + // This is an editable combo-box with user-entered text + objV.initString(editedChoice->copy()); } else { - int numSelected = getNumSelected(); + const int numSelected = getNumSelected(); + + // Create /I array only if multiple selection is allowed (as per PDF spec) + if (multiselect) { + objI.initArray(xref); + } + if (numSelected == 0) { - obj1.initString(new GooString("")); + // No options are selected + objV.initString(new GooString("")); } else if (numSelected == 1) { + // Only one option is selected for (int i = 0; i < numChoices; i++) { - if (choices[i].optionName && choices[i].selected) { - obj1.initString(choices[i].optionName->copy()); - break; + if (choices[i].selected) { + if (multiselect) { + objI.arrayAdd(obj1.initInt(i)); + } + + if (choices[i].optionName) { + objV.initString(choices[i].optionName->copy()); + } + + break; // We've just written the selected option. No need to keep on scanning } } } else { - obj1.initArray(xref); + // More than one option is selected + objV.initArray(xref); for (int i = 0; i < numChoices; i++) { - if (choices[i].optionName && choices[i].selected) { - Object obj2; - obj2.initString(choices[i].optionName->copy()); - obj1.arrayAdd(&obj2); + if (choices[i].selected) { + if (multiselect) { + objI.arrayAdd(obj1.initInt(i)); + } + + if (choices[i].optionName) { + objV.arrayAdd(obj1.initString(choices[i].optionName->copy())); + } } } } } - obj.getDict()->set("V", &obj1); + obj.getDict()->set("V", &objV); + obj.getDict()->set("I", &objI); xref->setModifiedObject(&obj, ref); updateChildrenAppearance(); } commit 102553e2104a1b223c8ac924aa6702829adebbdb Author: Fabio D'Urso <[email protected]> Date: Wed Oct 31 16:57:56 2012 +0100 FormFieldChoice::updateSelection: Fixed wrong loop condition diff --git a/poppler/Form.cc b/poppler/Form.cc index d396437..4a3f30d 100644 --- a/poppler/Form.cc +++ b/poppler/Form.cc @@ -1189,7 +1189,7 @@ void FormFieldChoice::updateSelection() { if (numSelected == 0) { obj1.initString(new GooString("")); } else if (numSelected == 1) { - for (int i = 0; numChoices; i++) { + for (int i = 0; i < numChoices; i++) { if (choices[i].optionName && choices[i].selected) { obj1.initString(choices[i].optionName->copy()); break; commit d7522ea1d2e66beef64f705e8986142f15fcf613 Author: Fabio D'Urso <[email protected]> Date: Wed Oct 31 15:26:37 2012 +0100 FormFieldChoice ctor: Don't convert "human-readable" option names to unicode Despite that comment, they're not meant to be read by humans only, but they are also used as option identifiers. This patch stops poppler from forcing them to be unicode. Instead, they now stay the same encoding as their corresponding /Opt entry. This fixes poppler not being able to recognize selected entries in documents produced by poppler itself: previously, the /V value was always written in Unicode encoding, and therefore it was very often not binary-equal to the corresponding /Opt entry. Now the /V value is always binary-equal to the corresponding /Opt entry. diff --git a/poppler/Form.cc b/poppler/Form.cc index 68a4219..d396437 100644 --- a/poppler/Form.cc +++ b/poppler/Form.cc @@ -1130,7 +1130,7 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object *aobj, const Ref& ref, For } obj1.free(); - // find selected items and convert choice's human readable strings to UTF16 + // find selected items if (Form::fieldLookup(dict, "V", &obj1)->isString()) { for (int i = 0; i < numChoices; i++) { if (!choices[i].optionName) @@ -1138,13 +1138,6 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object *aobj, const Ref& ref, For if (choices[i].optionName->cmp(obj1.getString()) == 0) choices[i].selected = true; - - if (!choices[i].optionName->hasUnicodeMarker()) { - int len; - char* buffer = pdfDocEncodingToUTF16(choices[i].optionName, &len); - choices[i].optionName->Set(buffer, len); - delete [] buffer; - } } } else if (obj1.isArray()) { for (int i = 0; i < numChoices; i++) { @@ -1162,13 +1155,6 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object *aobj, const Ref& ref, For } obj2.free(); } - - if (!choices[i].optionName->hasUnicodeMarker()) { - int len; - char* buffer = pdfDocEncodingToUTF16(choices[i].optionName, &len); - choices[i].optionName->Set(buffer, len); - delete [] buffer; - } } } obj1.free(); _______________________________________________ poppler mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/poppler
