This site is currently read-only as we are migrating to Oracle Forums for an improved community experience. You will not be able to initiate activity until January 30th, when you will be able to use this site as normal.

    Forum Stats

  • 3,889,958 Users
  • 2,269,775 Discussions
  • 7,916,823 Comments

Discussions

Dynamically displaying PDF in a region

jayp941
jayp941 Member Posts: 3 Green Ribbon
edited Oct 18, 2019 7:25AM in APEX Discussions

Hello,

I am relatively new to Apex, and I have been trying to solve this problem for the last few days on and off, with no actual results. So I have decided to ask the experts here for help.

So what I am going to have in this part of the application (I am testing in an isolated mock-environment) is an Interactive Report filled with files (mostly .pdf).

The goal is to have a region on the same page, which displays the currently selected pdf using the browsers PDF viewer (this makes ApexPdfRenderer unsuitable, since it only loads the pages of the pdf as images and provides pagination).

Now, as you can see, I am somewhat close to solving this, yet farther away than ever before.  What I have done so far:

1. Created a Dynamic Action and bound it to the file_name - link. It contains the following js-code:

console.log('this', this);var id = this.data.id;console.log(id);function getData() {    let result;console.log(apex.server);    apex.server.process(        "download_my_file", //PL/SQL Procedure (See 2)        { x01: id }, //ID of the file        {             dataType: 'text', //Using anything else than json or html return an error (for JSON: "SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data")            async: false,            success: (pData) => {                console.log(pData);                let blob = new Blob([pData], {type: 'application/pdf'});                console.log(blob);                document.getElementById('pdf').src = URL.createObjectURL(blob);                result = blob;             }        }    );    return result;}getData();

2. download_my_file is a Process/Ajax-Callback...

pastedImage_4.png

... Which executes the following PL/SQL Procedure:

CREATE OR REPLACE PROCEDURE download_my_file(p_file in number) AS      v_mime  VARCHAR2(48);      v_length  NUMBER;      v_file_name VARCHAR2(2000);      Lob_loc  BLOB;  BEGIN      SELECT FILE_MIMETYPE, FILE_BLOB, filename, DBMS_LOB.GETLENGTH(file_blob)  INTO     v_mime,lob_loc,v_file_name,v_length  FROM EBA_DEMO_FILES  WHERE id = p_file;  --  -- set up HTTP header  --  -- use an NVL around the mime type and  -- if it is a null set it to application/octect  -- application/octect may launch a download window from windows  owa_util.mime_header( nvl(v_mime,'application/octet'), FALSE );  -- set the size so the browser knows how much to download  htp.p('Content-length: ' || v_length);  -- the filename will be used by the browser if the users does a save as  htp.p('Content-Disposition:  inline; filename="'||replace(replace(substr(v_file_name,instr(v_file_name,'/')+1),chr(10),null),chr(13),null)|| '"');  -- close the headers             owa_util.http_header_close;  -- download the BLOB  wpg_docload.download_file( Lob_loc );  end download_my_file;

The result of all of this is a pdf being loaded into the specified region when the filename is clicked. However, all pages are blank. Yet, the number of pages is correct. The console shows the following errors (Firefox):

pastedImage_7.png

Altough there are a lot of resources for this problem on the Internet, most of them either

A. Use a static approach instead of a dynamic one

B. Contradict themselves

C. Only work with some browsers

D. All of the above

So I am now looking for a definitive answer to this problem. I feel like I am very close to achieving it, yet the way I am doing it doesn't seem to be the most efficient one. So I have come here, looking for guidance.

Thank you in advance!

Tagged:

Best Answer

  • jayp941
    jayp941 Member Posts: 3 Green Ribbon
    edited Oct 16, 2019 9:55AM Answer ✓

    So, I have found a solution for the problem. I am shocked at how little information there is regarding how to set this up, since it seems like a pretty common use-case to me.

    Regardless, here is a step-by-step tutorial:

    Fully-Functional, dynamically loaded PDF Viewer in Oracle APEX

    This Tutorial explains how you can build and implement an easy PDF Viewer in Oracle Apex utilizing Guidelines by PDFObject to fully support current browsers as well as Apex's RESTful Services for Data Delivery. The tutorial makes use of newer Web APIs such as Fetch, which might be exchanged with XMLHttpRequest. The browser should also support Blobs.

    The documentation is based on the Sample File Upload and Download which can be installed using the App Gallery

    The HTML

    After getting into the Application Builder open the page you want your PDF Viewer to be in (in this tutorial it is Page 3 - Files):

    pastedImage_0.pngAfter opening the Page Select where you want to place your PDF Viewer, in our case it is below the rest of the content, therefore we create a subregion in one of the parent containers of the page:

    pastedImage_1.png

    We call it PDF Viewer for simplicity:

    pastedImage_2.png

    Leave the Object as Type Static Content. In the source paste the following Code:

    <div id="pdfViewer" style="width: 100%; height: 500px">    <iframe src="" style="border: none;" type="application/pdf" width="100%" height="100%"></iframe></div>

    We are adhering to the implementation of PDFObject found at the very bottom of this page: https://pdfobject.com/static

    Both the id and the style attribute for the top level div container can be changed, but keep in mind that this has to be carried over to later parts of the tutorial as well. This tutorial will use the values seen here.

    The Region Window should now look something like this:

    pastedImage_5.png

    The Javascript

    Next up we have to add a few Javascript Statements to ensure that the RESTful Service that will be implemented later is called correctly.

    First up we will create a dynamic action to trigger the RESTful service and to act as a link between the user interaction and the service.

    For that select the Dynamic Actions Tab Item in the Application:

    pastedImage_6.png

    Right-click on Event and create a new Action:

    pastedImage_7.png

    As Identification we will use PDFViewer for simplicity. In the When Block change the Event type to Custom (at the very bottom of the list). A new Input Bar should appear that asks for a Custom Event, which we will call PDFViewer as well. As Selection Type select JavaScript Expression. In the Javascript Expression Textbox enter document.

    This should look something like this:

    pastedImage_8.png

    Next step is to go into the True Block:

    pastedImage_9.png

    In the Identification Block change the Value of Action to Execute JavaScript Code

    In the Settings Block below change the Value of Code to the following: 

    let id = this.data.id;async function getData(){    let afetch = await fetch(`http://localhost:8080/ords/obe/pdf/pdfs/${id}`);    let blob = await afetch.blob();    let blobUrl = URL.createObjectURL(blob);    let pdfContainer = document.getElementById('pdfViewer');    pdfContainer.getElementsByTagName('iframe')[0].src = blobUrl;}getData();

    The rest can be left as is.

    This should now look something like this:

    pastedImage_12.png

    As we are using the Sample File Upload and Download we have a table with all the PDFs in the database. We are going to edit the current link to the details of the document (placed on the Filename Column) to reflect a call to a dynamic action that we created earlier. For that scroll to the Link block and change the Target value to:

    • Type: URL
    • javascript:apex.event.trigger(document, 'PDFViewer', [{id:'#ID#'}]);
      void(0);
    • PDFViewer is the Dynamic Action we created earlier

    pastedImage_18.png

    The RESTful Service

    The last step is to create a RESTful Service. You do that by going to the RESTful Services Page

    pastedImage_19.png

    If you see something like this register the schema, keep the default values:

    pastedImage_20.png

    Create a new Module:

    pastedImage_21.png

    We are using a very simple Module for gathering the PDF which we call PDFViewer and use pdf as Base Path.

    Clicking on Create you will get the following menu:

    pastedImage_23.png

    Scroll down to Create Template and press the button:

    Click Create Handler:

    pastedImage_26.png

    Here you have the change the Source Type add the Source:

    SELECT FILE_MIMETYPE, FILE_BLOBFROM EBA_DEMO_FILESWHERE ID = :id

    Please change the Select Statement accordingly. You need the mimetype as well as blob as return statement. After that press Create Handler.

    The view on the left side should look like this now:

    pastedImage_33.png

    Running the application will look like this:

    pastedImage_34.png

    Youcef KazarCesar Berrio

Answers

  • Pierre Yotti
    Pierre Yotti Senior Oracle Consultant GermanyMember Posts: 4,046 Bronze Crown
    edited Oct 15, 2019 2:07PM
  • jayp941
    jayp941 Member Posts: 3 Green Ribbon
    edited Oct 15, 2019 3:42PM

    Unfortunately, this is not a suitable solution. As I have mentioned, something like ApexPDFRenderer which, just like your example, provides no functionality beyond going from page to page, is not

    what I am looking for.

    What I need is a fully functional PDF-Viewer, like the one browser use by default

    https://stackoverflow.com/questions/14081128/how-to-embed-a-pdf-viewer-in-a-page

    The problem lies with dynamically getting the .pdf-file from the database. Meaning: I can change the contents of the PDF-viewer below the IR by clicking on different elements in the IR, and the page will not reload.

    https://joelkallman.blogspot.com/2014/03/yet-another-post-how-to-link-to.html

    I am currently trying to get this approach to work, without much success...

  • Pierre Yotti
    Pierre Yotti Senior Oracle Consultant GermanyMember Posts: 4,046 Bronze Crown
    edited Oct 15, 2019 3:49PM

    You can make a demo and post the workspace/username/password here. We will take a look at it and

    help you to make it works.

  • jayp941
    jayp941 Member Posts: 3 Green Ribbon
    edited Oct 16, 2019 9:55AM Answer ✓

    So, I have found a solution for the problem. I am shocked at how little information there is regarding how to set this up, since it seems like a pretty common use-case to me.

    Regardless, here is a step-by-step tutorial:

    Fully-Functional, dynamically loaded PDF Viewer in Oracle APEX

    This Tutorial explains how you can build and implement an easy PDF Viewer in Oracle Apex utilizing Guidelines by PDFObject to fully support current browsers as well as Apex's RESTful Services for Data Delivery. The tutorial makes use of newer Web APIs such as Fetch, which might be exchanged with XMLHttpRequest. The browser should also support Blobs.

    The documentation is based on the Sample File Upload and Download which can be installed using the App Gallery

    The HTML

    After getting into the Application Builder open the page you want your PDF Viewer to be in (in this tutorial it is Page 3 - Files):

    pastedImage_0.pngAfter opening the Page Select where you want to place your PDF Viewer, in our case it is below the rest of the content, therefore we create a subregion in one of the parent containers of the page:

    pastedImage_1.png

    We call it PDF Viewer for simplicity:

    pastedImage_2.png

    Leave the Object as Type Static Content. In the source paste the following Code:

    <div id="pdfViewer" style="width: 100%; height: 500px">    <iframe src="" style="border: none;" type="application/pdf" width="100%" height="100%"></iframe></div>

    We are adhering to the implementation of PDFObject found at the very bottom of this page: https://pdfobject.com/static

    Both the id and the style attribute for the top level div container can be changed, but keep in mind that this has to be carried over to later parts of the tutorial as well. This tutorial will use the values seen here.

    The Region Window should now look something like this:

    pastedImage_5.png

    The Javascript

    Next up we have to add a few Javascript Statements to ensure that the RESTful Service that will be implemented later is called correctly.

    First up we will create a dynamic action to trigger the RESTful service and to act as a link between the user interaction and the service.

    For that select the Dynamic Actions Tab Item in the Application:

    pastedImage_6.png

    Right-click on Event and create a new Action:

    pastedImage_7.png

    As Identification we will use PDFViewer for simplicity. In the When Block change the Event type to Custom (at the very bottom of the list). A new Input Bar should appear that asks for a Custom Event, which we will call PDFViewer as well. As Selection Type select JavaScript Expression. In the Javascript Expression Textbox enter document.

    This should look something like this:

    pastedImage_8.png

    Next step is to go into the True Block:

    pastedImage_9.png

    In the Identification Block change the Value of Action to Execute JavaScript Code

    In the Settings Block below change the Value of Code to the following: 

    let id = this.data.id;async function getData(){    let afetch = await fetch(`http://localhost:8080/ords/obe/pdf/pdfs/${id}`);    let blob = await afetch.blob();    let blobUrl = URL.createObjectURL(blob);    let pdfContainer = document.getElementById('pdfViewer');    pdfContainer.getElementsByTagName('iframe')[0].src = blobUrl;}getData();

    The rest can be left as is.

    This should now look something like this:

    pastedImage_12.png

    As we are using the Sample File Upload and Download we have a table with all the PDFs in the database. We are going to edit the current link to the details of the document (placed on the Filename Column) to reflect a call to a dynamic action that we created earlier. For that scroll to the Link block and change the Target value to:

    • Type: URL
    • javascript:apex.event.trigger(document, 'PDFViewer', [{id:'#ID#'}]);
      void(0);
    • PDFViewer is the Dynamic Action we created earlier

    pastedImage_18.png

    The RESTful Service

    The last step is to create a RESTful Service. You do that by going to the RESTful Services Page

    pastedImage_19.png

    If you see something like this register the schema, keep the default values:

    pastedImage_20.png

    Create a new Module:

    pastedImage_21.png

    We are using a very simple Module for gathering the PDF which we call PDFViewer and use pdf as Base Path.

    Clicking on Create you will get the following menu:

    pastedImage_23.png

    Scroll down to Create Template and press the button:

    Click Create Handler:

    pastedImage_26.png

    Here you have the change the Source Type add the Source:

    SELECT FILE_MIMETYPE, FILE_BLOBFROM EBA_DEMO_FILESWHERE ID = :id

    Please change the Select Statement accordingly. You need the mimetype as well as blob as return statement. After that press Create Handler.

    The view on the left side should look like this now:

    pastedImage_33.png

    Running the application will look like this:

    pastedImage_34.png

    Youcef KazarCesar Berrio
  • Natalie G
    Natalie G Member Posts: 225 Bronze Badge
    edited Oct 16, 2019 3:42PM

    Thank you for the tutorial. That will work in Chrome and Firefox, but Edge won't display the PDF toolbar if you load the PDF in an iframe. Those users won't have a "fully functional PDF-Viewer, like the one browser use by default".

    jayp941 wrote:I am shocked at how little information there is regarding how to set this up

    https://www.google.com/search?q=oracle+apex+pdf+preview

      > "Oracle Apex - PDF Display" [Oct 9, 2018]

        > [Using <embed> ]

      > "Oracle Apex PDF Viewer" [Oct 26, 2018]

        > https://apex.oracle.com/pls/apex/f?p=34781 (using an On Demand process + pdf.js +)

    https://www.google.com/search?q=oracle+apex+iframe+pdf

      > "displaying contents of a pdf on a region" [Aug 9, 2006]

    https://www.google.com/search?q=oracle+apex+blob+url

      > "Download a BLOB image as file, how?" [Sep 13, 2016]

        > "Yet Another Post How to Link to Download a File or Display an Image from a BLOB column" [March 20, 2014] "It's definitely easier to implement via a RESTful Service, and ... will always be much faster.... However, one benefit of doing this via an On Demand process is that it will also be constrained by any conditions or authorization schemes that are in place for your APEX application"

          > "Using RESTful to deliver Images for APEX" [July 24, 2013]

  • Satish Yadav
    Satish Yadav Member Posts: 19 Red Ribbon
    edited Oct 18, 2019 7:25AM

    Hi,

    https://www.youtube.com/watch?v=cyN6rrawd74

    Please follow this video to the show table store pdf into a particular region.

  • User_GTW5U
    User_GTW5U Member Posts: 1 Green Ribbon

    I run APEX 21.2 and followed your tutorial, but it is not working.

    I can see following error in console.log

    DevTools failed to load source map: Could not load content for http://localhost:8080/i/libraries/apex/minified/ojtimezonedata.js.map;: HTTP error: status code 404, net::ERR_HTTP_RESPONSE_CODE_FAILURE

    I also went to find ojtimezonedata.js.map, but there is no such file in this dir.

  • Youcef Kazar
    Youcef Kazar Member Posts: 16 Red Ribbon

    Hello,

    I'm running APEX 21.1 and I followed every step but when i click on the filename to render the pdf i get the following error on the static region

    • The request could not be processed because an error occurred whilst attempting to evaluate the SQL statement associated with this resource. Please check the SQL statement is correctly formed and executes without error. SQL Error Code: 1722, Error Message: ORA-01722: invalid number


  • Cesar Berrio
    Cesar Berrio Bogota, ColombiaMember Posts: 9 Red Ribbon

    Thank you very much for the answer and complete explanation, the procedure works very well and is displayed without any problem in both Chrome and Microsoft Edge 👍️

    According to my version of Rest and APEX, I changed the API a bit, but it was minimal.

    ::

    Muchas gracias!!

    Thank you very much..