Editable fields on PDF document from DOCX

Hi,
I’m using your software just for basic DOCXtoPDF conversions.
Is it possible to get a PDF with editable fields (where the user can insert his own stuff and then save the final version) starting from a DOCX and going through format conversion ?

Any help will be appreciated
Regards
Luigi

Hi Luigi,

Unfortunately, GemBox.Document currently doesn’t support exporting interactive form fields to PDF.
However, note that we do have plans to support this and I have added your report to our backlog ticket in which we keep track of this feature’s priority.

But at this moment I cannot tell you exactly when it will be implemented. Please note that we prioritize feature request implementations by the number of users requesting them and currently we are working on some other features that have greater priority (more user requests).

Nevertheless, for now as a workaround perhaps you could try the following:

  • Replace the Field elements in the DocumentModel with the Picture elements of specific metadata.
  • Save the DocumentModel to a PDF file and then load it into a PdfDocument using GemBox.Pdf.
  • Find the PdfImageContent elements of specific metadata and replace them with one of the PdfInteractiveForm types of elements.

Something like this:

var document = DocumentModel.Load("Input.docx");

var imageOptions = new GemBox.Document.ImageSaveOptions(GemBox.Document.ImageSaveFormat.Png);
var fieldsMapping = new Dictionary<string, FormFieldData>();

foreach (var fieldData in document.Content.FormFieldsData)
{
    var field = fieldData.Field;

    // Create a TextBox that will contain only a copy of the current Field element,
    // its size will be auto-fitted.
    var tempTextBox = new TextBox(document, Layout.Inline(new Size(0, 0)));
    tempTextBox.TextBoxFormat.InternalMargin = new Padding(0);
    tempTextBox.TextBoxFormat.WrapText = false;
    tempTextBox.TextBoxFormat.AutoFit = TextAutoFit.ResizeShapeToFitText;

    var tempParagraph = new Paragraph(document, field.Clone(true));
    tempParagraph.ParagraphFormat.LineSpacing = 1;
    tempParagraph.ParagraphFormat.LineSpacingRule = LineSpacingRule.AtLeast;
    tempParagraph.ParagraphFormat.SpaceAfter = 0;
    tempParagraph.ParagraphFormat.SpaceBefore = 0;

    tempTextBox.Blocks.Add(tempParagraph);

    // Save a TextBox as an image.
    var imageStream = new MemoryStream();
    tempTextBox.FormatDrawing().Save(imageStream, imageOptions);

    // Replace the current Field element with its image representation.
    var picture = new Picture(document, imageStream);
    string guid = Guid.NewGuid().ToString();
    picture.Metadata.Title = guid;
    field.Content.Start.InsertRange(picture.Content);
    field.Content.Delete();

    // Map the GUID value with the original Field element,
    // I'll use the Picture.Metadata.Title information to identify these images in the output PDF.
    fieldsMapping.Add(guid, fieldData);
}

document.Save("Temp.pdf");

using (var pdf = PdfDocument.Load("Temp.pdf"))
{
    foreach (var page in pdf.Pages)
    {
        var imagesMapping = new Dictionary<string, (PdfImageContent Image, PdfQuad Bounds)>();

        // Iterate through PdfPage elements and find the PdfImageContent elements
        // that are the image representations of the Field elements.
        var enumerator = page.Content.Elements.All(page.Transform).GetEnumerator();
        while (enumerator.MoveNext())
        {
            var element = enumerator.Current;
            if (element.ElementType != PdfContentElementType.Image)
                continue;

            var imageElement = (PdfImageContent)element;
            string metadata = imageElement.Image.Metadata?.Value;
            if (metadata == null)
                continue;

            var xml = new XmlDocument();
            xml.LoadXml(metadata);
            var xmlManager = new XmlNamespaceManager(xml.NameTable);
            xmlManager.AddNamespace("dc", "http://purl.org/dc/elements/1.1/");

            var xmlNode = xml.SelectSingleNode("//dc:title", xmlManager);
            string guid = xmlNode?.InnerText;
            if (guid != null && fieldsMapping.ContainsKey(guid))
            {
                var bounds = imageElement.Bounds;
                enumerator.Transform.Transform(ref bounds);

                // Map the GUID value with the image elements.
                imagesMapping.Add(guid, (imageElement, bounds));
            }
        }

        // Replace image elements with form fields.
        var pdfFields = pdf.Form.Fields;
        foreach (var item in imagesMapping)
        {
            var imageData = item.Value;
            var fieldData = fieldsMapping[item.Key];

            switch (fieldData.Field.FieldType)
            {
                case FieldType.FormText:
                    var textFieldData = (FormTextData)fieldData;
                    var pdfTextField = pdfFields.AddText(page,
                        imageData.Bounds.Left,
                        imageData.Bounds.Bottom,
                        imageData.Bounds.Right - imageData.Bounds.Left,
                        imageData.Bounds.Top - imageData.Bounds.Bottom);

                    pdfTextField.Name = textFieldData.Name;
                    pdfTextField.Value = textFieldData.ValueOrDefault?.ToString();
                    // Perhaps set the "pdfTextField.Appearance" as well...
                    break;

                case FieldType.FormCheckBox:
                    var checkBoxFieldData = (FormCheckBoxData)fieldData;
                    var pdfCheckBoxField = pdfFields.AddCheckBox(page,
                        imageData.Bounds.Left,
                        imageData.Bounds.Bottom,
                        imageData.Bounds.Right - imageData.Bounds.Left,
                        imageData.Bounds.Top - imageData.Bounds.Bottom);

                    pdfCheckBoxField.Name = checkBoxFieldData.Name;
                    pdfCheckBoxField.Checked = checkBoxFieldData.ValueOrDefault;
                    // Perhaps set the "pdfCheckBoxField.Appearance" as well...
                    break;

                case FieldType.FormDropDown:
                        // TODO
                    break;
            }

            imageData.Image.Collection.Remove(imageData.Image);
        }
    }

    pdf.Save("Output.pdf");
}

I hope this works for you.

Regards,
Mario