简体   繁体   中英

Populate PdfRadioButtonField using PDFSharp

I'm using PDFSharp version 1.50.4740-beta5 from http://www.pdfsharp.net which I installed from NuGet.

I'm able to fill text form fields and checkbox form fields but I can't get radio buttons to work at all. I don't get an error. SelectedIndex is -1 before and after I set it to 1.

Several at articles on Stack Overflow have helped me to get this far. Has anyone successfully populated radio button form fields using this product? Here is a link to the sample PDF: http://www.myblackmer.com/bluebook/interactiveform_enabled.pdf

(Before you suggest iTextPdf, I've already evaluated it and Aspose.PDF and they are not practical)

        //using PdfSharp.Pdf.IO;
        //using PdfSharp.Pdf;
        //using PdfSharp.Pdf.AcroForms;
        string fileName = "x:\\interactiveform_enabled.pdf";
        PdfSharp.Pdf.PdfDocument pdfDocument = PdfReader.Open(fileName);

        //The populated fields are not visible by default
        if (pdfDocument.AcroForm.Elements.ContainsKey("/NeedAppearances"))
        {
            pdfDocument.AcroForm.Elements["/NeedAppearances"] = new PdfBoolean(true);
        }
        else
        {
            pdfDocument.AcroForm.Elements.Add("/NeedAppearances", new PdfBoolean(true));
        }

        PdfRadioButtonField currentField = (PdfRadioButtonField)(pdfDocument.AcroForm.Fields["Sex"]);
        currentField.ReadOnly = false;
        currentField.SelectedIndex = 1;

        pdfDocument.Flatten();
        pdfDocument.Save("x:\\interactiveform_enabled_2.pdf");

The following worked for me:

  1. Find out the possible values of the radio buttons, eg using a PDF tool
  2. Use the Value property with a PdfName instance (not a PdfString !)
  3. Use the prefix / before the value name

Example, if you want to set the option "choice1" selected:

var radio = (PdfRadioButtonField)form.Fields["MyRadioButtonField"];
radio.Value = new PdfName("/choice1");

I found the solution at https://forum.pdfsharp.net/viewtopic.php?f=2&t=632

Since this is #1 in a Google search for "PDFSharp radio buttons", figured I'd share what seems to be working for me.

I created extension functions for the PdfRadioButtonField object that:

  • Gets all option values and their indexes
  • Sets the value for a radio field by index number
  • Sets the value for a radio field by value

In the following examples, ErPdfRadioOption is:

public class ErPdfRadioOption
{
    public int Index { get; set; }
    public string Value { get; set; }
}

Get all available options for a radio field


public static List<ErPdfRadioOption> FindOptions(this PdfRadioButtonField source) {
    if (source == null) return null;
    List<ErPdfRadioOption> options = new List<ErPdfRadioOption>();

    PdfArray kids = source.Elements.GetArray("/Kids");

    int i = 0;
    foreach (var kid in kids) {
        PdfReference kidRef = (PdfReference)kid;
        PdfDictionary dict = (PdfDictionary)kidRef.Value;
        PdfDictionary dict2 = dict.Elements.GetDictionary("/AP");
        PdfDictionary dict3 = dict2.Elements.GetDictionary("/N");

        if (dict3.Elements.Keys.Count != 2)
            throw new Exception("Option dictionary should only have two values");

        foreach (var key in dict3.Elements.Keys)
            if (key != "/Off") { // "Off" is a reserved value that all non-selected radio options have
                ErPdfRadioOption option = new ErPdfRadioOption() { Index = i, Value = key };
                options.Add(option);
            }
        i++;
    }

    return options;
}

Usage

PdfDocument source;
PdfAcroField field = source.AcroForm.Fields[...]; //name or index of the field
PdfRadioButtonField radioField = field as PdfRadioButtonField;
return radioField.FindOptions();

Result (JSON)


[
  {
    "Index": 0,
    "Value": "/Choice1"
  },
  {
    "Index": 1,
    "Value": "/Choice2"
  },
  {
    "Index": 2,
    "Value": "/Choice3"
  }
]

Set the option for a radio field by index

You'd think this is what the SelectedIndex property should be for... but apparently not.


public static void SetOptionByIndex(this PdfRadioButtonField source,int index) {
    if (source == null) return;
    List<ErPdfRadioOption> options = source.FindOptions();
    ErPdfRadioOption selectedOption = options.Find(x => x.Index == index);
    if (selectedOption == null) return; //The specified index does not exist as an option for this radio field
    
    // https://forum.pdfsharp.net/viewtopic.php?f=2&t=3561
    PdfArray kids = (PdfArray)source.Elements["/Kids"];
    int j = 0;
    foreach (var kid in kids) {
        var kidValues = ((PdfReference)kid).Value as PdfDictionary;
        //PdfRectangle rectangle = kidValues.Elements.GetRectangle(PdfAnnotation.Keys.Rect);
        if (j == selectedOption.Index)
            kidValues.Elements.SetValue("/AS", new PdfName(selectedOption.Value));
        else
            kidValues.Elements.SetValue("/AS", new PdfName("/Off"));
        j++;
    }
}

Note: This depends on the FindOptions() function above.

Usage

PdfDocument source;
PdfAcroField field = source.AcroForm.Fields[...]; //name or index of the field
PdfRadioButtonField radioField = field as PdfRadioButtonField;
radioField.SetOptionByIndex(index);

Set the option for a radio field by value


public static void SetOptionByValue(this PdfRadioButtonField source, string value) {
    if (source == null || string.IsNullOrWhiteSpace(value)) return;
    if (!value.StartsWith("/", StringComparison.OrdinalIgnoreCase)) value = "/" + value; //All values start with a '/' character
    List<ErPdfRadioOption> options = source.FindOptions();
    ErPdfRadioOption selectedOption = options.Find(x => x.Value == value);
    if (selectedOption == null) return; //The specified index does not exist as an option for this radio field
    source.SetOptionByIndex(selectedOption.Index);
}

Note: This depends on the FindOptions() and SetOptionByIndex() functions above.

Based on this post in the PDFsharp forum: https://forum.pdfsharp.net/viewtopic.php?f=2&t=3561#p11386

Using PDFsharp version 1.50.5147

Based on Chris's answer, the salient point is that you have to set the value for all options to \\Off while setting the option's value you want to the \\Choice. To that end, you can have a simple function like:

    //sets a radio button option by setting the index and value and turning the others off
    static void setRadioOption (PdfRadioButtonField field, int option, string optvalue)
    {
        PdfArray kids = (PdfArray)field.Elements["/Kids"];
        int j = 0;
        foreach (var kid in kids)
        {
            var kidValues = ((PdfReference)kid).Value as PdfDictionary;
            if (j == option)
                kidValues.Elements.SetValue("/AS", new PdfName(optvalue));
            else
                kidValues.Elements.SetValue("/AS", new PdfName("/Off"));
            j++;
        }
    }

Is it me you're looking for?

private static void SetPdfRadiobutton(PdfDocument document, string fieldname, string newvalue, int item)
    {
        PdfRadioButtonField field = (PdfRadioButtonField)document.AcroForm.Fields[fieldname];
        PdfArray items = (PdfArray)field.Elements["/Kids"];

        for (int i = 0; i < items.Elements.Count; i++)
        {
            PdfDictionary keys = ((PdfReference)items.Elements[i]).Value as PdfDictionary;
            keys.Elements.SetValue("/AS", i == item ? new PdfName(newvalue) : new PdfName("/Off"));
        }

        field.ReadOnly = true;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM