We are using WkHtmlToPdf to convert from html to pdf. Now I have make all the form fields editable on request as well. Since there is a bug in WkHtmlToPdf while converting Html to Pdf it makes all the form field's property as Visible Not Printable.
To get around this problem we are using ITextSharp to read existing pdf that has been generated by WkHtmlToPdf and setting that property as Visible for every form field.
This is how I am doing that
var pdfReader = new PdfReader(inputFilename);
using (var outputStream = new FileStream(outputFilename, FileMode.OpenOrCreate))
{
var stamper = new PdfStamper(pdfReader, outputStream);
// Regenarating all the fileds using itextSharp.
// Since wkHtmltoPdf has differnt default settings for pdf form fields (Visible and not Printable)
var formFields = stamper.AcroFields;
var filedNames = formFields.Fields.Keys;
foreach (var keyName in filedNames)
{
formFields.SetFieldProperty(keyName, "setflags", PdfAnnotation.FLAGS_PRINT, null);
formFields.SetFieldProperty(keyName, "setflags", PdfFormField.FF_EDIT, null);
formFields.SetFieldProperty(keyName, "textsize", 8.0f, null);
formFields.RegenerateField(keyName);
}
}
This solved my problem for all the fields but check Boxes. I have few check boxes in html which are converted to pdf using WkHtmlToPdf. I realized that when ever I try to RegenerateField() (Last line in foreach loop) check box is loosing all its properties (Whether I checked or unchecked) it is only showing empty check box, and its not editable.
To solve this problem I came up with the following
if (formFields.GetFieldType(keyName).Equals(AcroFields.FIELD_TYPE_CHECKBOX))
{
// Regenerate checkbox formfield causing it to reset all its properties
// including checkd/uncheked property
formFields.SetFieldProperty(keyName, "setflags", PdfAnnotation.FLAGS_PRINT, null);
// Ignore the commented code as it is not working Not sure why??? and how
//var item = formFields.GetFieldItem(keyName);
//var appearance = item.GetValue(0).Get(PdfName.AS);
//if (appearance != null)
/{
// if (appearance.tostring().tolower().equals("/yes"))
// {
// formfields.regeneratefield(keyname);
// Not even one working when I try to set the check box status like below
// success = formfields.setfield(keyname, "yes");
// success = formfields.setfield(keyname, "/yes");
// success = formfields.setfield(keyname, "/yes");
// success = formfields.setfield(keyname, "on");
// success = formfields.setfield(keyname, "1");
// }
}
With the code (excluding commented) code Check box successfully retaining its status and displaying in the generated Pdf. But it's not printing. I checked the properties of check box it's visible like any other fields in the pdf. Apart from check boxes all the fields(text boxes) are printing its values.
I am checking properties through --> Open pdf document--> Forms--> Add/Edit Fields
What am I doing wrong here? Instead of setting property on check box I tried to construct a brand new check box using Itextsharp but I couldn't using ItexSharp manual example. DO anyone did this stuff before?
Even if I generate a new check box using ItextSharp, how can I replace exactly existing check box?
Thanks a lot for reading my story..
Well I solved this problem by replacing wkHtmltoPdf check box with ITextSharp. I am answering it in detail it might save your time
/// <summary>
/// By default, wkHtmlToPdf leaves pdf form fields as being "Visible but not printable". Using iTextSharp we will change them to being "Visible".
/// </summary>
private string MakePdfFormFieldsEditable(string inputFilename)
{
var outputFilename = CreateTempPdfFilename();
var pdfReader = new PdfReader(inputFilename);
var checkedCheckBoxNames = new List<string>();
using (var outputStream = new FileStream(outputFilename, FileMode.OpenOrCreate))
{
var stamper = new PdfStamper(pdfReader, outputStream);
// Regenarating all the fileds using itextSharp.
// Since wkHtmltoPdf has differnt default settings for pdf form fields (Visible and not Printable)
var formFields = stamper.AcroFields;
var filedNames = formFields.Fields.Keys;
var removableFiledNames = new List<string>();
foreach (var keyName in filedNames)
{
if (formFields.GetFieldType(keyName).Equals(AcroFields.FIELD_TYPE_CHECKBOX))
{
// Since check boxes generated by WKHtmltoPdf is crapy and click behaviour is annoying
// we are replacing it with new checkbox
var item = formFields.GetFieldItem(keyName);
// Return "/yes" if it's checked
var appearance = item.GetValue(0).Get(PdfName.AS);
// Holds current page number information and Location on the page (Rectangle Dimentions)
var position = formFields.GetFieldPositions(keyName);
var newCheckBoxFieldname = CreateNewCheckBox(position, stamper, keyName);
// set newly created check box value
if (appearance != null && appearance.ToString().ToLower().Equals("/yes"))
{
checkedCheckBoxNames.Add(newCheckBoxFieldname);
}
// List of Check Box field names to be removed
removableFiledNames.Add(keyName);
}
else
{
formFields.SetFieldProperty(keyName, "setflags", PdfAnnotation.FLAGS_PRINT, null);
formFields.SetFieldProperty(keyName, "setflags", PdfFormField.FF_EDIT, null);
formFields.SetFieldProperty(keyName, "textsize", 8.0f, null);
formFields.RegenerateField(keyName);
}
}
// Removing check boxes generaed with WkHtmlToPdf
foreach (var oldCheckBox in removableFiledNames)
{
formFields.RemoveField(oldCheckBox);
}
stamper.Close();
}
return checkedCheckBoxNames.Any() ? UpdateCheckBoxes(outputFilename, checkedCheckBoxNames) : outputFilename;
}
/// <summary>
/// Updating Check Boxe's status checked or not in input file
/// </summary>
private string UpdateCheckBoxes(string inputFilename, ICollection<string> checkedCheckBoxNames)
{
var outputFilename = CreateTempPdfFilename();
var pdfReader = new PdfReader(inputFilename);
using (var outputStream = new FileStream(outputFilename, FileMode.OpenOrCreate))
{
var stamper = new PdfStamper(pdfReader, outputStream);
var newformFields = stamper.AcroFields;
var newfiledNames = newformFields.Fields.Keys;
foreach (var keyName in newfiledNames)
{
if (!newformFields.GetFieldType(keyName).Equals(AcroFields.FIELD_TYPE_CHECKBOX)) continue;
if (!checkedCheckBoxNames.Contains(keyName)) continue;
newformFields.SetField(keyName, "On");
}
stamper.Close();
}
return outputFilename;
}
/// <summary>
/// Constructing new check box
/// </summary>
/// <param name="fieldPosition"></param>
/// <param name="stamper"></param>
/// <param name="keyName"></param>
/// <returns>new field name</returns>
private string CreateNewCheckBox(IList<AcroFields.FieldPosition> fieldPosition, PdfStamper stamper, string keyName)
{
var pageNumber = fieldPosition.First().page;
var locationRectangle = fieldPosition.First().position;
PdfContentByte canvas = stamper.GetOverContent(1);
// Create array with two appearances
var onOff = new PdfAppearance[2];
onOff[0] = canvas.CreateAppearance(15, 15);
onOff[0].Rectangle(1, 1, 13, 13);
onOff[0].Stroke();
onOff[1] = canvas.CreateAppearance(15, 15);
onOff[1].SetRGBColorFill(255, 128, 128);
onOff[1].Rectangle(1, 1, 13, 13);
onOff[1].FillStroke();
onOff[1].MoveTo(1, 1);
onOff[1].LineTo(14, 14);
onOff[1].MoveTo(1, 14);
onOff[1].LineTo(14, 1);
onOff[1].Stroke();
var newFieldName = keyName + "_G";
// Creates new check boxes
var checkbox = new RadioCheckField(stamper.Writer, locationRectangle, newFieldName, "On");
var field = checkbox.CheckField;
// Add check box field to writer
stamper.AddAnnotation(field, pageNumber);
return newFieldName;
}
This is my solution using CSS pseudo elements:
<style>
label.checkbox:before {
content: "☐ ";
}
label.checkbox.checked:before {
content: "☑ ";
}
</style>
<label class="checkbox checked">Yes</label>
<label class="checkbox">No</label>