Recently a question was raised by one of the MVP’s if somebody has an example of a PDF viewer in Business Central. That reminded me that I actually had an example, created almost one year ago. Should have published it way earlier! Anyway, I thought it still makes sense to polish that thing and share it.
However, the browser will use whichever PDF reader is installed on the system and there is no way to customize the look and feel. There is only a very limited set of API’s available and even these API’s are not supported in all browsers. In other words, how the PDF is rendered on the page is out of your control.
So I decided to move on and look for a solution that gives you full control over how the PDF is rendered, including an API to integrate with. And I stumbled over PDF.js, an open-source PDF viewer, built with HTML5 and supported by Mozilla Labs.
This tool proved to be very flexible and I got it to work pretty quickly. In the first place as a single page viewer, as explained in the examples. However, the option were still quite limited and I figured that it would be great to take the viewer, which has all the options you can think of, and embed that one into Business Central. Not just as-is, but integrated with Busines Central, and with a little re-skin applied, like they ask here: However, we do ask if you plan to embed the viewer in your own site, that it not just be an unmodified version. Please re-skin it or build upon it.
Allright, enough talking, let’s first look at the result and then see how it’s done.
And the same control can also be used in a factbox, without any modifications:
And now straight to the source code. The complete code can be found on GitHub: https://github.com/ajkauffmann/PDFViewer
Please read the explanation below how to use it, before you jump in and start using it right away!
The GitHub repository consists of two folders:
- PDFViewer: Business Central extension
- pdfjs-2.0.943-dist: source code for a static HTML website
If you clone the repo, then you will find a PDFViewer.code-workspace file in the root. Open this file with VS Code and you will find both folders opened at once. Alternatively, you can open the folders individually.
Let’s first look at the folder pdfjs-2.0.943-dist. This is a download of the latest version of the viewer application, which can be found here. Alternatively, you can clone the full repo on GitHub, with all source code and build the viewer yourself by using gulp. But if you are not used to that, it’s easier to download the distributable file of the viewer and use that one.
So I decided to just grab the whole PDF.js viewer application and host it on Azure. I have used static website hosting in Azure storage. This is an option with Azure storage with no additional cost. Azure blob storage is pretty cheap, so it was a no-brainer. With extra options like CDN and custom domain names it is even more attractive.
So this is what I did: after modifying the PDF web viewer application to integrate with Business Central (see below) I have created a static website with the Azure portal as explained here. Then I uploaded the files using the Azure Storage Explorer. I have now the PDF web viewer application hosted here: https://bcpdfviewer.z6.web.core.windows.net/web/viewer.html. As a result, the AL extension is pretty small and easy to understand.
Warning: don’t use my PDF web viewer website for any production scenario! Ue your own hosting website instead!
After I figured this hosting thing out, there were some other hurdles to take. I will save some of them for another blog post and just focus on the parts to integrate the web viewer application with Business Central. I will first go over the modifications I did to the web viewer application and then switch to the AL code.
This bcintegration.js must be added to the viewer.html page.
The viewer.css defines the look and feel of the viewer. I’ve made a few changes to better integrate the viewer into Business Central. Feel free to play with it and change to whatever you like. Look at the history of the file to see what changes I did.
That’s it, let’s now have a look at the AL code
The most important part you need to understand is how exactly the web viewer is embedded in the page. The web viewer is loaded in a nested <iframe> element that is added to the control add-in element. This is done in script.js.
Because it is not possible to directly communicate between the iframe, which has a different url then the parent, and the parent object, the script.js file implements to use postMessage and onMessage. This is a safe way to communicate between the two and supported by all browsers.
In the top of the script.js file, the url of the website where the PDF web viewer is hosted is specified. You should change this to the website where you have hosted your version of the web viewer.
Don’t use a trailing slash for the url setting. It wil fail the test in the onMessage function.
The rest of the code is just tableextensions, pages and pageextensions to add the viewer to the Incoming Document pages. I guess you will be able to figure out yourself what’s going on there.
Thanks Mohana for raising the question and testing the first release!
Get luck with it and please let me know what you think! Feel free to grab the GitHub repository and to contribute to it if you have something cool to share.
Nice! I used pdfjs also a couple of months ago to create a control-addin :-).
Did you create the viewer from scratch or also use the web viewer from pdfjs?
I created a very simple viewer from scratch with only prev/next buttons. So no problem to create a regular control-addin (and loading pdfjs from a cdn). But if you want to have a more full fledged viewer, your idea to host it elsewhere is a good idea.
I consider to write another blog post about using pdf.js in that way. Prev/next button could be implemented on the page in AL instead of in HTML.
And very nice to see that you use the same library!
Pingback: Controlling the size of a Control Add-In - Kauffmann @ Dynamics NAV - Dynamics 365 Business Central/NAV User Group - Dynamics User Group
Nice blog post – do you know how to control the width of such a PDF viewer if you place it on the factbox area?
I would like to have a repeater list on left side, and PDF preview taking 1/3 of screen width, this is impossible with factbox default width.
I tried so many things 🙂
I don’t think you can do that with the factbox area. Maybe, just maybe, it can work with putting a page part in the content area in a grid control. Never tried that, and I really don’t know if it would be possible at all.
On windows client it’s easy – just drag the factbox width out on those pages you have PDF viewer – I’ve done that for years, and works very well. Also with this example here.
Hi, I have downloaded the PDF Viewer and tested in Business central extension, it works in web browsers, but when i used in Business central Apps (Windows 10) then Download and Print button is not working.
Can you suggest us what’s the changes are required in this solution.
The apps don’t fully support control add ins, I wouldn’t waste my time trying to make that work.
Can this work with throughout BC “Attachments” area or only on “Incoming Documents”? I am not a developer but would like to know if this can be used within the “Attachments” of Customers, Sales Orders, etc.
Can the auto PDF Viewer work within the “Attachments” section of BC (Customers, Sales Orders and etc) or only within “Incoming Documents”? I need functionality this for the “Attachments” in BC.
It works with any page. Just feed it with the PDF data.
I also have similar question as Bergamini,
The blog you post has a good knowledge thanks for sharing such a good stuff.
I had tried this code to change for using it in document attachment. but I din’t get success.
In “Incoming Document” table there is one BLOB field “Content” which you used to pass through the Base64content. but in “Document attachement” table there is no Blob field which can be used to create the instream.
I tried to do the same with media type field which is actually returens no data in this case becuase media is importing and exporting the data into or from database directly.
If Possible, Could you please help out for some workarrount to achieve the code for document attachment table.
Yes, we are having the similar issue when working with the “Attachments”. Seems to work with Incoming Documents but not Attachments.
Can you suggest how this will work with Attachments in BC? We see how it works with Incoming Documents as your example shows but cannot see how it works with Attachments. Suggestions?
Yes, but I’ve some deadlines first. Takes me two weeks, and then I’ll pick this up.
Hello AJK, Any further insight on this issue with using your code within BC “Attachments”?
AJK, I thought I would check in one more time to see if you can help guide how it can be done with BC Attachments.
Did anyone find the solution to be able to use it will BC attachments?
Working on it, stay tuned! I’m redesigning the extension to support both Incoming Documents and Document Attachments. The prototype already works, I just need to do some plumbing to get a clean extension.
That is great! Thank you very much
The repo on GitHub has been updated with a completely redesigned PDF Viewer.
It is now based on v18 and uses some of the new features. But it can easily be downgraded to v17.