Not detecting afterprint from PDF - javascript

I have received a PDF from a server in which the user often wants to print without really looking at it. I try to use an onafterprint event to close the window, however, it foes not seem to get triggered when I open a PDF this way. If I instead set the URL blank to open an empty page it seems to work just fine.
let url = URL.createObjectURL(blob);
let printWindow = window.open(url);
printWindow.onafterprint = function(){
console.log("afterprint")
};

Your code won't get executed since the pdf window MIME type is "application/pdf", not "text/html".
Check this one for more details.
https://stackoverflow.com/a/51735450/1141936

Related

print not workig on IE11

i am using blob url and iFrame to print document. It's work fine in Chrome but not working in IE.
can anyone tell me how to print blob url set in iframe src?
let file = new Blob([res.blob()], { type: 'application/pdf' });
let blobURL = URL.createObjectURL(file);// here file is blob object
let iframe = document.createElement('iframe');
document.getElementById('viewerContainer').appendChild(iframe);
iframe.onload = function () {
iframe.contentWindow.print();
};
iframe.src = blobURL;
I don't know the specific case of IE7 but I had a similar error with Firefox. This one triggers the onload event before the content is fully loaded. Unfortunately, I couldn't find a solution. So I cunningly used a setTimeout().
This solution is weak but you can use setTimeout(time) with a time that will allow you to load the full content of the document. Warning this time depends on the network connection

Can I set the filename of a PDF object displayed in Chrome?

