- 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