how to generate pdf using Wicked-pdf after the page is loaded? - wicked-pdf

is there a way to create pdf using wicked pdf after the page is loaded?
i want to create pdf after loaded, because i create the content for pdf after the page is loaded using javascript.
<div id="content">
<!-- some content -->
</div>
I would like to create a pdf file using wicked pdf, but is there a way to create it after the page is loaded?
Thank you very much.

First off, you can probably just create the pdf before it loads, since wkhtmltopdf can process and execute javascript. You may need to adjust the :javascript_delay setting if it isn't completely finishing running the JS.
Otherwise, you may want to package up the html in #content#, and send it as a param to a PDF-creating endpoint, with something like this:
$(function(){
form = $('<form action="/pages/report.pdf"><input id="form_content" name="form_content"></form>');
html_content = $('#content').html();
$('#form_content').val(html_content);
form.submit();
});
And an endpoint something like this:
format.pdf do
#content = params[:form_content]
render :pdf => 'report.pdf'
end
and a view that uses #content:
<%= #content %>
This endpoint probably should not do double duty with an html view, since that injected content could open you up to CSRF or XSS issues.

Related

Generating PDFs in ColdFusion

I have an application for customer and product management that has many screens/pages. I would like certain pages to be printable and exportable to PDF.
To print a document, its a simple case of providing a CSS stylesheet for #Print media which strips a lot of the regular styling. However, when generating the PDF I noticed the <cfdocument> and <cfhtmltopdf> tags both want the an entire HTML page to render (including <head> tags).
This means that it doesn't recognise the purpose of the PDF as being for print and doesn't pick up the #Print styles. Currently I'm having to make a seperate makepdf.cfm page and repeat the HTML of the page I want to export along with including ONLY the print CSS stylesheet.
This seems a bit crazy because I'll have to update the makepdf.cfm page each and everytime I change a main application screen that can be printed/exported to pdf.
Is there a better way to achieving what I'm trying to do?
I am not sure how your CSS is organized, but I had a similar situation where I needed to print various content off the screen. My solution was to use an AJAX call (CFAJAXPROXY) in Javascript to a CFC in order to set a CF session variable with the DIV content and then call my print page which will output the content with the cfhtmltopdf tag. My experience has been that the CSS must be included in within whichever PDF generation tag you are using.
For example:
<div id="myContent">content on my page to print</div>
And a button to print
<button onclick="printContent('myContent')">Print</button>
The onclick calls the following in the Javascript:
var printContent = function(_contentDiv){
var _content = $('#' + _contentDiv).html();
var _setContent = _cfc.setContent(_content);
window.location.href = 'printPage.cfm';
}
The "_cfc" is defined in a cfajaxproxy call.
The CF function that's going to get called looks something like the following:
<cfcomponent>
<cffunction access="remote" name="setContent" returntype="string">
<cfargument name="_content" required="yes" type="string">
<cfset session.content = _content>
<cfreturn "whateveryouwant">
</cffunction>
</cfcomponent>
And the printPage.cfm might look like this (I use Bootstrap as my CSS):
<cfhtmltopdf>
<link href="css/bootstrap.css" rel="Stylesheet" type="text/css"></link>
<cfoutput>#session.content#</cfoutput>
</cfhtmltopdf>
This is obviously a bare bones solution, but it works. I would imagine that if you needed different CSS pages, you could set the HREF value in the link tag as a session parameter as well
Assuming you're using a link tag in the head you can try to use cfinclude inside style tags to include the css file. cfdocument seems to do better with inline styles.
I ended up passing a url flag pdf=0/1, then in the template simply add a cfif around whatever content you want to toggle.
I'm trying to get it to recognize FontAwesome and Google Charts at this point. Seems to help so far.

MVC helper inside a template

