- 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
Stamp a PDF using iTextSharp for .NET core
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