How to print a PDF in Firefox?
This function works in Chrome but not in Firefox
function print_pdf(url){
var id = 'iframe', html = '<iframe id="'+id+'" src="'+url+'" style="display:none"></iframe>';
$('#main').append(html);
$('#'+id).load(function(){
document.getElementById(id).contentWindow.print();
}
}
error
Error: Permission denied to access property "print"
Firefox: Permission denied to access property "print"
This is a bug in firefox. Locally it can be disabled by going to about:config and set the property of pdfjs.disabled to true. Only possible workaround is to use a server-side script and modify the pdf. Using php you could use fpdf and embed extensions to implement js (inclunding the print() function) or simply convert the pdf to an image, return the url and print it. You could use FPDI to modify the existing pdf. I will give you an example on how I got it to work with PHP.
Generating a PDF file with inline javascript (autoprint) using FPDI and PDF_JS
require_once('fpdf.php');
require_once('fpdi.php');
class PDF_JavaScript extends FPDI {
var $javascript;
var $n_js;
function IncludeJS($script) {
$this->javascript=$script;
}
function _putjavascript() {
$this->_newobj();
$this->n_js=$this->n;
$this->_out('<<');
$this->_out('/Names [(EmbeddedJS) '.($this->n+1).' 0 R]');
$this->_out('>>');
$this->_out('endobj');
$this->_newobj();
$this->_out('<<');
$this->_out('/S /JavaScript');
$this->_out('/JS '.$this->_textstring($this->javascript));
$this->_out('>>');
$this->_out('endobj');
}
function _putresources() {
parent::_putresources();
if (!empty($this->javascript)) {
$this->_putjavascript();
}
}
function _putcatalog() {
parent::_putcatalog();
if (!empty($this->javascript)) {
$this->_out('/Names <</JavaScript '.($this->n_js).' 0 R>>');
}
}
}
class PDF_AutoPrint extends PDF_JavaScript
{
function AutoPrint($dialog=false)
{
//Open the print dialog or start printing immediately on the standard printer
$param=($dialog ? 'true' : 'false');
$script="print($param);";
$this->IncludeJS($script);
}
function AutoPrintToPrinter($server, $printer, $dialog=false)
{
$script = "document.contentWindow.print();";
$this->IncludeJS($script);
}
}
$pdf=new PDF_AutoPrint();
$pdf->setSourceFile("mozilla.pdf");
//Open the print dialog
$tplIdx = $pdf->importPage(1, '/MediaBox');
$pdf->addPage();
$pdf->useTemplate($tplIdx, 10, 10, 90);
$pdf->AutoPrint(true);
$pdf->Output('generated.pdf', 'F');
Now you can simply append the generated pdf to your page and the included javascript will call the print() function. You do not even have to call it manually anymore. However, in firefox this will only work with visibility: hidden and not with display: none.
function print_pdf(url){
var iFrameJQueryObject = $('<iframe id="iframe" src="'+url+'" style="visibility: hidden"></iframe>');
$('#foo').append(iFrameJQueryObject);
}
print_pdf('mozilla_generated.pdf');
Chrome: Security Error (cross-origin)
The pdf should be located at the same host. Firefox was okay with other domains in my tests, but chrome gave me cross-origin errors.
Firefox: Printed page includes about:blank only
You will get an empty page in firefox (jsfiddle), because it will print the iframe before it has loaded any content. Mentioned methods like $(document).onload() won't help, since they only wait for the DOM to load and setTimeout() can still result in errors, since you do not know how long it takes the iFrame to load.
You can simply resolve this issue by using jQuery's load(). (doc) This will give you the possibility to use a callback function as parameter.
if a "complete" callback is provided, it is executed after post-processing and HTML insertion has been performed. The callback is fired once for each element in the jQuery collection, and this is set to each DOM element in turn.
Code Example 1
function print_pdf(url){
var id = 'iframe', html = '<iframe id="'+id+'" src="'+url+'" style="display:none"></iframe>';
$('body').append(html);
// wait for the iFrame to fully load and call the print() function afterwards
$('#' + id).load(function () {
document.getElementById(id).contentWindow.print();
});
}
Alternatively you could directly create an jQuery object and use jQuery's on() (doc) to attach any event handler.
Code Example 2 (jsfiddle)
function print_pdf(url){
var iFrameJQueryObject = $('<iframe id="iframe" src="'+url+'" style="display:none"></iframe>');
$('body').append(iFrameJQueryObject);
iFrameJQueryObject.on('load', function(){
$(this).get(0).contentWindow.print();
});
}
Edit, Updated
Try using window.onload event , document.createElement() , onload event , setTimeout() with duration set to 2000 , setting src of iframe after appending element to document
window.onload = function() {
function print_pdf(url){
var id = "iframe", frame = document.createElement("iframe");
frame.setAttribute("id", id);
frame.setAttribute("width", "800px");
frame.setAttribute("height", "600px");
frame.setAttribute("allowfullscreen", "true");
frame.setAttribute("name", "printframe");
document.body.appendChild(frame);
frame.onload = function() {
this.requestFullScreen = this.mozRequestFullScreen
|| this.webkitRequestFullScreen;
this.requestFullScreen();
setTimeout(function() {
print()
},2000)
}
frame.setAttribute("src", url);
}
print_pdf("http://zeitreisen.zeit.de/wp-content/uploads/2014/09/pdftest2.pdf");
}
plnkr http://plnkr.co/edit/mHBNmc5mdM0YJRwRbYif?p=preview
PDFs have Javascript support. I needed to have auto print capabilities when a PHP-generated PDF was created and I was able to use FPDF to get it to work:
http://www.fpdf.org/en/script/script36.php
#clarkk i would recommend to use something more powerful which has been covered from many people my suggestion is to use http://pdfmake.org/#/ .
I have use this pdfmake in my datatables and it works absolutely perfect. Keep in mind if you print more then 10 000 rows from tables or something it run out the memory of the browser :)
I haven't use this for a while, but this what I was used to do to print pdf's from an iframe...
function printfile() {
window.frames['objAdobePrint'].focus();
window.frames['objAdobePrint'].print();
}
<iframe src="urlOfPdf" name="objAdobePrint" id="objAdobePrint"></iframe>
<button onclick="printfile();">Print</button>
You can implement print function without create new iframe (only with css) to prevent security problems:
var style = document.createElement("style");
style.setAttribute("media", "print"); //style.setAttribute("media", "screen,print");
style.appendChild(document.createTextNode(""));
document.head.appendChild(style);
var width = $("#printDiv").width();
var height = $("#printDiv").height();
style.sheet.insertRule("body { width: 210mm !important, height: 25.4mm !important; visibility: hidden; }", 0);
style.sheet.insertRule("#printDiv { visibility: visible; position: fixed !important;top: 5px; left: 5px; width:" + width + "px;height:" + height + "; page-break-after: avoid;}", 0);
window.focus();
window.print(true);
style.remove();
Print a pdf with javascript or jquery
Create a iframe in html:
<iframe id="pdf-iframe">
Then change the src of that iframe and on load, print it:
$('#pdf-iframe').attr("src", pdf_url).load(function(){
document.getElementById('pdf-iframe').contentWindow.print();
});
Or, you might want to try https://mozilla.github.io/pdf.js/
Related
I'm using the object tag to load an html snippet within an html page.
My code looks something along these lines:
<html><object data="/html_template"></object></html>
As expected after the page is loaded some elements are added between the object tags.
I want to get those elements but I can't seem to access them.
I've tried the following
$("object").html() $("object").children() $("object")[0].innerHTML
None of these seem to work. Is there another way to get those elements?
EDIT:
A more detailed example:
consider this
<html><object data="http://www.YouTube.com/v/GGT8ZCTBoBA?fs=1&hl=en_US"></object></html>
If I try to get the html within the object I get an empty string.
http://jsfiddle.net/wwrbJ/1/
As long as you place it on the same domain you can do the following:
HTML
<html>
<object id="t" data="/html_template" type="text/html">
</object>
</html>
JavaScript
var t=document.querySelector("#t");
var htmlDocument= t.contentDocument;
Since the question is slightly unclear about whether it is also about elements, not just about the whole innerHTML: you can show element values that you know or guess with:
console.log(htmlDocument.data);
The innerHTML will provide access to the html which is in between the <object> and </object>. What is asked is how to get the html that was loaded by the object and inside the window/frame that it is producing (it has nothing to do with the code between the open and close tags).
I'm also looking for an answer to this and I'm afraid there is none. If I find one, I'll come back and post it here, but I'm looking (and not alone) for a lot of time now.
No , it's not possible to get access to a cross-origin frame !
Try this:
// wait until object loads
$('object').load(function() {
// find the element needed
page = $('object').contents().find('div');
// alert to check
alert(page.html());
});
I know this is an old question, but here goes ...
I used this on a personal website and eventually implemented it in some work projects, but this is how I hook into an svg's dom. Note that you need to run this after the object tag has loaded (so you can trigger it with an onload function). It may require adaptation for non-svg elements.
function hooksvg(elementID) { //Hook in the contentDocument of the svg so we can fire its internal scripts
var svgdoc, svgwin, returnvalue = false;
var object = (typeof elementID === 'string' ? document.getElementById(elementID) : elementID);
if (object && object.contentDocument) {
svgdoc = object.contentDocument;
}
else {
if (typeof object.getSVGDocument == _f) {
try {
svgdoc = object.getSVGDocument();
} catch (exception) {
//console.log('Neither the HTMLObjectElement nor the GetSVGDocument interface are implemented');
}
}
}
if (svgdoc && svgdoc.defaultView) {
svgwin = svgdoc.defaultView;
}
else if (object.window) {
svgwin = object.window;
}
else {
if (typeof object.getWindow == _f) {
try {
svgwin = object.getWindow();//TODO look at fixing this
}
catch (exception) {
// console.log('The DocumentView interface is not supported\r\n Non-W3C methods of obtaining "window" also failed');
}
}
}
//console.log('svgdoc is ' + svgdoc + ' and svgwin is ' + svgwin);
if (typeof svgwin === _u || typeof svgwin === null) {
returnvalue = null;
} else {
returnvalue = svgwin;
}
return returnvalue;
};
If you wanted to grab the symbol elements from the dom for the svg, your onload function could look like this:
function loadedsvg(){
var svg = hooksvg('mysvgid');
var symbols = svg.document.getElementsByTagName('symbol');
}
You could use the following code to read object data once its loaded completely and is of the same domain:
HTML-
<html>
<div class="main">
<object data="/html_template">
</object>
</div>
</html>
Jquery-
$('.main object').load(function() {
var obj = $('.main object')[0].contentDocument.children;
console.log(obj);
});
Hope this helps!
Here goes a sample piece of code which works. Not sure what the problem is with your code.
<html>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var k = $("object")[0].innerHTML;
alert(k);
$("object")[0].innerHTML = "testing";
});
</script>
<object data="/html_template">hi</object>
</html>
UPDATED
I used this line of Javascript to change the value of a input filed inside an iFrame, taken from How to pick element inside iframe using document.getElementById:
document.getElementById('iframeID').contentWindow.document.getElementById('inputID').value = 'Your Value';
In your case, since you do not have a frame, and since you want to get and not set the value, log it for example with:
console.log(document.getElementById('object').value);
And if you guess or choose an element:
console.log(document.getElementById('object').data);
Test page: https://jsfiddle.net/y25rk55w/
On this test page you can see 3 <iframe>'s embeded into each other. Each <iframe> contains a <script> tag in it's <head> tag.
The problem is: only the <script> in the first <iframe> will be loaded by the browser. The other two <script> tags will be present in the dom but the browser will never even try to load them. The problem is not browser specific, it can be reroduced in chrome, firefox, ie. The problem cannot be fixed by adding timeouts or waiting before appending the scripts. It seems to be important that all the iframes have programatically generated content; if you replace this iframes with iframes with actual src links, the problem will disappear.
The question is: how can I actually load a script into iframes 2 and 3?
Full test code:
// It doesn't matter if the scripts exist or not
// Browser won't try to load them either way
var scripts = [
'//testdomain.test/script1.js',
'//testdomain.test/script2.js',
'//testdomain.test/script3.js'
];
function createIFrame(win, onCreated) {
var iframe = win.document.createElement('iframe');
iframe.onload = function () {
onCreated(iframe);
};
win.document.body.appendChild(iframe);
}
function loadScript(win, url) {
var script = win.document.createElement('script');
script.src = url;
script.onload = function() {
console.log("Script " + url + " is loaded.");
};
win.document.getElementsByTagName('head')[0].appendChild(script);
}
createIFrame(window, function(iframe1) {
loadScript(iframe1.contentWindow, scripts[0]);
createIFrame(iframe1.contentWindow, function (iframe2) {
loadScript(iframe2.contentWindow, scripts[1]);
createIFrame(iframe2.contentWindow, function (iframe3) {
loadScript(iframe3.contentWindow, scripts[2]);
});
});
});
Your code is working fine --> http://plnkr.co/edit/vQGsyD7JxZiDlg6EZvK4?p=preview
Make sure you execute createIFrame on window.onload or DOMContentLoaded.
var scripts = [
'https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.js',
'https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.2/jquery.js',
'https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js'
];
function createIFrame(win, onCreated) {
var iframe = win.document.createElement('iframe');
iframe.onload = function () {
onCreated(iframe);
};
win.document.body.appendChild(iframe);
}
function loadScript(win, url) {
var script = win.document.createElement('script');
script.src = url;
script.onload = function() {
console.log("Script " + url + " is loaded.");
};
win.document.getElementsByTagName('head')[0].appendChild(script);
}
window.onload = function(){
createIFrame(window, function(iframe1) {
loadScript(iframe1.contentWindow, scripts[0]);
createIFrame(iframe1.contentWindow, function (iframe2) {
loadScript(iframe2.contentWindow, scripts[1]);
createIFrame(iframe2.contentWindow, function (iframe3) {
loadScript(iframe3.contentWindow, scripts[2]);
});
});
});
};
In the question you can see that I was ommiting the protocol:
/* This is valid to omit the http:/https: protocol.
In that case, browser should automatically append
protocol used by the parent page */
var scripts = [
'//testdomain.test/script1.js',
'//testdomain.test/script2.js',
'//testdomain.test/script3.js'
];
The thing is, programatically created iframes have protocol about: (or javascript:, depending on how you create them). I still can't explain why the first script was loading or why the other two scripts were not showing up in the network tab at all, but I guess it's not very important.
The solution: either explicitly use https:// or programatically append protocol using something like the following code:
function appendSchema(win, url) {
if (url.startsWith('//')) {
var protocol = 'https:';
try {
var wPrev = undefined;
var wCur = win;
while (wPrev != wCur) {
console.log(wCur.location.protocol);
if (wCur.location.protocol.startsWith("http")) {
protocol = wCur.location.protocol;
break;
}
wPrev = wCur;
wCur = wCur.parent;
}
} catch (e) {
/* We cannot get protocol of a cross-site iframe.
* So in case we are inside cross-site iframe, and
* there are no http/https iframes before it,
* we will just use https: */
}
return protocol + url;
}
return url;
}
I've been successful using a simpler method than what the OP proposes in the self-answer. I produce the URLs using:
new URL(scriptURL, window.location.href).toString();
where scriptURL is the URL that needs to be fixed to get a proper protocol and window is the parent of the iframe element that holds the scripts. This can take care of scenarios that differ from the OPs example URLs: like relative URLs (../foo.js) or absolute URLs that don't start with a host (/foo.js). The above code is sufficient in my case.
If I were to replicate the search through the window hierarchy that the OP used, I'd probably do something like the following. This is TypeScript code. Strip out the type annotations to get plain JavaScript.
function url(win: Window, path: string): string {
// We search up the window hierarchy for the first window which uses
// a protocol that starts with "http".
while (true) {
if (win.location.protocol.startsWith("http")) {
// Interpret the path relative to that window's href. So the path
// will acquire the protocol used by the window. And the less we
// specify in `path`, the more it gets from the window. For
// instance, if path is "/foo.js", then the host name will also be
// acquired from the window's location.
return new URL(path, win.location.href).toString();
}
// We searched all the way to the top and found nothing useful.
if (win === win.parent) {
break;
}
win = win.parent;
}
// I've got a big problem on my hands if there's nothing that works.
throw new Error("cannot normalize the URL");
}
I don't have a default return value if the window chain yield nothings useful because that would indicate a much larger issue than the issue of producing URLs. There'd be something wrong elsewhere in my setup.
I am having an iframe inside a div element which is hidden/display none, I want to get the href attribute of a tag using javascript my code is
HTML
<div id="questions" style="display: none;">
<iframe id="article_frame" width="100%" height="100%">
Click here
</iframe>
</div>
JS
window.onload = function() {
alert("Hello " + window.document.getElementById("article_frame"));
}
But I am getting alert as "Hello null" any solution
Thanks
Thanks All,
I have got the answer with javascript it just simple code
var anchor = document.getElementById('en_article_link').firstChild;
var newLink = anchor.getAttribute("href")+"sid="+sidvalue;
anchor.setAttribute("href", newLink);
Ok i feel this may be a slight overkill but it will get you what you require (the href value of the anchor tag inside the iframe) :
window.onload = function() {
var frame = window.document.getElementById("article_frame");
var myString = frame.childNodes[0].textContent
, parser = new DOMParser()
, doc = parser.parseFromString(myString, "text/xml");
var hrefValue = doc.firstChild.getAttribute('href');
alert("Hello " + hrefValue);
}
I guess it depends on your requirements but another way would be to create a string and then using functions: substring and indexof you could get your value. Here is how you would get the string:
window.onload = function() {
var frame = window.document.getElementById("article_frame");
var elementString = frame.childNodes[0].textContent;
//then perform your functions on the string here
}
Note that you can only access the contents of an iframe that contains a page on the same domain due to the Same-Origin Policy (Wikipedia).
I recommend using jQuery for this. The tricks here are:
Wait for the iframe to finish loading $("#article_frame").ready()
Access the iframe's document $("#article_frame").contents()
From there you're just handling the task at hand:
$("#article_frame").ready(function() {
alert("Hello " + $("#article_frame").contents().find("#en_link").href);
});
I have read up on all issues regarding Safari and blank printing. It seems that a white flash happens, re-rendering the page, and content of the iframe is lost before a print dialog can grab it.
Here is my javascript - It works in all browsers except safari. It brings up the dialog, but prints a blank page.
function PrintPopupCode(id) {
framedoc = document;
var popupFrame = $(framedoc).find("#" + id + '\\!PopupFrame');
var icontentWindow = popupFrame[0].contentWindow || popupFrame[0].contentDocument;
icontentWindow.focus();
icontentWindow.print();
}
function PrintPopup(id) {
setTimeout(function () { PrintPopupCode(id) }, 3000);
}
I have set a timeout, i previously read it would help if the transfer of content took sometime, but it has not helped.
I have also tried with printElement() function on the icontentWindow variable, but it does not support this method.
Print Element Method
This is all in a .js file, and not on the page. I have tried on the page, but the same thing happens.
Help?
Maybe you should try this:
function PrintPopupCode(id) {
framedoc = document;
var popupFrame = $(framedoc).find("#" + id + '\\!PopupFrame');
var icontentWindow = popupFrame[0].contentWindow || popupFrame[0].contentDocument;
icontentWindow.focus();
setTimeout(icontentWindow.print, 3000);
}
function PrintPopup(id) {
PrintPopupCode(id);
}
I would like to know if it's possible to use javascript to open a popup window containing an image, and at the same time have the print dialog show. Once someone clicks on print, the popup closes.
Is this easily attainable?
Another great solution!! All credit goes to Codescratcher
<script>
function ImagetoPrint(source)
{
return "<html><head><scri"+"pt>function step1(){\n" +
"setTimeout('step2()', 10);}\n" +
"function step2(){window.print();window.close()}\n" +
"</scri" + "pt></head><body onload='step1()'>\n" +
"<img src='" + source + "' /></body></html>";
}
function PrintImage(source)
{
var Pagelink = "about:blank";
var pwa = window.open(Pagelink, "_new");
pwa.document.open();
pwa.document.write(ImagetoPrint(source));
pwa.document.close();
}
</script>
PRINT
See the full example here.
popup = window.open();
popup.document.write("imagehtml");
popup.focus(); //required for IE
popup.print();
Use this in the head block
<script type="text/javascript">
function printImg() {
pwin = window.open(document.getElementById("mainImg").src,"_blank");
pwin.onload = function () {window.print();}
}
</script>
use this in the body block
<img src="images.jpg" id="mainImg" />
<input type="button" value="Print Image" onclick="printImg()" />
This code will open YOUR_IMAGE_URL in a popup window, show print dialog and close popup window after print.
var popup;
function closePrint () {
if ( popup ) {
popup.close();
}
}
popup = window.open( YOUR_IMAGE_URL );
popup.onbeforeunload = closePrint;
popup.onafterprint = closePrint;
popup.focus(); // Required for IE
popup.print();
MDN Reference code
A cross browser solution printImage(document.getElementById('buzzBarcode').src)
/**
* Prints an image by temporarily opening a popup
* #param {string} src - image source to load
* #returns {void}
*/
function printImage(src) {
var win = window.open('about:blank', "_new");
win.document.open();
win.document.write([
'<html>',
' <head>',
' </head>',
' <body onload="window.print()" onafterprint="window.close()">',
' <img src="' + src + '"/>',
' </body>',
'</html>'
].join(''));
win.document.close();
}
img {
display: block;
margin: 10px auto;
}
button {
font-family: tahoma;
font-size: 12px;
padding: 6px;
display: block;
margin: 0 auto;
}
<img id="buzzBarcode" src="https://barcode.orcascan.com/qrcode/buzz.png?text=to infinity and beyond" width="150" height="150" />
Yea, just put the image on the screen, and then call window.print(); in javascript and it should popup.
(This is how Google Maps/Google Calendar do printing)
This works in Chrome:
<body ><img src="image.jpg" alt="" style="display: block; width: 100%; height: 100%;">
<script type="text/javascript">
window.onload = function() {
window.print();
setTimeout(function() {
window.close();
}, 1);
};
</script>
</body>
I just spent 45 minutes on this "SIMPLE" problem, trying to get it the way I wanted it to operate.
I had an image inside an img tag, dynamically generated by a jQuery Barcode plugin that I had to print. I wanted it to print in another window and afterwards close the window. This was all supposed to happen after the user clicked a button inside a jQuery Grid plugin, inside a jQuery-UI dialog along with jQuery-UI dialog extender applied to it.
I adjusted everyone answers till I finally came up with this, maybe it can help someone.
w = window.open(document.getElementById("UB-canvas").src);
w.onload = function () { w.print(); }
w.onbeforeunload = setTimeout(function () { w.close(); },500);
w.onafterprint = setTimeout(function () { w.close(); },500);
The setTimeout is not just for shits and giggles, it's the only way I found Firefox 42 would hit those functions. It would just simply skip the .close() functions until I added a breakpoint to it, then it worked perfectly. So I'm assuming it created those window instances before it could apply the onbeforeload event function and onafterprint event functions, or something.
I wrote a coffee script function that does that (but without opening a new window):
#print_img = (url) ->
$children = $('body').children().hide()
$img = $('<img>', src: url)
$img.appendTo('body')
$img.on 'load', ->
window.print()
$(this).remove()
$children.show()
Or if you prefer in javascript:
this.print_img = function(url) {
var $children, $img;
$children = $('body').children().hide();
$img = $('<img>', {
src: url
});
$img.appendTo('body');
$img.on('load', function() {
window.print();
$(this).remove();
$children.show();
});
};
This function makes sure that the elements on the body are hidden and not redrawn into the DOM.
It also makes sure that the image is loaded before calling print (if the image is too large and the internet connection is slow, it may take a while to load the img, if you call print too soon, it will print an empty page)