I am trying to use a kendo MVC helper inside a template (remote template file loaded like: http://docs.kendoui.com/howto/load-templates-external-files#remote-templates. I have a controller that sends to the client the generated markup)
My template file is something like:
<script id="my-pager-template" type="text/x-kendo-template">
My pager
#(Html.Kendo().ListView<Business.Data.MyPage>()
.Name("myPagerListView")
.TagName("div")
.ClientTemplateId("my-pager-item-template")
.DataSource(dataSource => dataSource.Read(read =>
read.Action("GetMyPages","Page")
)
).ToClientTemplate())
</script>
<script id="my-pager-item-template" type="text/x-kendo-template" >
<div class="k-button" data-pager-item-pageid="${PageID}" data-pager-item-pagename="${Name}">
<span>${ButtonText}</span>
</div>
</script>
But the generated markup is giving me an Uncaught SyntaxError: Unexpected token < in my browser console (chrome).
The markup generated by the helper is like this:
<div id="myPagerListView"></div>
<script>
jQuery(function(){jQuery("\#myPagerListView").kendoListView({"dataSource":{"transport":{"prefix":"","read":{"url":"/Page/GetMyPages"}},"serverPaging":true,"serverSorting":true,"serverFiltering":true,"serverGrouping":true,"serverAggregates":true,"type":"aspnetmvc-ajax","filter":[],"schema":{"data":"Data","total":"Total","errors":"Errors","model":{"fields":{"PageID":{"type":"number"},"Name":{"type":"string"},"ButtonText":{"type":"string"}}}}},"template":kendo.template($('\#my-pager-item-template').html())});});
<\/script>
</script>
Can I use kendo helpers this way?
(In this post, it says that it can be used: Can I use Kendo MVC helpers inside templates?)
I got that message a lot of times, your code is fine, the problem comes retrieving the data, kendo deserialize what it recieves from read.Action("GetMyPages","Page"), you probably are retrieving an HTML page instead of a json, so it tries to serialize something like "<html ...." and here you got the error, just check the url on chrome to check if you recive an json
i mean check http://yourdomain.com/Pages/GetPages/ (or the routing according to your app), you probably get an HTML page
I had this exact issue also. I have come to realise (over the past 3 hours :( ) that this is because I was using ajax then the jquery html function to load the template file and that the error was happening with in jquery's function as it tried to parse than execute the template file which has been mangled for an unknown reason by kendo. (escaping that script tag and in my case inserting buttons in that space). Fortunatly when kendo Its self tries to use the template it does work.
To get around this problem I rendered the partial view directly on the page.
Hope this helps.

How to protect layout from malformatted html content on a generated webpage?

I have a container layout of a webpage that works like this:
<html>
#header#
<div class="content_container"> #content inserted here# </div>
#footer#
</html>
If the content has a div that is not closed or closes a div without it being opened, the content_container's div and so the layout gets messed up.
How is this situation normally avoided or solved?
Hy,
From what i found on the net, you could parse the fragment as a HTML document using the DOM loadHTML function, then spit it back out again as HTML. That will fix any HTML well-formedness errors.
Or you could use the Tidy extension for php5
http://www.php.net/tidy

Change Global Variable onclick/img source update with javascript variable

First off I am completely new to Javascript but I have some HTML/CSS experience. I've been trying to create an html/javascript image gallery for a website; (It would probably be a lot easier to do in PHP but the web coordinator disabled PHP on our server for security reasons).
Anyway What I have is a page showing an Album-list, Album-browser and Photo-viewer in different a div and 2 iframes respectively. I have it set up so that when someone clicks on an album from the album list, a page is opened up in the album browser section (iframe:"browser-frame" showing thumbnails of all the images in the particular album). I've been trying to set it up so that when someone clicks on an image in the album browser the image will appear in the Photo-viewer section (iframe:"viewer-frame" showing the photo itself).
I didn't want the photo's in the viewer-frame to be larger than the set dimensions for the viewer-frame so I created a page for the viewer-frame that puts the image in a div with a class of set dimensions (defined in a stylesheet) as follows:
...<body>
<div class="photoview">
<img id="viewed_image" class="large" src="images/album1/1.jpg" />
</div>
</body>...
I then created a script that updates the image src to a variable:image_to_be_viewed and called it image-changer.js
// JavaScript Document
{
var image_to_be_viewed="images/album1/1.jpg";
document.getElementById("viewed_image").src=image_to_be_viewed;
}
And added a script to the viewer-frame page so it looks like:
...<body>
<div class="photoview">
<img id="viewed_image" class="large" src="images/album1/1.jpg" />
<script src="image-changer.js"></script>
</div>
</body>...
Now I wanted the gallery to work so that in the page loaded in the browser-frame, whenever one clicked on one of the pictures, the value of the global variable 'image_to_be_viewed' would be changed to the source of the clicked image as follows:
<body>
<div class="photobrowse">
<img class="medium" src="images/album1/1.jpg" onClick="image_to_be_viewed='images/album1/1.jpg'"/>
<img class="medium" src="images/album1/2.jpg" onClick="image_to_be_viewed='images/album1/2.jpg'"/>
<img class="medium" src="images/album1/3.jpg" onClick="image_to_be_viewed='images/album1/3.jpg'"/>
</div>
</body>
It doesn't work....
the gallery i'm working on is on http://ptc.tamu.edu/test/gallery_directory/test_gallery.html
everything up to the loading of the selected picture in the viewer frame works (I'm running the onlick event on the default loaded pictures 1,2,3 in the browser-frame page)(default pic's 4 and 5 simply load the image in the iframe but with no way to adjust the size it is too big and gets cut off and i don't want that)
I've been working on for an entire day and I'm sure I'm doing something wrong here but I can't figure out what exactly it is. I have a feeling it has to do with changing the global variable: image_to_be_viewed from the browser-frame page but I wanted to confirm with experts instead of flopping about like a headless fish. I'm going to continue trying to figure this out but i thought maybe having some expert assistance would speed up the process.
What the onclick triggers should be a javascript function call.
e.g. onclick="changeImg('images/album1/1.jpg')"
And the function itself should looks like this
function changeImg (image_to_be_viewed) {
document.getElementById("viewed_image").src = image_to_be_viewed;
}
btw, you probably should learn javascript a little bit more before work on something real. I recommend this book
thank you I got it to work! I figured that the changeImg function was targeting the wrong document/wrong frame and I fixed it by changing the js script to:
function changeImg (image_to_be_viewed) {
window.parent.viewer_frame.document.getElementById("viewed_image").src = image_to_be_viewed;
}

Create PDF in Struts from data on screen

I am able to create a simple pdf using iText api inside a struts action class.
The data that should be passed into the pdf is generated on screen based on user search parameters.
What I am wondering is how I can pass the data into the struts action so it can be displayed in the pdf?
Thanks in advance.
Similar question is already here. You just need to transfer everything that is on the page to struts action. I would do it like so:
JSP:
<div id="content">
wrap everything generated in here
</div>
<html:hidden styleId="hiddenHtml" name="hiddenHtml"/>
<html:submit onclick="setContentAsParam();">Export PDF</html:submit>
JS:
function setContentAsParam() {
document.getElementById('hiddenHtml').value = document.getElementById('content').innerHTML
}
This will set all the HTML to a action class property hiddenHtml. Get back if anything won't work, I wrote this out of my head without a test :)