[poppler] 5 commits - poppler/Form.cc

Carlos Garcia Campos carlosgc at kemper.freedesktop.org
Sat Nov 3 02:01:02 PDT 2012


 poppler/Form.cc |  136 ++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 88 insertions(+), 48 deletions(-)

New commits:
commit 401de95f5ab42ab0f5d8fd92d76b5def50f84a2b
Author: Fabio D'Urso <fabiodurso at hotmail.it>
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 <fabiodurso at hotmail.it>
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 <fabiodurso at hotmail.it>
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 <fabiodurso at hotmail.it>
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 <fabiodurso at hotmail.it>
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();


More information about the poppler mailing list