Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Best fit of text length while printing PDF templates

5.00/5 (3 votes)
24 May 2010CPOL2 min read 19.5K  
Best fitting the text fields while adding text to the PDF files to save page space.

In one of my projects we are using PDF templates for the generating the PDF files. Like we create a template first with the textboxes on it and later we can use the template with filling the dynamic data and saving the resulting file. The issue was with the dynamic data not fitting in to the textboxes when the data from database is in Capital Letters. As we were not using the mono spaced fonts like courier. Mono spaced font takes equal number of points while printing on screen or paper say 8X12 pixels for all characters it may be ‘I’ or ‘W’. But non mono spaced font will take different width for different characters to save space, like ‘I’(may be say 3 pixels) will take less width on paper or screen than ‘W’(say 12pixels). And also capital letters take more width than small case. Got it?



There are more than 10 textboxes on my PDF for a paragraph like content to show on the PDF to look like lines of running text. Initially I set the line length to some 100 characters for each line in my code like if the text to show is 400 characters I was displaying in 4 lines. My issue was it is giving space on right side of the PDF textbox when text is in small letters. Then I increased number of characters to show from 100 to 120. Then the issue was with the uppercase text it was concatenated as the textbox width on the PDF form is limited and cannot increase.



The solution is to calculate the best fit of text length at runtime instead hard coding to 100 or 120. When the text is in uppercase the length can be calculated to 90 or so. And when the text is in lower case the length can be 120 characters.




Case Study: We are using iTextSharp (an open source project from SourceForge), there is a mothod GetEffectiveStringWidth() in the PdfContentByte class. This function will calculate how many points will the text take on the PDF at runtime based on the supplied font.



Before (here I added Cinederella story ... :) ) –


GetNextLine() is my own method (read at the end if you are interested) which returns the next line to display for the supplied length to maintain the text into readable way by keeping the words to-gether as when using Substring may split single word into lines, like 'letter' can be splitted to 'let' at the end of first line and 'ter' in second line.


C#
PdfReader pdfformreader=new PdfReader(strPdfTempPath);
PdfStamper pdfformstamper=new PdfStamper(pdfformreader,new FileStream(strPdfNewPath,FileMode.Create));

PdfContentByte cb = pdfformstamper.GetOverContent(2);
cb.SetFontAndSize(FontFactory.GetFont("Arial").GetCalculatedBaseFont(false), 8f);
//i am printing the text strRemarksSummary in to 12 lines
for(int ind = 0 ; ind < 12 ; ind++)
{
   string strNextRemarks = PermitPdfForm.GetNextLine( ref strRemarksSummary, 125); // fixed length of 125 
   if( strNextRemarks.Equals("EndOfContent"))
   {
       break;
   }
   else
   {
       pdfformfields.SetField( "txtRemarks" + (ind+1) , strNextRemarks);
   }
}


After, instead of hard coding to 125 use -



MIDL
string strNextRemarks = PermitPdfForm.GetNextLine( ref strRemarksSummary, //125);
                        PermitPdfForm.GetEffectiveTextLength(
                        ref strRemarksSummary, cb,
cb.PdfDocument.PageSize.Width - 70)); // textbox width on the pdf

public static int GetEffectiveTextLength(
            ref string str, PdfContentByte cb, float intTextBoxWidth)
{
    int intCharIndex = 0;
    while( true)
    {
        string strTemp = str.Substring(0,intCharIndex);
        float fltLength = cb.GetEffectiveStringWidth(strTemp,false);
        if ( fltLength > intTextBoxWidth)
        {
            return intCharIndex - 1;
        }
        else
        {
            intCharIndex++;
            if( intCharIndex >= str.Length)
                return str.Length;
        }
        //set to maximum of 1000 loop only to avoid infinite loop
        if( intCharIndex > 1000)
            return str.Length;
    }
}


C#
public static string GetNextLine(
    ref string strContent, int intLineMaxLength)
{
    if ( strContent == null ||
        strContent.Length == 0)
    {
        return "EndOfContent";
    }

    string strReturn = "";

    if( strContent.IndexOf('\n') != -1 &&
        strContent.IndexOf('\n') < intLineMaxLength)
    {
        strReturn = strContent.Substring( 0, strContent.IndexOf('\n'));
        strContent = strContent.Substring( strContent.IndexOf('\n') + 1);
        return strReturn;
    }

    if ( strContent.Length <= intLineMaxLength)
    {
        strReturn = strContent;
        strContent = "";
    }
    else
    {
        int intLastCharIndex = intLineMaxLength;
        while( true)
        {
            if ( Char.IsWhiteSpace( strContent[intLastCharIndex]))
            {
                strReturn = strContent.Substring( 0, intLastCharIndex);
                strContent = strContent.Substring( intLastCharIndex + 1).Trim();
                break;
            }
            else
            {
                intLastCharIndex--;
                if( intLastCharIndex == 0)
                {
                    strReturn = strContent.Substring( 0, intLineMaxLength);
                    strContent = strContent.Substring( intLineMaxLength + 1).Trim();
                    break;
                }
            }
        }
    }
    return strReturn;
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)