9

I'm working on dynamically filling in the fields on a pdf document using ITextSharp. I'd like to be able to determine the "export value" of the checkbox is from the codebehind in order to determine what value to send to this checkbox if it should be checked. Most of the documents I've worked with in the past had the same export value for every check box but the one I'm currently working with varies from checkbox to checkbox. I could go through all of the text boxes and make them consistent but it would save a lot of time in the future if I could just determine what the export value of these checkboxes are at runtime and set them accordingly.

Thanks in advance!

I tried to implement the solution below in C# and ended up with the following code:

 public string GetCheckBoxExportValue(AcroFields pdfDocument, string checkBoxFieldName)
    {
        AcroFields.Item item = pdfDocument.GetFieldItem(checkBoxFieldName);
        if (item.values.Count > 0)
        {
            PdfDictionary valueDict = item.GetValue(0);

            PdfDictionary appearanceDict = valueDict.GetAsDict(PdfName.AP);

            // if there's an appearance dict at all, one key will be "Off", and the other
            // will be the export value... there should only be two.
            if (appearanceDict != null)
            {


                foreach (PdfName curKey in appearanceDict.Keys)
                {
                    if (!PdfName.OFF.Equals(curKey))
                    {
                        return curKey.ToString(); // string will have a leading '/' character
                    }
                }
            }

            // if that doesn't work, there might be an /AS key, whose value is a name with 
            // the export value, again with a leading '/'
            PdfName curVal = valueDict.GetAsName(PdfName.AS);
            if (curVal != null)
            {
                return curVal.ToString();
            }

        }
        //return null if you get this far
            return null;

    }

This just returns "/D" every single time. I'm not sure if the approach needs to be different in C# or if I'm just missing something.

Ben
  • 2,058
  • 9
  • 29
  • 39
  • @Lorenzo - I think you need to stop caring about how other people use the site so much – Charles Boyung Dec 20 '10 at 16:55
  • @Lorenzo, I'm not sure what I should be reading in the FAQ's? – Ben Dec 20 '10 at 18:01
  • This is an excerpt from the FAQ when it comes to explain how to accept an answer: `[...] This lets other people know that you have received a good answer to your question. Doing this is helpful because it shows other people that you're getting value from the community. (If you don't do this, people will often politely ask you to go back and accept answers for more of your questions!)`. – Lorenzo Dec 20 '10 at 21:51

4 Answers4

12

Okay, you need to check the low-level PDF objects for the appropriate values. You can look up said values in the PDF Reference (chapter 12: Interactive Features, section 7: Interactive Forms).

In particular (and in Java):

AcroFields.Item item = acroFields.getFieldItem(fldName);
PdfDictionary valueDict = item.getValue(0);

PdfDictionary appearanceDict = valueDict .getAsDict(PdfName.AP);

if (appearanceDict != null) {
  PdfDictionary normalAppearances = appearanceDict.getAsDict(PdfName.N);
  // /D is for the "down" appearances.

  // if there are normal appearances, one key will be "Off", and the other
  // will be the export value... there should only be two.
  if (normalAppearances != null) {
    Set<PdfName> keys = normalAppearances .getKeys();
    for (PdfName curKey : keys) {
      if (!PdfName.OFF.equals(curKey)) {
        return curKey.toString(); // string will have a leading '/' character
      }
    }
  }


}
// if that doesn't work, there might be an /AS key, whose value is a name with 
// the export value, again with a leading '/'
PdfName curVal = valueDict.getAsName(PdfName.AS);
if (curVal != null) {
  return curVal.toString();
}

Something like that. The usual "I just wrote this in the edit box here" provisions apply, but that should be good to go. I write a distressingly large amount of low level iText code.