In my Vue app I receive a PDF as a blob, and want to display it using the browser's PDF viewer.
I convert it to a file, and generate an object url:
const blobFile = new File([blob], `my-file-name.pdf`, { type: 'application/pdf' })
this.invoiceUrl = window.URL.createObjectURL(blobFile)
Then I display it by setting that URL as the data attribute of an object element.
<object
:data="invoiceUrl"
type="application/pdf"
width="100%"
style="height: 100vh;">
</object>
The browser then displays the PDF using the PDF viewer. However, in Chrome, the file name that I provide (here, my-file-name.pdf) is not used: I see a hash in the title bar of the PDF viewer, and when I download the file using either 'right click -> Save as...' or the viewer's controls, it saves the file with the blob's hash (cda675a6-10af-42f3-aa68-8795aa8c377d or similar).
The viewer and file name work as I'd hoped in Firefox; it's only Chrome in which the file name is not used.
Is there any way, using native Javascript (including ES6, but no 3rd party dependencies other than Vue), to set the filename for a blob / object element in Chrome?
[edit] If it helps, the response has the following relevant headers:
Content-Type: application/pdf; charset=utf-8
Transfer-Encoding: chunked
Content-Disposition: attachment; filename*=utf-8''Invoice%2016246.pdf;
Content-Description: File Transfer
Content-Encoding: gzip
Chrome's extension seems to rely on the resource name set in the URI, i.e the file.ext in protocol://domain/path/file.ext.
So if your original URI contains that filename, the easiest might be to simply make your <object>'s data to the URI you fetched the pdf from directly, instead of going the Blob's way.
Now, there are cases it can't be done, and for these, there is a convoluted way, which might not work in future versions of Chrome, and probably not in other browsers, requiring to set up a Service Worker.
As we first said, Chrome parses the URI in search of a filename, so what we have to do, is to have an URI, with this filename, pointing to our blob:// URI.
To do so, we can use the Cache API, store our File as Request in there using our URL, and then retrieve that File from the Cache in the ServiceWorker.
Or in code,
From the main page
// register our ServiceWorker
navigator.serviceWorker.register('/sw.js')
.then(...
...
async function displayRenamedPDF(file, filename) {
// we use an hard-coded fake path
// to not interfere with legit requests
const reg_path = "/name-forcer/";
const url = reg_path + filename;
// store our File in the Cache
const store = await caches.open( "name-forcer" );
await store.put( url, new Response( file ) );
const frame = document.createElement( "iframe" );
frame.width = 400
frame.height = 500;
document.body.append( frame );
// makes the request to the File we just cached
frame.src = url;
// not needed anymore
frame.onload = (evt) => store.delete( url );
}
In the ServiceWorker sw.js
self.addEventListener('fetch', (event) => {
event.respondWith( (async () => {
const store = await caches.open("name-forcer");
const req = event.request;
const cached = await store.match( req );
return cached || fetch( req );
})() );
});
Live example (source)
Edit: This actually doesn't work in Chrome...
While it does set correctly the filename in the dialog, they seem to be unable to retrieve the file when saving it to the disk...
They don't seem to perform a Network request (and thus our SW isn't catching anything), and I don't really know where to look now.
Still this may be a good ground for future work on this.
And an other solution, I didn't took the time to check by myself, would be to run your own pdf viewer.
Mozilla has made its js based plugin pdf.js available, so from there we should be able to set the filename (even though once again I didn't dug there yet).
And as final note, Firefox is able to use the name property of a File Object a blobURI points to.
So even though it's not what OP asked for, in FF all it requires is
const file = new File([blob], filename);
const url = URL.createObjectURL(file);
object.data = url;
In Chrome, the filename is derived from the URL, so as long as you are using a blob URL, the short answer is "No, you cannot set the filename of a PDF object displayed in Chrome." You have no control over the UUID assigned to the blob URL and no way to override that as the name of the page using the object element. It is possible that inside the PDF a title is specified, and that will appear in the PDF viewer as the document name, but you still get the hash name when downloading.
This appears to be a security precaution, but I cannot say for sure.
Of course, if you have control over the URL, you can easily set the PDF filename by changing the URL.
I believe Kaiido's answer expresses, briefly, the best solution here:
"if your original URI contains that filename, the easiest might be to simply make your object's data to the URI you fetched the pdf from directly"
Especially for those coming from this similar question, it would have helped me to have more description of a specific implementation (working for pdfs) that allows the best user experience, especially when serving files that are generated on the fly.
The trick here is using a two-step process that perfectly mimics a normal link or button click. The client must (step 1) request the file be generated and stored server-side long enough for the client to (step 2) request the file itself. This requires you have some mechanism supporting unique identification of the file on disk or in a cache.
Without this process, the user will just see a blank tab while file-generation is in-progress and if it fails, then they'll just get the browser's ERR_TIMED_OUT page. Even if it succeeds, they'll have a hash in the title bar of the PDF viewer tab, and the save dialog will have the same hash as the suggested filename.
Here's the play-by-play to do better:
You can use an anchor tag or a button for the "download" or "view in browser" elements
Step 1 of 2 on the client: that element's click event can make a request for the file to be generated only (not transmitted).
Step 1 of 2 on the server: generate the file and hold on to it. Return only the filename to the client.
Step 2 of 2 on the client:
If viewing the file in the browser, use the filename returned from the generate request to then invoke window.open('view_file/<filename>?fileId=1'). That is the only way to indirectly control the name of the file as shown in the tab title and in any subsequent save dialog.
If downloading, just invoke window.open('download_file?fileId=1').
Step 2 of 2 on the server:
view_file(filename, fileId) handler just needs to serve the file using the fileId and ignore the filename parameter. In .NET, you can use a FileContentResult like File(bytes, contentType);
download_file(fileId) must set the filename via the Content-Disposition header as shown here. In .NET, that's return File(bytes, contentType, desiredFilename);
client-side download example:
download_link_clicked() {
// show spinner
ajaxGet(generate_file_url,
{},
(response) => {
// success!
// the server-side is responsible for setting the name
// of the file when it is being downloaded
window.open('download_file?fileId=1', "_blank");
// hide spinner
},
() => { // failure
// hide spinner
// proglem, notify pattern
},
null
);
client-side view example:
view_link_clicked() {
// show spinner
ajaxGet(generate_file_url,
{},
(response) => {
// success!
let filename = response.filename;
// simplest, reliable method I know of for controlling
// the filename of the PDF when viewed in the browser
window.open('view_file/'+filename+'?fileId=1')
// hide spinner
},
() => { // failure
// hide spinner
// proglem, notify pattern
},
null
);
I'm using the library pdf-lib, you can click here to learn more about the library.
I solved part of this problem by using api Document.setTitle("Some title text you want"),
Browser displayed my title correctly, but when click the download button, file name is still previous UUID. Perhaps there is other api in the library that allows you to modify download file name.

Print a pdf created with jsPDF in all browsers

So, I have dynamically created a pdf, and now i want to print it:
var doc = new jsPDF();
var name = "Doe, John"
doc.setFontType("normal");
doc.setFontSize(12);
doc.text(20,20,'Name: '+ name);
//do something that prints the pdf...
So, how do I take this doc variable and print it. Everywhere else I found uses the url of the pdf. Do I need to create a url for it first?
So, the solution I am currently going with is to show the pdf in a new tab/window, from which the pdf can be printed.
window.open(doc.output('datauristring'));
Unfortunately, this only works in Chrome. Anyone know how to get it working in IE, Firefox, Safari, etc.?
I still wonder if there is a way to skip this step (of opening the pdf and then requiring another button to be pushed).
There is a method autoPrint() provided by jsPDF library.
You can use that as shown below
var doc = new jsPDF();
var name = "Doe, John"
doc.setFontType("normal");
doc.setFontSize(12);
doc.text(20,20,'Name: '+ name);
doc.autoPrint();
//This is a key for printing
doc.output('dataurlnewwindow');
Try this:
window.open(doc.output('bloburl'), '_blank');
This can have problems with adblock sometimes.
So, in conclusion, for Chrome and Safari, use
window.open(doc.output('datauristring'));
but for IE and Firefox, use
doc.save();
These will both allow you to open the pdf in a new window, from which it can be printed. For those who have taken the time to figure out what is necessary on other browsers, feel free to add your research here...
All you need is to add this
doc.save('Test.pdf');
(I suppose you already have all the above written code inside the button trigger which user clicks to get pdf)
You better implement "printjs" from npm, convert jspdf document to blob and print it like this
const data = PDF.output('blob')
const blobUrl = URL.createObjectURL(data);
printJS(blobUrl);

Java Download file from url with download dialog

I want to download a file from a url which initially shows some html, then displays a download dialog after 2-3 seconds. Obviously if I do this:
try {
URL url = new URL("http://my.url");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Content-Length",
Integer.toString(Integer.MAX_VALUE));
con.setReadTimeout(Integer.MAX_VALUE);
con.setConnectTimeout(Integer.MAX_VALUE);
con.connect();
bis = new BufferedInputStream(con.getInputStream(), 4096);
byteArray = IOUtils.toByteArray(bis);
FileUtils.writeByteArrayToFile(new File("myFile"), byteArray);
} catch (Exception e) {
}
I will save the displayed .html rather than the file that is displayed in the save dialog.
How should I change the code in order to do this?
I'm guessing the dialog just has some javascript which waits a couple seconds and requests the file download embedded in the dialog somewhere.
If this is the case, if you figure out what element the 'real download' is contained in, you can use JSoup, or any other html parser library to scrape the link out of the page.
You obviously only have to do that if the download link is generated dynamically.
After doing what rossa suggests, I'd suggest setting javascript breakpoints in the dialog window to figure out how exactly the real url is getting requested.
Are you sure the url is the exact location of the file you want to download? I mean, is there any redirect - you can check in your browser and use HTTP headers extension for instance to check what's going on behind the scene.

Open a new window on response with pdf attachment

jqGrid('navButtonAdd',"#pager2",{caption:"Save All",title:"Save & Create Receipt",onClickButton:function () {
var s;
s = jQuery("#list2").jqGrid('getDataIDs');
alert("selected");
$.ajax({
type: 'POST',
url:'http://localhost:3000/order/receipt',
data: {ids: s},
});
}});
With above code, I'm able to submit data to server and the on server side it will generate a pdf as attachment, now I want to view the pdf on response via a new window/tab on browser.
Anyway to do it?
thanks,
lupind
If you return a URL to the just generated PDF then you can call window.open with that URL. But I wouldn't use an Ajax call if you want to open a new window to display the PDF anyway. In this case I would either:
Use Javascript's window.open passing
through the data in the URL to the
page that generates the PDF and let
it output to the browser's window
(but it might be too much data in the
URL)
Create a special form to hold
the data from your page and submit
that form. This special form would
have a target attribute set
(target="_blank") to open it in a new
window/tab.
For me, using two HTTP calls for one result (the generation and viewing of a PDF) is a waste.
You can use window.open() to open a new window with a specified URL. Be careful, though, only to do this when necessary.
Excerpt from this link.
Use an iframe with visibility:hidden, then change the iframe location to a downloadable file in javascript on the success callback.
var iFrame = document.getElementById('hiddenIFrame');
iFrame.src = theurlThatWillProduceTheFile;

Categories