0

I'm trying to call a JavaScript-Method from within my JavaFX-WebView via:

JSObject win = (JSObject)getEngine().executeScript("window");
win.call("showPDF", myPDFFile /*typeof java.io.File*/);

the result of this call is:

Invalid parameter object: need either .data, .range or .url

This is the JavaScript-Part (not by me), which throws the error:

var source;
if (typeof src === 'string') {
   source = { url: src };
} else if (isArrayBuffer(src)) {
  source = { data: src };
} else if (src instanceof PDFDataRangeTransport) {
  source = { range: src };
} else {
  if (typeof src !== 'object') {
     error('Invalid parameter in getDocument, need either Uint8Array, ' +
     'string or a parameter object');
  }
  if (!src.url && !src.data && !src.range) {
     error('Invalid parameter object: need either .data, .range or .url');
  }
}

Implementation of isArrayBuffer:

function isArrayBuffer(v) {
   return typeof v === 'object' && v !== null && v.byteLength !== undefined;
}

So my question is:
What type of (java) object could be used so that this call might work?

win.call("showPDF", ???);

EDIT 1:
String cannot be used, because it will be treated as a URL.
I would like to commit something like a ByteArray (my concrete file), but using a byte[] (instead of java.io.File) causes the same error.

Here are some comments from the JS function above: https://github.com/mozilla/pdf.js/blob/master/src/display/api.js#L234

This is the main entry point for loading a PDF and interacting with it.
NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR)
is used, which means it must follow the same origin rules that any XHR does
e.g. No cross domain requests without CORS.

@param {string|TypedArray|DocumentInitParameters|PDFDataRangeTransport} src
Can be a url to where a PDF is located, a typed array (Uint8Array)
already populated with data or parameter object.

What Datatype i have to use (in JAVA), so it will be a (e.g) TypedArray (Uint8Array) in JAVASCRIPT?

EDIT 2:
I was trying Aarons suggestion:

String encoded = Base64.getEncoder().encodeToString(Files.readAllBytes(myPDFFile.toPath()));
engine.executeScript("var src = _base64ToArrayBuffer('"+encoded+"'); showPDF(src);");

This causes a new problem:

Error: Invalid PDF binary data: either typed array, string or array-like object is expected in the data property.

This (new) error is thrown (few lines later) here: https://github.com/mozilla/pdf.js/blob/master/src/display/api.js#L291

console.log(_base64ToArrayBuffer(encoded)) returns: [object ArrayBuffer]

Solution:
I managed to make this work (with help of Aaron):

pdfViewer.html

<!doctype html>
<html>
<head>
  <script src="web/compatibility.js"></script>
  <script src="build/pdf.js"></script>
  <script>
    function _base64ToBinaryString(base64) {
        var binary_string =  window.atob(base64);
        return binary_string;
    }
    function showPDF(pdfFile) {
        console.log('calling showPDF...');  

        'use strict';

        PDFJS.disableWorker = true; /* IMPORTANT TO DISABLE! */
        PDFJS.workerSrc = 'build/pdf.worker.js';

        console.log('TRYING TO GET DOCUMENT FROM BINARY DATA...');
        PDFJS.getDocument({data: pdfFile}).then(function(pdf) 
        {
            console.log('PDF LOADED.');
            console.log('TRYING TO GET PAGE 1...');

            pdf.getPage(1).then(function(page) {
            console.log('PAGE 1 LOADED.');

            var scale = 1.0;
            var viewport = page.getViewport(scale);

            var canvas = document.getElementById('the-canvas');
            var context = canvas.getContext('2d');
            canvas.height = viewport.height;
            canvas.width = viewport.width;

            var renderContext = {
                canvasContext: context,
                viewport: viewport
            };
            console.log('RENDERING PAGE 1...');
            page.render(renderContext);
            });
        });
    };
   </script>
   </head>
   <body>
      <canvas id="the-canvas" style="border:1px solid black;"/>
   </body>
</html>

After loading the above HTML-Page into the WebView, following Java-Code is used to load a PDF-File into the Page:

