Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cfdocument prevent page breaks mid-row

Tags:

coldfusion

I know this has been asked before but the solutions are from 2.5+ years ago so I'm asking if anyone has devised or knows of a more elegant solution to the problem using CF9. Can anyone confirm if CF10 supports the "page-break-inside: avoid" rule?

How can I prevent page-break in CFDocument from occuring in middle of content?

COLDFUSION: cfdocument and forcing a pagebreak

This is pretty much how I'm doing it. I've estimated, depending on what type of page it is, I can fit 9 or 11 rows of data before having to force a page break. Of course this is prone to breaking so if anyone knows of any evolution of the solution I would be grateful.

like image 726
genericHCU Avatar asked Nov 04 '22 17:11

genericHCU


1 Answers

I believe I have found a pseudo solution. It is basically just what I said in the comments above. I take a best guess and see if it fits using the value of cfpdf's getInfo.totalPages. If it fits, great, merge it to the final document, if it doesn't, try again with one less row.

The downside to doing it this way is that it slows it down a bit and you can't use some of the stuff cfdocument makes easy like like messing with headers and footers. That being said, part 2 of this solution may be to record the number of rows that fit on a page in an array instead of merging the pages and rebuild the entire document again using cfdocument and those values as the loop constraints forcing a page break after. As it is, the below solution is already a little time consuming so building it again inside of a cfdocument tag may not work in high traffic sites.

Bug workaround: It looks like there is a bug with cfdocument that removes the background colors when saving the document to memory with the name attribute. The workaround is to remove the cfdocument tag to an external file. I saw one programmer placed it into a cfc, I found it's possible to use a simple cfinclude.

I hope someone finds this helpful, and if you know a better way to do this please comment.

<cfset reviewText = "Lorem ipsum dolor sit amet, + lots of characters.">
<cfset estimatedRowsPerPage = 7> <!--- This is the max number of records you want to try on each page.  The larger the gap between max and actual will slow down the process. Used to reset attemptedRowsPerPage if the value changes --->
<cfset attemptedRowsPerPage = estimatedRowsPerPage> <!---- number of rows attempted to add to the page --->
<cfset totalRowsOutput = 0><!--- this is the number of records successfully saved to the final PDF --->
<cfset recordCount = 20> <!--- this is the query's record count --->
<!--- cfpdf cannot create a file from scratch and cfdocument requires some content so a container object cannot be created without at least one page. This page will be deleted later --->
<cfdocument format="pdf" marginbottom=".25" margintop=".25" marginleft=".25" marginright=".25" name = "finalDocument">Delete me</cfdocument>
<cfloop condition="totalRowsOutput lt recordCount">
    <!--- create what *should* be a single page document --->
    <cfdocument format="pdf" marginbottom=".25" margintop=".25" marginleft=".25" marginright=".25" name = "testDocument">
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml">
            <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <title>A title</title>
            </head>
            <body>
                <table border="1">
                    <tr>
                        <td>Row:</td>
                        <td>Title:</td>
                        <td>Author:</td>
                        <td>Price:</td>
                        <td>Average Rating:</td>
                        <td>Reviews:</td>
                    </tr>
                    <cfoutput>
                    <cfloop from = "1" to = "#attemptedRowsPerPage#" index = "i">
                        <tr>
                            <td>
                                #i#
                            </td>
                            <td nowrap="nowrap">
                                #mid(reviewText,1,randRange(4,10))#
                            </td>
                            <td nowrap="nowrap">
                                #mid(reviewText,20,randRange(8,20))#
                            </td>
                            <td>
                                $10.00
                            </td>
                            <td>
                                #randRange(1,5)#
                            </td>
                            <td>
                                #mid(reviewText,1,randRange(10,700))#
                            </td>
                        </tr>
                    </cfloop>
                    </cfoutput>
                </table>
            </body>
        </html>
    </cfdocument>
    <!--- get the document info to see if the page count = 1 --->
    <cfpdf action="getinfo" source="testDocument" name="testInfo">
    <cfif testInfo.totalPages gt 1>
        <!--- if the page count is greater than 1 we need to try again with one less record. --->
        <cfset attemptedRowsPerPage -= 1>
    <cfelse>
        <!--- merge the new single page to the final document --->
        <cfpdf action = "merge" name = "finalDocument">
            <cfpdfparam source="finalDocument">
            <cfpdfparam source="testDocument">
        </cfpdf>
        <cfset totalRowsOutput += attemptedRowsPerPage>
        <!--- if the page count = 1, we need to increment the startAttempt and reset the attemptedRowsPerPage unless attemptedRowsPerPage = recordCount --->
        <cfif totalRowsOutput lt recordCount>
            <!--- don't try to output more than exist --->
            <cfset attemptedRowsPerPage = estimatedRowsPerPage+totalRowsOutput lt recordCount ? estimatedRowsPerPage : recordCount-totalRowsOutput>
        </cfif>
    </cfif>
</cfloop>
<!--- delete the manditory page needed to create our final document --->
<cfpdf action="deletePages" pages="1" source="finalDocument" name="finalDocument">
<!--- see "http://www.raymondcamden.com/index.cfm/2007/7/12/ColdFusion-8-Working-with-PDFs--A-Problem" to see why you need toBinary --->
<cfcontent type="application/pdf" variable="#toBinary(finalDocument)#">
like image 184
genericHCU Avatar answered Nov 13 '22 02:11

genericHCU