Coldfusion creating reports with wkhtmltopdf - coldfusion

I am trying to get some assistance with wkhtmltopdf.
I have downloaded and installed and trying to get it to work correctly with my program.
What I have is a form that lets the user choose print, preview or excel. When the user chooses print I want pop up the html table that is created by a query depending on the associates and locations that are chosen.
Right now when the user chooses print it just shows this table in the browser.
<cfif FORM.Format IS "print">
<!---<cfdocument format="pdf" scale="75" backgroundvisible="yes" overwrite="no" fontembed="yes">--->
<link rel="stylesheet" href="css/form-table.css"/>
<!---<cfdocumentitem type="header" >
<cfoutput><p align= "right">Page #cfdocument.currentpagenumber# of #cfdocument.totalpagecount#</p></cfoutput>
</cfdocumentitem> --->
<div class="RTable">
<h3 class="RTable-h3">CHECKLIST STATS</h3>
<cfoutput>
<ul class="RTable-headingList">
<li>FROM <span class="RTable-headingList-date">#dateFormat(date1, 'mm/dd/yyyy')#</span> TO <span class="RTable-headingList-date">#dateFormat(date2, 'mm/dd/yyyy')#</span></li>
<li>LOCATIONS: <span class="RTable-headingList-locations">#locList#</span></li>
</ul>
</cfoutput>
<table class="table table-hover RTable-table">
<thead>
<tr>
<th>Associate Name</th>
<th>Location</th>
<th><small>Generated by</small>Associate</th>
<th><small>Generated by</small>Selected Location(s)</th>
<th><small>Associate Percentage of</small>Location Total</th>
</tr>
</thead>
<tbody>
<cfoutput query="GetEmployeeInfo">
<tr>
<td class="RTable-name"><cfif rnA EQ 1><strong>#assoc_name#</strong></cfif></td>
<td class="RTable-location"><cfif rnL EQ 1>#trans_location#</cfif></td>
<td>#checklistsByAssocLoc#</td>
<td>#assocChecklistsByLoc#</td>
<td>#DecimalFormat(totalChecklistsByAssocLocPct)# %</td>
<!---<td> rnA: #rnA# | rnL: #rnL# | rnTotAssoc: #rnTotAssoc# </td> --->
</tr>
<cfif rnTotAssoc EQ 1>
<tr class="RTable-row-associate-total">
<td>Associate Total</td>
<td></td>
<td>#totalChecklistsByAssoc#</td>
<td>#totalAssocChecklistsByAllFilteredLoc#</td>
<td>#DecimalFormat(totalChecklistsByLocPct)# %</td>
</tr>
</cfif>
</cfoutput>
</tbody>
</table>
</div>
<!---</cfdocument>--->
I am trying to use it like cfdocument in the <cfif FORM.Format IS "print"> do I cfexecute this table some how in replace of how I have the table? I am using this as reports and dont want to save a million reports to the server. I guess I am looking for some assistance in getting off on the right foot. Any help would be greatly appreciated.
Iv tried adding this code inside the if "print":
<cfexecute name="C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe"
arguments="http://path/path/checklist/wkhtmltestpage.cfm C:\temp\wkhtmlTest.pdf"
errorVariable="errorMessage"
timeout="10"
/>
But this does not download it to show the user to print.
Also for some reason its not going to the link I am specifying its making a pdf of the login page...

When WKHTMLTOPDF requests the webpage, the "user" is not authenticated and the CGI.Http_User_Agent contains "wkhtmltopdf". The remote IP of the request will also be a local IP that is configured on the server. As a result, I don't directly process any CFM scripts using WKHTMLTOPDF.
I recommend:
Use CFContent to capture generated static HTML.
Save the HTML to a public-accessible directory with a random file name.
Use the web path + random file name w/WKHTMLTOPDF command line.
After PDF is generated, delete random files.
This process will make any issues easier to troubleshoot. I prefer creating static BAT files with the same randomized filename so that I can manually re-run on the server or even locally on my on PC to further troubleshoot and view any weird messages returned by the program. There's many other command line settings you'll want to pass (margins, orientation, pagesize, header/footer, JS delay, etc), so creating a standalone BAT file with all of the command line arguments is the best approach. (I noticed in another question that you may be using CFX_Exec. I recommend using it over CFExecute.)
<!--- Simple WKHTMLTOPDF Generation Usage --->
<cfset FileID = CreateUUID()>
<cffile action="WRITE" file="#webroot#\#FileID#.htm" output="#TheHTML#">
<cfexecute name="C:\wkhtmltopdf.exe" arguments="http://mywebsite.com/#FileID#.htm C:\temp\#FileID#.pdf" timeout="30">
<cffile action="DELETE" file="#webroot#\#FileID#.htm">
To deliver the PDF file, you can either perform a 302 redirect to the randomized PDF file and let your webserver handle the mimetype (and then delete it later) or use CFContent to deliver it using a ColdFusion thread and automatically delete it:
<!--- Return PDF to browser inline or as downloadable attachment --->
<!--- <cfheader name="content-disposition" value="attachment; filename=""NiceFilename.pdf"""> --->
<cfheader name="content-disposition" value="inline; filename=""NiceFilename.pdf""">
<cfcontent type="application/pdf" file="C:\temp\#FileID#.pdf" deletefile="Yes">

