Our regular Save As PDF Links are meant to quickly let your customers turn your webpages into PDF by adding a simple link to your pages. Though that is a very quick solution for most situations, it won't work in all situations.
For example when you have one of the following situations:
For all these situations you can use our API with some simple JavaScript that we will explain below. You can see it in action when you click the button in the "Usage Example" section on the right.
Our HTML to PDF API takes HTML as input and returns a PDF. With the JavaScript below you take the HTML of the webpage that you're on and send it to our API. Our API will then return a PDF and the JavaScript will display it to the user.
Here's the basic code in JavaScript that you should link to the click of your button. You can do this by adding onclick="PDFmyURL()" to your save as PDF button or link, for example like this:
<a href="#" onclick="PDFmyURL()" class="hide_me">Save this page as PDF</a>
<script> function PDFmyURL() { // Replace this with your PDFmyURL license - you get one when you sign up at https://pdfmyurl.com/plans var license = 'yourlicensekey'; // First we take the HTML of the page var html = '', node = document.firstChild; while (node) { switch (node.nodeType) { case Node.ELEMENT_NODE: html += node.outerHTML; break; case Node.TEXT_NODE: html += node.nodeValue; break; case Node.CDATA_SECTION_NODE: html += '<![CDATA[' + node.nodeValue + ']]>'; break; case Node.COMMENT_NODE: html += '<!--' + node.nodeValue + '-->'; break; case Node.DOCUMENT_TYPE_NODE: // (X)HTML documents are identified by public identifiers html += "<!DOCTYPE " + node.name + (node.publicId ? ' PUBLIC "' + node.publicId + '"' : '') + (!node.publicId && node.systemId ? ' SYSTEM' : '') + (node.systemId ? ' "' + node.systemId + '"' : '') + '>\n'; break; } node = node.nextSibling; } // Now we prepare the data that we pass to the API // We pass the html, our license and custom CSS to hide the PDF button // Note that you don't need to pass any other parameters if your defaults in our members area are already good var data = { html: html, license:license, css:'.hide_me{display:none;}'}; var serialized = Object.keys(data).map(function(k) { return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }).join('&') // You can insert an "in progress" message here // We now prepare the API call xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { var a; if (xhttp.readyState === 4 && xhttp.status === 200) { // The PDF is now generated // You can remove the "in progress" message here // Now we show the PDF to the user var filename = "example.pdf"; if (window.navigator && window.navigator.msSaveOrOpenBlob) { window.navigator.msSaveOrOpenBlob(xhttp.response, filename); } else { a = document.createElement('a'); a.href = window.URL.createObjectURL(xhttp.response); a.download = filename; a.style.display = 'none'; document.body.appendChild(a); a.click(); } } }; // This is the actual call to our API xhttp.open("POST", "https://pdfmyurl.com/api", true); xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhttp.responseType = 'blob'; xhttp.send(serialized); } </script>
If your pages use a Content-Security-Policy then XMLHttpRequests will usually only be allowed to the same domain as yours. This means you'll get an error when you use the above example code.
If you want to be able to call our API as in the example, then you'd need to add it as a connect-src directive. You can do this with a statement like this:
Content-Security-Policy: connect-src 'self' https://pdfmyurl.com;
If your company's policies do not allow this, then you could instead create a server-side script that calls our API. You would then POST to that instead of to https://pdfmyurl.com/api. This makes the XMLHttpRequest call the same domain and would not break the Content Security Policy.
In some situations we will find that just sending the plain HTML is not enough. This can be when the resources on your page are ALSO not accessible from the internet and you didn't inline them.
In that case you can use some of our JavaScript that inlines the resources for you.
Below we explain our own JavaScript, where we:
Here's the example with some commenting. Please drop us an email if you need further help implementing this.
<script> // In this example we show everything so you can copy/paste if you have jQuery & BootStrap //jQuery is loaded now, now wait for the DOM to be loaded $(document).ready(function() { $('#pdfbutton').click(function(e) { e.preventDefault(); // The following code is now linked to the PDF button // now we replace the stylesheets with their absolute URL version var elements = document.querySelectorAll('link[rel=stylesheet]'); for(var i=0;i<elements.length;i++){ var head = document.head; var link = document.createElement("link"); link.type = "text/css"; link.rel = "stylesheet"; link.href = elements[i].href; head.appendChild(link); elements[i].parentNode.removeChild(elements[i]); } // let's convert all images to data so they don't need to be downloadable function setBase64Image(img) { var canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); var dataURL = canvas.toDataURL("image/png"); img.src = dataURL; } var images = document.getElementsByTagName("img"); if (images.length > 0) { for (var j=0; j<images.length; j++ ) { setBase64Image(images[j]); } } // now we'll take ALL HTML including the doctype var html = '', node = document.firstChild; while (node) { switch (node.nodeType) { case Node.ELEMENT_NODE: html += node.outerHTML; break; case Node.TEXT_NODE: html += node.nodeValue; break; case Node.CDATA_SECTION_NODE: html += '<![CDATA[' + node.nodeValue + ']]>'; break; case Node.COMMENT_NODE: html += '<!--' + node.nodeValue + '-->'; break; case Node.DOCUMENT_TYPE_NODE: // (X)HTML documents are identified by public identifiers html += "<!DOCTYPE " + node.name + (node.publicId ? ' PUBLIC "' + node.publicId + '"' : '') + (!node.publicId && node.systemId ? ' SYSTEM' : '') + (node.systemId ? ' "' + node.systemId + '"' : '') + '>\n'; break; } node = node.nextSibling; } var data = { html: html, license: 'yourlicensekey' }; // You can pass all the parameters you like if you want, but you don't need to if your defaults in our members area are already good // we will show an in progress message with a BootStrap modal $('#inProgress').modal('show'); xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { var a; if (xhttp.readyState === 4 && xhttp.status === 200) { // the PDF is ready so we remove the in progress message $('#inProgress').modal('hide'); // and show the PDF var filename = "example.pdf"; if (window.navigator && window.navigator.msSaveOrOpenBlob) { window.navigator.msSaveOrOpenBlob(xhttp.response, filename); } else { a = document.createElement('a'); a.href = window.URL.createObjectURL(xhttp.response); a.download = filename; a.style.display = 'none'; document.body.appendChild(a); a.click(); } } }; xhttp.open("POST", "https://pdfmyurl.com/api", true); xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhttp.responseType = 'blob'; xhttp.send($.param( data )); }); }); </script>
The button below uses the demo code to show how a "save as PDF" button can work in your protected members area - even if it's on a local system that's not reachable on the web.
Save as PDF!