2

I have an application that generates a PDF on the fly (accessed via service/generatePdf).

It returns a HTTP response with content-type="application/pdf" and the outputStream set to the binary contents.

Per business specs, when the user clicks a button we need to open a new window/tab that displays that PDF.

Submitting the form below, that all works beautifully on the Happy Path, where the response is an actual PDF.

<form action="service/generatePdf" method="post" name="PdfForm" target="_blank">

However, what does not work so well is when the PDF can't be generated for whatever reason. For example, let's say that the HTTP response outputStream is empty.

What I want to be able to do is display a nice error message on the first page, and not open the new window/tab.

But there doesn't seem to be any way to do it. Your choices seem to be

  • Return a valid PDF, or
  • Live with how the browser's PDF plugin handles corrupt files

I've tried jQuery, Ajax, the jQuery Form Plugin, the jQuery Download plugin plugin, and nothing seems to work.

JDB
  • 25,172
  • 5
  • 72
  • 123
SirEd
  • 31
  • 6
  • I appreciate the enthusiasm, but when I said "I have an application", I meant I've inherited a REST Java application whose (partial) URL is "service/generatePdf" (the actual URL is irrelevant). I'm responsible for both the REST app generating the PDF and for the Javascript consuming it. – SirEd Mar 13 '15 at 13:55
  • If it's a true REST service, then you should be able to examine the HTTP Response [Status Code](http://en.wikipedia.org/wiki/List_of_HTTP_status_codes) and determine whether the operation was successful. If it was, you can take the base64 string in the response's body and open that as a new window. See http://stackoverflow.com/a/2805990/211627 – JDB Mar 13 '15 at 14:10
  • I had seen that, and I think it's close to what I need. Unfortunately we are required to support IE8 and that solution fails when the PDF is too large. But the concept is pretty much what I need. – SirEd Mar 13 '15 at 14:35
  • Tell "them" that you can solve this issue for all browsers immediately except IE8. That browser is more complicated and the solution will take you [about 10 months](http://www.itnews.com.au/News/390877,microsoft-gives-deadline-for-end-of-ie8-support.aspx). – JDB Mar 13 '15 at 15:02
  • Ha! :) If only. Believe me, IE8 is the bane of my existence and that P.O.S. can't die quickly enough. – SirEd Mar 13 '15 at 16:07

2 Answers2

4

The server should indicate error or success through a HTTP status code (e.g. 200 = OK, 500 = error). This you can catch in your REST client, with JQuery

$.ajax({
    url: 'service/generatedPDF',
    error: function(jqXHR, textStatus, errorThrown) {
        ... // show error message
    },
).done(function(data) {
    // data contains the PDF
}

It would be better to just create the PDF on the server, put it in a temporary store and send the URL to this PDF in the response. Once the client downloads the file, or after a certain download, the PDF is removed from the store.

In that case you would just open a new window with the URL you received from the server.

If the server provides the PDF in the initial request, you can convert it to a Data URI and open that data URI in a new window.

Moritz Petersen
  • 12,902
  • 3
  • 38
  • 45
  • From what I can find, Ajax doesn't support binary data. When I did something similar to what you have, and the "data" object returned had a String that looks like binary PDF content, but I'm doubtful a String of binary data is exactly the same thing as a byte[] array. The idea of returning a URL via Ajax and doing a separate retrieve sounds like the cleanest, but it would require some serious rework of the app – SirEd Mar 13 '15 at 14:56
  • Here is an example for converting binary response to data URL. http://stackoverflow.com/questions/20035615/using-raw-image-data-from-ajax-request-for-data-uri – Moritz Petersen Mar 13 '15 at 16:27
  • I think I would use the Data URI rather than storing the data temporarily and providing a link. That way lies a lot of complexity (tracking when to delete the temp file). LiveCycle does something similar for attachments on SOAP calls and it is problematic (clients get errors because the files get deleted before they've been downloaded). Using the Data URI approach eliminates the asynchronous aspects of the response processing which will be simpler and less error-prone in the long run, IMHO. – Rob McDougall Mar 13 '15 at 16:36
  • @RobMcDougall I agree, only that the Data URI is limited in size. – Moritz Petersen Mar 13 '15 at 17:30
  • _I agree, only that the Data URI is limited in size_ Which is kind of a deal-killer. Everything would work fine until one day some IE8 user is going to fail because the PDF was too large. – SirEd Mar 13 '15 at 21:36
  • I ended up doing the 2 step approach. Step 1 is that the PDF is retrieved and stored in a local cache. The result of that effort is sent back as a JSON response - any funny business results in an error message in the reply. Step 2 is a second request for the PDF itself which is pulled from cache. – SirEd Mar 14 '15 at 02:05
0

This is a fairly common requirement. You need to make your REST app a little smarter. It needs to check the result of the LiveCycle PDF generation and, if it wasn't successful, return an HTML response (with a content-type of text/html).

The browser is fairly dumb. It examines the content-type of the incoming response and, based on the content-type, launches the plug-in. It's then up to the plug-in to process the response. The PDF plug-in is also not so bright, it assumes that the incoming data stream is a PDF and if it's empty, it produces an error.

The key here is to send down the right content-type (and content) to the browser, which means checking the PDF result and sending a more appropriate response if the PDF result is a failure.

We often see this in LiveCycle orchestrations too. The temptation is to generate the PDF into a com.adobe.idp.Document object and then return that object directly. This leads to similar problems that you describe. Instead, the better approach is to check the result of the PDF generation. If it is valid, then return that response. If the PDF generation failed, then construct an HTML response in a com.adobe.idp.Document object (with the appropriate text/html content-type) and then return that instead.

Rob McDougall
  • 328
  • 1
  • 10
  • As mentioned above, the Adobe LifeCycle thing is a red herring, but that's OK, I think we're still on track here... :) I can make the REST layer as smart as it need to be. So you're saying if everything is fine send the "application-pdf" response, but if something goes wrong, send an "test/html" response instead, correct? If so, I can do that. But that still means a new window would open for the error condition, it would just display HTML instead of a PDF. My desire is for an error to not open a new window and instead display the HTML on the original page. – SirEd Mar 13 '15 at 14:49
  • > So you're saying if everything is fine send the "application-pdf" response, but if something goes wrong, send an "test/html" response instead, correct? Correct – Rob McDougall Mar 13 '15 at 16:18
  • _> My desire is for an error to not open a new window and instead display the HTML on the original page._ I think that's an HTML issue. AFAIK, where the response goes is based on your target attribute. You don't really have an opportunity to make it conditional upon the response. I think your only alternative would be to write a lot of javascript that uses XMLHTTPRequest object to talk to the server and control the presentation of the response. – Rob McDougall Mar 13 '15 at 16:25
  • _I think that's an HTML issue. AFAIK, where the response goes is based on your target attribute. You don't really have an opportunity to make it conditional upon the response._ .... that's the conclusion I'm coming to, too. – SirEd Mar 13 '15 at 18:47
  • Popping an HTML error message up in new window isn't the worst thing in the world. I think I would propose that to the end user and see if it would fly. It may not be ideal, however it's not the end of the world either. – Rob McDougall Mar 13 '15 at 19:02
  • But here's the problem. Scenario #1 is where a PDF **should** be generated but for whatever reason isn't. That, I agree, wouldn't be the worst thing to put into an HTML error message in a window. Scenario #2 is that the PDF can't be generated because they've entered a date that's outside the range supported by back-end systems. In that case, they view that as an input error and want notification consistent with other input errors. The REST layer can make that distinction, but without a way to handle it on the client (browser) side it's a moot point. – SirEd Mar 13 '15 at 21:32