Related

How to store an image file after uploading the image file in ColdFusion

I am fairly new to Coldfusion and I am having a hard time to figure out how to allow users to upload images in my form
Currently, I was able to find the following code that will upload the image in Coldfusion:
<cfparam name="form.fileUpload" default="">
​
<cfif len(trim(form.fileUpload))>
<cffile action="upload"
fileField="fileUpload"
destination="C:\docs">
<p>Thankyou, your file has been uploaded.</p>
</cfif>
​
<form enctype="multipart/form-data" method="post">
<input type="file" name="fileUpload" /><br />
<input type="submit" value="Upload File" />
</form>
Although it gives me an idea on how to approach my issue, I still not sure on the following:
Instead of the destination="C:\docs" storing the file at a drive, I would like to be able to upload the uploaded image to an email. The reason being is the once the user finishes and submits the form, an email will be sent out to the user who submitted the request and the user who will be assigned in creating the card.
How can I achieve this? Any suggestions and examples would be greatly appreciated
Use CFMAILPARAM with remove="yes". You can also show the filename in the email. Full example:
<cfmail
to="the#recipient.com"
subject="#application.companyName# Contact Submission"
type="html"
from="#form.email#">
<!--- WAS A FILE UPLOADED? --->
<cfif isDefined("form.attachment") and form.attachment neq ''>
<!--- SAVE THE FILE --->
<cffile action="upload" fileField="attachment" destination="#expandPath('./')#" nameConflict="Overwrite">
<!--- ATTACH TO EMAIL, THEN DELETE IT --->
<cfmailparam
disposition = "attachment"
file = "#expandPath('./' & cffile.serverfile)#"
remove = 'yes'>
<!--- MAKE THE FILE NAME A FORM FIELD --->
<cfset form.attachment = cffile.serverfile>
</cfif>
<html>
<body>
<cfset form.URL = cgi.http_referrer>
<table border="1" cellspacing="0" cellpadding="3">
<tr>
<th>Field</th>
<th>Value</th>
</tr>
<cfloop list="#form.fieldnames#" index="f">
<tr>
<td nowrap="nowrap" valign="top">#replace(f, '_', ' ', 'all')#</td>
<td>#form[f]#</td>
</tr>
</cfloop>
</table>
</body>
</html>
</cfmail>

Coldfusion app generating blank PDF page

We had a server migration and since the migration, a Coldfusion application is generating blank pdf pages that are able to be saved locally and printed. The application queries a SQL Server database. I have been through the code several times and am pretty positive the data sources are all pointed to the new environment. I have a report from the day before the migration and all was well. I think the paths are fine because when I changed the path, the application blew up. I have minimal experience with Coldfusion and I am out of ideas at the moment. What else should I be looking at when debugging this application? Has any code been deprecated and the new environment will not run it? Is it possible there are settings on the new server preventing this PDF from being generated? Do I need to add a MIME somewhere that was unnecessary before? Any advice would be helpful. Below is some of the code being used.
<cfif #qSelect.Record# EQ 0>
<h1>Empty recordset</h1>
<cfelse>
<cfdocument filename="../Reports/CustomReport.pdf" format = "pdf" orientation = "landscape" overwrite="yes">
<h2>Database</h2>
<cfoutput>
<h4>Monthly Report </h4>
</cfoutput>
<table cellpadding="3" cellspacing="0" border="0">
<tr>
<th>Date</th>
<th>Status</th>
</tr>
<cfoutput query="qLocalEvents">
<tr>
<td>#DateFormat(qSelectEvents.EventDate, "mm/dd/yyyy")#</td>
<td>#qSelectEvents.Status#</td>
</tr>
</cfoutput>
</table>
</cfdocument>
</cfif>

