How to add page breaks to visual force page rendered as pdf so that a dynamically rendered list in apex repeat tag does not strech the table lines
I found an easy solution for the problem, no need to implement any logic for the same just included the following attribute -fs-table-paginate: paginate;
in tables style tag and worked like a miracle.
What you want to do typically requires a custom controller. There's an excellent tutorial in the Technical Library titled Creating Professional PDF Documents with CSS and Visualforce that describes exactly how how to do what you're asking about.
It basically involves composing your pages in the controller by counting the lines before they're sent to the page. Since the first is always different than the 2nd because of Company info and other details, the number of lines for the 1st page is less than subsequent pages and handled like below:
//controls how many quote line items are displayed on page 1
private static Integer FIRST_BREAK = 10;
//controls how many quote line items are displayed on subsequent pages
private static Integer SUBSEQ_BREAKS = 20;
public List<SFDC_520_QuoteLine__c[]> pageBrokenQuoteLines {get; private set; }
Using the above technique, the quote line items are prepared for printing using a controller like below:
//splits the quote lines into an approximate number of rows that can be
//displayed per page
private void prepareQuoteLinesForPrinting()
{
pageBrokenQuoteLines = new List<SFDC_520_QuoteLine__c[]>();
SFDC_520_QuoteLine__c[] pageOfQuotes = new SFDC_520_QuoteLine__c[]{};
Integer counter = 0;
boolean firstBreakFound = false;
boolean setSubSeqBreak = false;
Integer breakPoint = FIRST_BREAK;
for(SFDC_520_QuoteLine__c q : quoteLineItems)
{
if(counter <= breakPoint)
{
pageOfQuotes.add(q);
counter++;
}
if(counter == breakPoint)
{
if (!firstBreakFound)
{
firstBreakFound = true;
setSubSeqBreak = true;
}
counter = 0;
pageBrokenQuoteLines.add(pageOfQuotes);
pageOfQuotes.clear();
}
if(setSubSeqBreak)
{
breakPoint = SUBSEQ_BREAKS;
setSubSeqBreak = false;
}
}
//if we have finished looping and have some quotes left let's assign them
if(!pageOfQuotes.isEmpty())
pageBrokenQuoteLines.add(pageOfQuotes);
}
FIRST_BREAK
and SUBSEQ_BREAKS
are to split the array into smaller arrays which will fit on a page of the PDF quotation using the constructor defined below:
// constructor, loads the quote and any opportunity lines
void queryQuoteLines(id id) {
quote = [ Select s.Opportunity__r.Pricebook2Id, Quote_Amount_rollup__c,
(Select Unit_Price__c, Unit_Net_Price__c, ServiceDate__c,
Sales_Discount__c, Quote__c, Qty_Ordered__c, Product2__c,
Product2__r.Name, Name, Id, Ext_Price__c,
Ext_Net_Price__c, Ext_Price_tmp__c, Description__c From Quote_Lines__r
order by name ),
s.Opportunity__r.HasOpportunityLineItem, s.Opportunity__r.Name, s.Name,
s.Opportunity__r.Id, s.Opportunity__c From SFDC_520_Quote__c s
where s.id = :id limit 1];
quoteLineItems = quote.Quote_Lines__r;
for ( SFDC_520_QuoteLine__c ql : quoteLineItems ) {
ql.Ext_Price_tmp__c = ql.Ext_Net_Price__c;
if ( ql.Sales_Discount__c == null ) ql.Sales_Discount__c = 0.0;
}
prepareQuoteLinesForPrinting();
}
In code provided for this example, the page is constructed using a datatable, but an html table is actually the preferred way to do this like you're currently using. As such, you can ignore much of the style sheet. The important part of that code is the following:
page-break-after:always
Unfortunately, the above CSSS line used by itself won't solve your problem without precomposing your pages.
You can also number your pages and set the orientation using code like below:
@page {
/* Landscape orientation */
size:landscape;
/* Put page numbers in the top right corner of each
page in the pdf document. */
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
}
You'll find more details and a further explanation of how to use this code in the link provided above.