Mark Storer
  • 15,672
  • 3
  • 42
  • 80
  • 2
    Mark - I've admired your iText expertise on the "iText-questions" list and I'm gflad you're sharing some of that here on SO. – Jay Riggs Dec 22 '10 at 03:28
  • I tried to implement this and I ended up with the code above. For some reason this approach isn't working for me. Any thoughts? – Ben Dec 22 '10 at 17:56
  • I get "/D" for the export value every time. The first curKey in appearanceDict.Keys ALWAYS comes back as != to PdfName.OFF and it is ALWAYS "/D". Any thoughts? – Ben Dec 22 '10 at 20:28
  • Oops. Missed a level of nesting. I'll fix the answer – Mark Storer Dec 22 '10 at 22:30
  • 3
    Marks code works awesome, except when multiple checkboxes are tied to one field name. in C#, I use `acroFields.GetAppearanceStates(fldName)` to get a string array of possible values. – Josh Jul 22 '11 at 15:09
  • Oh! Do it the easy way... I see how it is. (I suspect that the code for `GetAppearanceStates` will look remarkably like what I've written above). – Mark Storer Jul 22 '11 at 17:18
  • What if I have a radio button group and want to populate it? http://stackoverflow.com/questions/24063439/how-to-use-itextsharp-to-populate-radio-button – Si8 Jun 05 '14 at 15:19
5

The best way I found to set a checkbox is:

    void SetCB(AcroFields fields, string F)
    {
        try
        {
            fields.SetField(F, fields.GetFieldItem(F).GetValue(0).GetAsDict(PdfName.AP).GetAsDict(PdfName.N).Keys.Single().ToString().TrimStart('/'));
        } catch { }
    }

error: Sequence contains more than one element

e.g:

       PdfReader reader = new PdfReader("c:\\qqq\\fl100Y2.pdf");// formFile);
        using (PdfStamper stamper = new PdfStamper(reader, new FileStream("c:\\qqq\\fl100Ynew.pdf", FileMode.Create)))
        {
            AcroFields fields = stamper.AcroFields;

            bool set = fields.SetFieldProperty("FillText156", "textsize", 10.0f, null);
            SetCB(fields, "CheckBox24");
            SetCB(fields, "CheckBox24by");
             fields.SetField("FillText156", "John Doe");
            // flatten form fields and close document
            stamper.FormFlattening = true;
            stamper.Close();
        }
Sigar Dave
  • 2,598
  • 1
  • 20
  • 42
yuli
  • 51
  • 1
  • 2
  • What about radio button group? http://stackoverflow.com/questions/24063439/how-to-use-itextsharp-to-populate-radio-button – Si8 Jun 05 '14 at 15:23
0

This was the final method I used to get it working based on the others:

    public string GetCheckBoxExportValue(AcroFields fields, string cbFieldName)
    {
        AcroFields.Item item = fields.GetFieldItem(cbFieldName);
        if (item.values.Count > 0)
        {
            PdfDictionary valueDict = item.values[0] as PdfDictionary;
            PdfDictionary appDict = valueDict.GetAsDict(PdfName.AP);

            if (appDict != null)
            {
                PdfDictionary normalApp = appDict.GetAsDict(PdfName.N);

                foreach (PdfName curKey in normalApp.Keys)
                {
                    if (!PdfName.OFF.Equals(curKey))
                    {
                        // string will have a leading '/' character
                        return curKey.ToString(); 
                    }
                }
            }

            PdfName curVal = valueDict.GetAsName(PdfName.AS);
            if (curVal != null)
            {
                return curVal.ToString();
            }

        }

        return null;
    }
Janspeed
  • 2,644
  • 2
  • 22
  • 22
0

I couldn't get the answer Mark to work for me because the appearanceDict was always null. Here is a method I wrote that works for the CheckBox and RadioButton controls on the forms I'm dealing with.

private static string GetAnswerValue(AcroFields.Item f, int i)
{
    var widg = f.GetWidget(i);
    if (widg == null)
        return null;
    var ap = widg.GetAsDict(PdfName.AP);
    if (ap == null)
        return null;

    //PdfName.D also seems to work
    var d = ap.GetAsDict(PdfName.N);
    if (d == null)
        return null;
    var e = d.Keys.FirstOrDefault(n => !n.Equals(PdfName.OFF));
    if (e == null)
        return null;
    return e.ToString().Substring(1);
}
park896
  • 455
  • 2
  • 6
  • 9