9

I have created a VF page rendered as pdf for QuoteLineItems in which I have added the list inside <apex:repeat> tags inside a html table row which again is inside another table.The problem however is that when the list includes more elements than the page can hold, the table borders gets streched and the remaining rows are displayed on the next page. This is my code for rendering the list

                   <table>
                   <tr>
                   <td>
                   <table>
                   <apex:repeat value="{!listQuoteLineItems}" var="product">
                        <tr style="page-break-inside:avoid;">
                            <td>{!product.Product2.Name}</td>
                            <td>{!product.Quantity}</td>
                            <td>{!product.TotalPrice}</td>
                        </tr>
                   </apex:repeat>
                   </table>
                   </td>
                   </tr>
                   </table>

I know i am supposed to use html page-break properties but not sure which and where! I need to display the tables w/o the borders being streched.I hope it is possible to display records partly on both the pages in such case.Please help in any way possible!!

2 Answers 2

12

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.

2
  • 3
    The -fs would seem to indicate this is a style tag specific to the flying saucer PDF render engine used by Salesforce. Thanks for sharing it.
    – crmprogdev
    Commented Jun 5, 2015 at 15:24
  • Praise you, Sir!
    – user
    Commented Mar 8, 2018 at 18:41
4

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&nbsp;: 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 =&nbsp;:id limit 1]; 
    quoteLineItems = quote.Quote_Lines__r;  
    for ( SFDC_520_QuoteLine__c ql&nbsp;: 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.

1
  • Thank you crmprogdev ! Though I had gone through the link earlier, I wasnt sure it was the same thing they wanted to achieve.Anyways thanks!! Commented Jun 3, 2015 at 5:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .