JacksonHaenchen
JacksonHaenchen

Reputation: 1477

iTextSharp: Setting text on textfield that is on a table cell results in text being too wide and squashed

I think this might be a bug, but if anyone can help I'd appreciate it. I currently have another question open that deals with a similar issue, but I think this question better exemplifies the problem, and more simply too. That being said I don't want to delete the old one in case it increases my wait time. I yield to the mods to decide which question is better.

Here's a sample application that creates a pdf, then a table. It adds a cell to the table and then ties a fieldpositioningevent to the cell event.

using System;
using System.Diagnostics;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace iTextSharpTextBoxInTableCell
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create a PDF with a TextBox in a table cell
            BaseFont bfHelvetica = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1250, false);
            Font helvetica12 = new Font(bfHelvetica, 12, Font.NORMAL, BaseColor.BLACK);

            Document doc = new Document(PageSize.LETTER, 18f, 18f, 18f, 18f);
            FileStream fs = new FileStream("TextBoxInTableCell.pdf", FileMode.Create);
            PdfWriter writer = PdfWriter.GetInstance(doc, fs);

            doc.Open();
            PdfPTable myTable = new PdfPTable(1);
            myTable.TotalWidth = 568f;
            myTable.LockedWidth = true;
            myTable.HorizontalAlignment = 0;

            TextField tf = new TextField(writer, new iTextSharp.text.Rectangle(67, 585, 140, 800), "cellTextBox");
            tf.Text = "test";
            PdfPCell tbCell = new PdfPCell(new Phrase(" ", helvetica12));
            iTextSharp.text.pdf.events.FieldPositioningEvents events =
                new iTextSharp.text.pdf.events.FieldPositioningEvents(writer, tf.GetTextField());
            tbCell.CellEvent = events;

            myTable.AddCell(tbCell);

            doc.Add(myTable);

            doc.Close();

            fs.Close();
            Process.Start("TextBoxInTableCell.pdf");
            Console.WriteLine("End Of Program Execution");
            Console.ReadLine();
        }
    }
}

Here's what this field looks like when it's generated: squashed

As you can see, the text is squashed. I've published the generated pdf here.

Upvotes: 0

Views: 3866

Answers (1)

Chris Haas
Chris Haas

Reputation: 55427

I'm definitely seeing what you're seeing and as @mkl said in your other post, the problem comes down to the appearance's BBOX entry not being set to the same size as the field. I can't really find too many examples of FieldPositioningEvents in the wild and the ones that do exist appear to be copy-and-paste's of each other for the most part.

Anyway, if you read the actual code for FieldPositioningEvents you'll see that it can be used for both page events as well as cell events which makes me think it was intended for broader purposes possibly, but that's just a guess on my part.

One solution is to just write your own subclass of IPdfPCellEvent. Below is an example of that that follows the example provided by FieldPositioningEvents however it is specific to TextFields since we're interested in setting the /BBOX entry. It has two constructors, one that works very similar to FieldPositioningEvents that takes a PdfWriter and a TextField and one that just takes the most commonly set properties of a TextFields and actually creates it for you. The CellLayout is part of the interface contract and actually figures out where the annotation should be drawn.

public class SingleCellFieldPositioningEvent : IPdfPCellEvent {

    public TextField Field { get; set; }
    public PdfWriter Writer { get; set; }
    public float Padding { get; set; }

    public SingleCellFieldPositioningEvent(PdfWriter writer, TextField field) {
        this.Field = field;
        this.Writer = writer;
    }

    public SingleCellFieldPositioningEvent(PdfWriter writer, string fieldName, string text = "", BaseFont font = null, float fontSize = 14 ) {
        //The rectangle gets changed later so it doesn't matter what we use
        var rect = new iTextSharp.text.Rectangle(1, 1);

        //Create the field and set various properties
        this.Field = new TextField(writer, rect, fieldName);
        this.Field.Text = text;
        if (null == font) {
            font = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
        }
        this.Field.Font = font;
        this.Field.FontSize = fontSize;

        this.Writer = writer;
    }

    public void CellLayout(PdfPCell cell, iTextSharp.text.Rectangle rect, PdfContentByte[] canvases) {
        //Create the field's rectangle based on the current cell and requested padded
        var newRect = new PdfRectangle(rect.GetLeft(Padding), rect.GetBottom(Padding), rect.GetRight(Padding), rect.GetTop(Padding));

        //Set the appearance's rectangle to the same as the box
        Field.Box = newRect.Rectangle;

        //Get the raw field
        var tf = this.Field.GetTextField();

        //Change the field's rectangle
        tf.Put(PdfName.RECT, newRect);

        //Add the annotation to the writer
        Writer.AddAnnotation(tf);
    }
}

You can use this in two different ways. Either manually create a field and set various properties:

//The rectangle is actually changed in the cell event so it doesn't matter what we use
TextField tf = new TextField(writer, new iTextSharp.text.Rectangle(1, 1), "cellTextBox");
tf.Text = "test";
tf.Font = bfHelvetica;
tf.FontSize = 14;
PdfPCell tbCell = new PdfPCell(new Phrase(" ", helvetica12));
tbCell.CellEvent = new SingleCellFieldPositioningEvent(writer, tf);

Or just pass the properties in:

PdfPCell tbCell = new PdfPCell(new Phrase(" ", helvetica12));
tbCell.CellEvent = new SingleCellFieldPositioningEvent(writer, "cellTextBox", "test", bfHelvetica, 14);
myTable.AddCell(tbCell);

Upvotes: 2

Related Questions