cfdocument issue - cfdocument has no body

I am working with Coldfusion10 and am facing this error:
The following information is meant for the website developer for debugging purposes.
Error Occurred While Processing Request
cfdocument tag has no body.
It must have a body or a source file or URL.
I checked the website and detected that cfsettings is not defined at the top or anywhere which can cause this issue, I am using it as
<cfdocument format="pdf">
<cfdocumentsection>
<cfdocumentitem type="header"></cfdocumentitem> - Footer is used too
</cfdocumentsection>
I tried using evalAtPrint= true but still no success. Am I missing something here?
Make sure you are actually putting in at the end. I'm assuming you just missed this here.
Otherwise everything seems to align up with the Wiki Docs.
I would suggest two things.
Verify you are using ColdFusion 11 Update 3. Update 3 was a major update and may have addressed this issue.
If you are using update 3, open a bug at bugbase.adobe.com
The error message you included in your question indicates that there is no content between your <cfdocument> tags. The code that you included corroborates that. If this is not your actual code then we cannot be of much help.
You need to include the content that you would like to be converted into PDF between the <cfdocument> tags. You need something like this:
<cfquery datasource="cfdocexamples" name="empSalary">
SELECT Emp_ID, firstname, lastname, e.dept_id, salary, d.dept_name
FROM employee e, departmt d
WHERE e.dept_id = d.dept_id
ORDER BY d.dept_name
</cfquery>
<cfdocument format="PDF">
<cfoutput query="empSalary" group="dept_id">
<cfdocumentsection>
<cfdocumentitem type="header">
<font size="-3"><i>Salary Report</i></font>
</cfdocumentitem>
<cfdocumentitem type="footer">
<font size="-3">Page #cfdocument.currentpagenumber#</font>
</cfdocumentitem>
<h2>#dept_name#</h2>
<table width="95%" border="2" cellspacing="2" cellpadding="2" >
<tr>
<th>Employee</th>
<th>Salary</th>
</tr>
<cfset deptTotal = 0 >
<!--- inner cfoutput --->
<cfoutput>
<tr>
<td>
<font size="-1">#empSalary.lastname#, #empSalary.firstname#</font>
</td>
<td align="right">
<font size="-1">#DollarFormat(empSalary.salary)#</font>
</td>
</tr>
<cfset deptTotal = deptTotal + empSalary.salary>
</cfoutput>
<tr>
<td align="right"><font size="-1">Total</font></td>
<td align="right"><font size="-1">#DollarFormat(deptTotal)#</font></td>
</tr>
<cfset deptTotal = 0>
</table>
</cfdocumentsection>
</cfoutput>
</cfdocument>
Copied from the ColdFusion documentation here

cfdocument prevent page breaks mid-row

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.
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)#">
Travis - I don't know of another way to do this. Usually I create an HTML table as the core of each "page" and have a specific number of rows before I close the table, break the page and open a new table.

Using cfpdf to display pdf files

I have a page where I need to display a pdf and an "I agree" button.
Q: Do I use cfpdf to create a thumbnail of the pdf and display the resulting output?
Chapter 32 of cfWack 8 talks about using cfpdf.
I did this in the office once...It's not that sweet as it's presented in Adobe's whitepapers.
Here's my code:
<cfpdf source="#attachmentFilePath##attachmentFilename#" pages="1" imagePrefix="#prefix#" action="thumbnail" destination="#application.attachmentsFilePath#/_thumbs" format="jpg" overwrite="true" resolution="low" scale="80">
<cfset thumbURL = "http://127.0.0.1:9001/attachments/_thumbs/#prefix#_page_1.jpg">
<cfif fileExists(thumb)>
<tr>
<td width="100%">
<a href="attachmentView.cfm?attachNo=#attachNo#&act=download">
<img alt="#attachmentFilename#" src="#thumbURL#" width="500" />
</a>
</td>
</tr>
<cfelse>
<cfheader name="Content-Type" value="#attachmentMIMEType#">
<cfheader name="Content-Disposition" value="attachment; filename=#attachmentFilename#">
<cfcontent type="#attachmentMIMEType#" file="#attachmentFilePath##attachmentFilename#">
</cfif>
I also check isPDF() and putting all this in try/catch.
So what's wrong with this? It works with 30% of PDF users are trying to preview so in catch I display link for download instead of image :(