String encoded = Base64.getEncoder().encodeToString(Files.readAllBytes(myPdfFile.toPath()));
webEngine.executeScript("var src = _base64ToBinaryString('"+encoded+"'); showPDF(src);");
Ben
  • 3,378
  • 30
  • 46
  • `showPDF()` isn't a standard function of WebKit (which JavaFX uses to render HTML). How did you add this function to the `WebView`? – Aaron Digulla May 19 '15 at 07:53
  • I created an HTML-File with a JavaScript-Function named `showPDF(pdfFile)`, then i loaded this file via `getEngine().load(URL)`. After that i tried to execute this method.`showPDF(pdfFile)` will then call `PDFJS.getDocument(pdfFile)` (from PDF.JS) to show the PDF. – Ben May 19 '15 at 08:14

1 Answers1

0

Try myPDFFile.getURI().getURL().toString() since the method accepts URLs in the form of Strings. Since WebKit is running locally, you should be able to read file:// URLs.

If you want to try the ArrayBuffer, then you can find some examples in the MDN JavaScript documentation at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer

That means you need to create a FileReader, pass the URL, handle the asynchronous events. I only recommend this if you know JavaScript fairly well.

Unfortunately, the examples are all geared towards handling files in a web page, not passing data into a page.

[EDIT] Here is an example how to convert a Base64 encoded string to ArrayBuffer: Convert base64 string to ArrayBuffer

So you need the load the file into a Java byte[] array. Then convert the array to a Base64 encoded Java String. You can then paste this into dynamic JavaScript:

String encoded = ...byte[] -> Base64...
getEngine().executeScript("var src = _base64ToArrayBuffer('"+encoded+"'); showPDF(src);");
Community
  • 1
  • 1
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • i don't have much experiences wih JS. The JS **code above** is from [https://github.com/mozilla/pdf.js/blob/master/src/display/api.js#L267](https://github.com/mozilla/pdf.js/blob/master/src/display/api.js#L267) – Ben May 19 '15 at 08:26
  • As far as i understand, `src` can be of different types (by reading the comment of this parameter {string|TypedArray|DocumentInitParameters|PDFDataRangeTransport}) – Ben May 19 '15 at 08:36
  • Are you 100% sure that **local WebKit** does not have problems with access to local filesysystem (security?)? I tried your solution with (**file://**) now i get no error....but nothing else happens.... – Ben May 19 '15 at 08:41
  • Sorry, my edit was wrong. When you pasted the code, you broke the indentation. The `if` is inside the last `else`. A String with a URL should work. – Aaron Digulla May 19 '15 at 09:30
  • As i pointed out, i **cannot** use a string/url. When using (local) URL, nothing happends (like in Chrome when trying to access local files via JS.) Maybe same security restrictions in WebView? **Do you know how to create a JS Uint8Array from JAVA (what java type will be converted to Uint8Array)?** – Ben May 19 '15 at 09:35
  • Ah yes, [same-origin policy](https://en.wikipedia.org/wiki/Same-origin_policy). See my second edit. – Aaron Digulla May 19 '15 at 09:39
  • Thanks! I am getting closer, i think...After following your suggestion, i got a new error: `Error: Invalid PDF binary data: either typed array, string or array-like object is expected in the data property.` – Ben May 19 '15 at 09:54
  • Try if you can get `console.log()` to work. You need to examine what `_base64ToArrayBuffer()` returns and why it's not a typed array. And why it passes the check which you posted in your question but fails later. – Aaron Digulla May 19 '15 at 09:57
  • I edited my question (see **Edit 2** ) to add more details (like console.log()-output). – Ben May 19 '15 at 10:09
  • Hm. Try to replace `return bytes.buffer;` with `return bytes;` in `_base64ToArrayBuffer()`. – Aaron Digulla May 19 '15 at 10:17
  • OK, I did that. i had some more changes to do, to make it (almost) work. I needed only `var binary_string = window.atob(base64)` to pass to `getDocument({data: binary_string})`. Now everything seems to be fine (no errors, PDF canvas is created with correct size...), except -> canvas is totally empty :( – Ben May 19 '15 at 10:48
  • i replaced some JS-Libs, now it works (PDF is showing...OK is uses kind of chinese characters to display the PDF...but this is another problem...THX for your Help! i will accept your answer!) – Ben May 19 '15 at 11:04
  • I suggest to post the working code as an answer for the next person. Now for the PDF: Make sure it actually renders using a normal PDF reder, make sure that the Base64 encoding works be decoding the data and doing a binary compare of the two files (original and the copy which as encoded/decoded). – Aaron Digulla May 19 '15 at 11:05
  • I added my solution to the Question. Further: I have tested another PDF. This new PDF seems to be OK...i don't know what's the problem with the first PDF. – Ben May 19 '15 at 12:34