- Home>
 - .NET core>
 - Dynamically adjust font size of text to fit into a PDF text field using iTextSharp.LGPLv2.Core.
 
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.
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;
        }
 /// <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;
        }
 private float ComputeValueWidth([Required] string value, [Required] float targetFontSize)
        {
            float valueWidth = BaseFont.CreateFont().GetWidthPoint(value, targetFontSize);
            return valueWidth;
        }
        /// <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.
 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.
Calculating the maximum string length that fits in a PDF’s form textbox using iText
Rendering a PDF in angular that works with mobile browsers using ng2-pdf-viewer
Fill out a PDF form using iTextSharp for .NET core.
Web scraping in C# using HtmlAgilityPack
Building multitenant application – Part 2: Storing value into database session context from ASP.NET core web API
Common frameworks, libraries and design patterns I use
Build and deploy a WebJob alongside web app using azure pipelines
Authenticate against azure ad using certificate in a client credentials flow
Notes on The Clean Architecture