Quote of the Day

more Quotes

Categories

Buy me a coffee

  • Home>
  • .NET core>

Dynamically adjust font size of text to fit into a PDF text field using iTextSharp.LGPLv2.Core.

Published January 23, 2021 in .NET core - 0 Comments

In one of the asp.net core projects I worked on, I used iTextSharp.LGPLv2.Core to programmatically fill out a PDF form. Sometimes, we have a string that is too long to fit within the rectangle area of the text field. After a bit of googling, I found and adopted the solution in this StackOverFlow to solve this problem by dynamically compute the appropriate font size I can use so that the value can fit within the field.

The logic is fairly straightforward.

  1. Find the current font size and width of the text field.
  2. Compute the width of the value using the current font size as the starting point.
  3. Keep reducing the font size until the width of the value is less than or equal to the width of the text field.
  4. Set the font size of the text field to the final font size.

Find the width of a text field

A PDF text field is basically a rectangular container and we can compute the width using the left and right positions of the container on the page, as the below snippets demonstrate.

     /// <summary>
        /// Helper method to compute the width of a field's container 
        /// </summary>
        /// <param name="acroFields">The acro fields representing the PDF.</param>
        /// <param name="name">The name of the field.</param>
        /// <returns>The container's width or null if the data is not available</returns>
        private float? ComputeFieldWidth(AcroFields acroFields, string name)
        {
            float[] fieldPositions = acroFields.GetFieldPositions(name);
            if (fieldPositions != null && fieldPositions.Length == 5)
            {
                // the values are: [page, llx, lly, urx, ury]
                float urx = fieldPositions[3];
                float llx = fieldPositions[1];
                return urx - llx;
            }
            return null;
        }

Find the font size of a text field

 /// <summary>
        /// Helper method to compute the font size of a field.
        /// </summary>
        /// <param name="acroFields">The acro fields representing the PDF.</param>
        /// <param name="fieldName">The name of the field for lookup.</param>
        /// <returns>The font size attribute of the field. </returns>
        private float GetFontSizeForField(AcroFields acroFields, string fieldName)
        {
            AcroFields.Item fieldItem = acroFields.GetFieldItem(fieldName);
            PdfDictionary mergedFieldAttributes = fieldItem.GetMerged(0);
            TextField tmp = new TextField(null, null, null);
            acroFields.DecodeGenericDictionary(mergedFieldAttributes, tmp);
            return tmp.FontSize;
        }

Find the width of a value based on a font size

 private float ComputeValueWidth([Required] string value, [Required] float targetFontSize)
        {
            float valueWidth = BaseFont.CreateFont().GetWidthPoint(value, targetFontSize);
            return valueWidth;
        }

Find the appropriate font size to fit a value into a text field

        /// <summary>
        /// Compute the font size to ensure a given value would fit within 
        /// a given field's container.
        /// </summary>
        /// <param name="acroFields">The acro fields representing the PDF.</param>
        /// <param name="fieldModel">The model representing the name and value 
        /// of the field. 
        /// </param>
        /// <returns>A font size that would ensure the value fit within the 
        /// container.
        /// </returns>
        /// Note: The calculation assuming the font is the default font (Helveltica) 
        private float ComputeFontSizeToFitValueInFieldContainer(AcroFields acroFields, [Required] PdfFormFieldModel fieldModel)
        {
            // adjust the size to fit within the field 
            // https://stackoverflow.com/questions/27068721/calculating-the-maximum-string-length-that-fits-in-a-pdfs-form-textbox-using-it
            float fontSize = GetFontSizeForField(acroFields, fieldModel.Name);
            if (string.IsNullOrEmpty(fieldModel.Value))
            {
                // value is not given, so we just return the original font
                // default size set for the field. 
                return fontSize;
            }
            float valueWidth = ComputeValueWidth(fieldModel.Value, fontSize);
            float? fieldWidth = ComputeFieldWidth(acroFields, fieldModel.Name);
            if (fieldWidth.HasValue && valueWidth > fieldWidth.Value)
            {
                // reduce the font size until fit. 
                do
                {
                    valueWidth = ComputeValueWidth(fieldModel.Value, --fontSize);
                } while (valueWidth > fieldWidth.Value);
            }
            return fontSize;
        }

In the above snippets, PDFFormFieldModel is just a POCO encapsulating the name of the text field and the value we want to set.

Updating the font size of a text field

 public virtual Stream FillPdf(PDFForm<T> formFillingRequest)
        {
            Stream inStream = null;
            Stream outStream = null;
            PdfReader pdfReader = null;
            PdfStamper pdfStamper = null;
            try
            {
                inStream = new FileStream(GetPdfFormFilePath(), FileMode.Open, FileAccess.Read);
                outStream = new MemoryStream();
                pdfReader = new PdfReader(inStream);
                pdfStamper = new PdfStamper(pdfReader, outStream);
                AcroFields form = pdfStamper.AcroFields;
                foreach (KeyValuePair<string, PdfFormFieldModel> entry in ToFormDict(formFillingRequest.Content as T))
                {
                    AcroFields.Item fieldItem = form.GetFieldItem(entry.Key);
                    if (fieldItem != null)
                    {
                        // field exists 
                        PdfFormFieldModel fieldModel = entry.Value;
                        if (fieldModel.IsVisible)
                        {
                            if (!IsMultiField(fieldItem))
                            {
                                // adjust the size to fit within the field 
                                // https://stackoverflow.com/questions/27068721/calculating-the-maximum-string-length-that-fits-in-a-pdfs-form-textbox-using-it
                                if (!string.IsNullOrEmpty(fieldModel.Value))
                                {
                                    float fontSize = ComputeFontSizeToFitValueInFieldContainer(form, fieldModel);
                                    // need to use object ref since there is an 
                                    // overload method of SetFieldProperty that 
                                    // accept a float
                                    object targetSize = fontSize;
                                    form.SetFieldProperty(fieldModel.Name, "textsize", targetSize, null);
                                }

                            }
                            form.SetField(fieldModel.Name, fieldModel.Value);
                        }
                        else
                        {
                            form.RemoveField(fieldModel.Name);
                        }
                    }
                }

                pdfStamper.FormFlattening = true;
                return outStream;
            }
            finally
            {
                pdfStamper?.Close();
                pdfReader?.Close();
                inStream?.Close();
            }
        }

In the above snippets, PDFForm encapsulates the data we use to fill out the form specific to our needs. The code reads in a PDF form, goes through each field in the form, adjust the font size if necessary and set the value for the field.

References

Calculating the maximum string length that fits in a PDF’s form textbox using iText

iTextSharp.LGPLv2.Core nuget package

No comments yet