New: transition to a UI design inspired by Material Design (work in progress)
Version 3.0.0 comes with a slightly modified design. That's work in progress. The goal is to give you a highly customizable, easily themeable library with a fresh and modern look inspired my Material Design. As an unintended side effect, it resembles the redesign called "Photon" of pdf.js. Among other things, you'll soon be able to select between the light mode and the dark mode.
Talking of which: if you're an UX- or UI-designer with some time to spear, don't hesitate to send me suggestions how to improve the look and feel of the library. Or send me your complaints if you don't my attempts at UI-design. I can't promise to implement every suggestion, but I'm looking forward to your valuable feedback!
Showcase and manual
There's a showcase at https://pdfviewer.net. Check this page for live demos, source code examples, and a handbook.
Bringing PDF to the Angular world
This library provides an embeddable PDF viewer component. It's different from other approaches like ng2-pdf-viewer in that it shows the full suite of UI widgets. In other words, it strongly resembles the PDF viewer of your browser:
- Support for Internet Explorer 11
- Searching (including a programmatic API)
- Sidebar with thumbnails, outlines, and attachments
- Download and upload
- Zoom (with optional two-way binding to an attribute)
- Full-screen mode
- various selection tools
- standard display or even / odd spreads (like a book)
- several event listeners
- various approaches to scrolling (vertical, horizontal, "wrapped" scrolling)
- Internationalization (providing translations to several dozen languages)
- direct access to the core API of pdf.js (including TypeScript definition files)
- plus the ability to deactivate each of these features
- and/or to customize the toolbars and menus according to your needs.
Not to mention the ability to display PDF files, running on Mozilla's pdf.js 2.4.456, released in March 2020.
Potentially breaking changes with version 3.0.0
- I've fixed the behavior of
(zoom). Now this event is never triggered if the value of
"page-actual". It was always meant to work like this, but nonetheless, fixing this bug might break your application. If it does, use
- I've updated to the most current version of pdf.js (2.4.456). That's shouldn't break anything, but you never know, so I've included it in this list.
Expand to learn more about the other options to display PDF files in AngularIf you only need the base functionality, I'll happily pass you to [the project of Vadym Yatsyuk](https://github.com/vadimdez/ng2-pdf-viewer/). Vadym does a great job delivering a no-nonsense PDF viewer. However, if you need something that can easily pass as the native viewer on a gloomy day, ngx-extended-pdf-viewer is your friend.
There's also a direct counterpart to my library: ng2-pdfjs-viewer. As far as I can see, it's also a good library. Recently (May 24, 2019), it has been updated to PDF.js 2.2.171. It wraps the PDF viewer in an iFrame. That's a more reliable approach, but it also offers fewer options. The list of attributes is shorter, and the PDF viewer can't emit events to your application. If you're not happy with my library, check out ng2-pdfjs-viewer. It's a good library, too. Its unique selling point is displaying multiple PDF files simultaneously on the same page.
You might also try to use the native PDF viewer of your browser. That's a valid approach. Actually, it's even the preferred approach. The advantages of both
ng2-pdfjs-viewer are they also support Internet Explorer 11 (with some help of the polyfills).
ngx-extended-pdf-viewer also gives you a wide range of options that aren't available using the native API.
How to use the library
As a rule of thumb, I recommend to clone the showcase project from GitHub before doing anything else. It's a standard Angular CLI application, so you'll get it up and running in less than ten minutes. It's a good starting point to do your own experiments. Maybe even more important: you'll learn if the library works on your machine. (Of course it does, but it's always good to double-check!)
The detailed instructions for JHipster and Angular 2, 4, and 5 are available on the showcase.
Install the library with
npm i ngx-extended-pdf-viewer --save
Open the file
.angular-cli.jsonif you're using an older version of Angular) and configure Angular to copy the
assetsfolder of the library into the
assetsfolder of your application:
This simply copies the entire assets folder. If you're concerned about disk memory, you can omit the subfolders
additional-locale. If you need only one language, you can reduce the list to
locale.properties and your language folder.
Hint: There are two ways to define the language files needed for the labels of the buttons and screen elements of the PDF viewer. The second method is described below in the "internationalization" section.
- Now you can display the PDF file like so:
- If you want to display a PDF file you have downloaded from a server, you probably have a
Blobor a Base64 encoded string.
Blobscan be passed directly to the attribute
[src]. Base64 string must be passed to the attribute
Do you miss a configuration option? File an issue on the project bug tracker. If the base library pdf.js supports the requested option, I'll probably add it. BTW, you can speed up the process by providing a code snippet telling me how to implement the feature or by submitting a pull request.
- [(attribute)] describes an attribute with two-way-binding
- [attribute] means that PDF-viewer reacts when the attribute changes
- (attribute) means an event is raised when the user changes a setting
- attribute (without special characters) means the attribute is used at load time only. Subsequent changes are ignored.
Also see the attribute list on the showcase. It's the same list, just a little more useful because the showcase doesn't truncate the table on the right-hand side.
|[base64Src]||accepts a PDF file as base64 encoded string|
|(afterPrint)||This event is fired after the print jov has either been sent to the printer, or after the user has cancelled the print job. Note there's no way to tell printing from aborting the print.|
|(beforePrint)||This event is fired when the print is initiated. Note that this simply means the print preview window is opened. There's no way to predict if the user is actually going to print.|
|[contextMenuAllowed]||true||should the context menu show when the right-hand side button is clicked?|
|(currentZoomFactor)||n/a||fires each time the viewer changes the zoom level. The parameter is the numeric scale factor. Note that it's not a percentage; instead,
|delayFirstView||0||Number of milliseconds to wait between initializing the PDF viewer and loading the PDF file. Most users can let this parameter safely at it's default value of zero. Set this to 1000 or higher if you run into timing problems (typically caused by loading the locale files after the PDF files, so they are not available when the PDF viewer is initialized).|
|[enablePinchOnMobile]||false||By default, the pinch gesture zooms the entire window on mobile devices. If you prefer to zoom the PDF viewer only, you can activate
|[enablePrint]||true||Setting this flag to false disables printing the PDF file.|
|[filenameForDownload]||document.pdf||Allows the user to define the name of the file after clicking "download"|
|[(handTool)]||true||setting this flag to true, activates the "hand tool" to scroll the PDF file by dragging. Setting this to false activates the "text selection" tool. You can also use this flag as a two-way binding attribute. If you're only interested in the event, the event name is
|[height]||(see text)||define the height of the PDF window. By default, it's 100%. On most web pages, this results in a height of 0 pixels. In this case, the height is set to fill all the available space. More precisely, the all the space to the bottom of the window. If that's less then 100 pixel, the height is set to 100 pixel. Note that this is just an initial setting. It doesn't change when the window is resized.|
|[ignoreKeyboard]||false||if this flag is set to true, the PDF viewer ignores every keyboard event.|
|[ignoreKeys]||(none)||Accepts a comma-separated list of keys. For example, a legal value is
|[acceptKeys]||(none)||Accepts a comma-separated list of keys. For example, a legal value is
|imageResourcesPath||./assets/images||allows you to put the viewer's SVG file into an arbitrary folder.|
|language||undefined||Language of the UI. Must the the complete locale name, such as "es-ES" or "es-AR". It may be all lowercase.|
|logLevel||1||Log level. Legal values: VerbosityLevel.ERRORS (=0),VerbosityLevel.WARNINGS (=1), and VerbosityLevel.INFOS (=5). The higher the log level, the more is logged.|
|listenToURL||false||deactivates the URL listener of the PDF viewer. You can set it to "true" to allow for anchor tags like "#page=23" or "#nameddest=chapter_3". Only activate this flag if you don't use the anchor to encode the URLs for the router.|
|[nameddest]||undefined||allows you to jump to a "named destination" inside the document. Typical examples of names destinations are "chapter_7" oder "image_3". The named destination are defined within the PDF document; you can only jump to destinations defined by the author of the PDF file.|
|[showSidebarButton]||true||Show or hide the button to toggle the sidebar|
|[mobileFriendlyZoom]||100%||Increases the size of the UI elements so you can use them on small mobile devices. Must be a percentage (
|[password]||undefined||Allows you to pass a password programmatically. If you pass the wrong password, a red error message claiming "corrupt pdf file" is show in below the toolbar. Caveat: the password must be a string. During my test I accidentally used a numerical value. This fails even if the password consists only of digits. Please note that the password is stored in the main memory without encryption. If you're really serious about protecting your user's data from hackers, this might be a security issue.|
|[(page)]||undefined||two-way binding attribute to determine the page to display; more precisely:
|[(pageLabel)]||undefined||two-way binding attribute to determine the page to display; more precisely:
|(pagesLoaded)||undefined||emits the number of pages when a document is loaded; more precisely: emits an instance of
|(pageRendered)||undefined||fires each time a page is rendered. Emits an instance of
|(pdfDownloaded)||undefined||fires when a user downloads a document. Strictly speaking, it fires when they click the "download" button. Caveat: Even if the user cancels the download, the event is fired.|
|(pdfLoaded)||undefined||emits when the PDF file has been load successfully. The parameter $event is an
|(pdfLoadingFailed)||undefined||emits when trying to load and open a PDF file has failed. The parameter
|[printResolution]||150||set the print resolution in DPI. Sensible values are 300, 600, and maybe even 900. Note that higher values result in more memory consumption, more CPU uses, and may even cause browser crashes. During my tests setting the resolution to 1100 dpi worked, but 1150 failed. In my case, the browser seemed to ignore the print request without displaying any error message.|
|[showBorders]||false||By default, the PDF file is displayed with page borders. You hide them by setting
|[(rotation)]||0||[rotation] allows you to rotate every page. Note that this attribute is not responsible to rotate individual pages - it always rotates the entire document. (rotationChange) notifies you when the user rotates the document. This attribute can be used as a two-way binding attribute, i.e. [(rotation)]="angle". Legal values are 0, 90, 180, and 270. Every other value is ignored.|
|[showBookmarkButton]||true||Show or hide the "bookmark" button|
|[showDownloadButton]||true||Show or hide the "download" button (aka "save" button)|
|[showFindButton]||true||Show or hide the "find" button|
|[showHandToolButton]||false||Show or hide the "hand tool" menu item in the secondary toolbar. (The hand tool allows you to move the page by clicking and dragging). This activates also the opposite menu item: "text selection tool". As a side effect, the hand tool also activates the "text layer". This, in turn, enables marking text and highlighting search result.|
|[showOpenFileButton]||true||Show or hide the "open file" button|
|[showPagingButtons]||true||Show or hide the buttons to navigate between pages and the input field to navigate to a particular input field|
|[showPresentationModeButton]||true||Show or hide the "full screen" button|
|[showPrintButton]||true||Show or hide the "print" button|
|[showPropertiesButton]||true||Show or hide the "show document properties" menu item in the secondary toolbar|
|[showRotateButton]||true||Show or hide the "rotate" menu items in the secondary toolbar|
|[showScrollingButton]||true||show or hide the button switching between horizontal and vertical scrolling|
|[showSecondaryToolbarButton]||true||Show or hide the secondary toolbar (the menu hiding behind the arrows at the right-hand side)|
|[showSelectToolButton]||n/a||This attribute has been removed with version 0.9.47. Use [showHandToolButton] instead.|
|showSidebarOnLoad||undefined||If this flag is set to "false", the sidebar is hidden until the user clicks the "show sidebar" button. If this flag is set to "true", the sidebar is shown (unless
|[showSpreadButton]||true||Show or hide the "spread" menu items in the secondary toolbar. Also see the attribute
|showUnverifiedSignatures||false||Display electronic signatures. This feature is deactivated by default because the PDF viewer isn't able to verify the signature reliably yet (see https://github.com/mozilla/pdf.js/issues/1076). Please be aware that the signature may be manipulated. A warning message is also shown in the console log.|
|[showZoomButtons]||true||Show or hide the "zoom" button|
|[(sidebarVisible)]||undefined||Two-way-binding attribute determining whether the sidebar is visible. If you're only interested in the event, the event name is
|[(spread)]||off||determines if you're seeing one page or two pages at once (like a paper book).
|[startTabindex]||undefined||By default, the PDF viewer leaves it to the browser to determine the correct tab index. You can set the tab indexes explicitly by setting
|textLayer||undefined||allows you to activate or deactivate the text layer. If this flag is set to false, both the "find" button and the "select tool" are hidden. If you don't set this flag explicitly, it's automatically set by
|(textLayerRendered)||undefined||This callback is called when the text layer is rendered.|
|(updateFindMatchesCount)||undefined||This event is call during a find operation. Note that it can be called hundreds or even thousands of time, due to the asynchronous implementation of the find algorithm. It sends a
|(updateFindState)||undefined||This event is called during the find operations. It sends a
|useBrowserLocale||false||if true, the PDF viewer assumes the locale files are in the assets folder. If false, you are responsible for providing the translated texts.|
NgxExtendedPdfViewerService allows you to search programmatically. If the PDF viewer hasn't been initialized, or if it has already been destroyed, calling the service results in an error message on the console.
||finds a certain text. If the PDF viewer is not initialized, the method returns
||finds the next search result. Only call it after calling
||finds the previous search result. Only call it after calling
Expand to learn how to translate ngx-extended-pdf-viewer to 120+ languages
Slow default way
If you add the translation files to your project as described above in step 3, the PDF viewer uses the browser language setting to determine which language to load. First, it loads the
locale.properties, scans it for the desired language files, and loads the language file from the corresponding folder. That's two additional HTTP calls. That's slow, and it may even lead to errors if the network is already congested loading other resource files.
Don't forget to set the attribute
useBrowserLocale="true" if you follow this approach.
Slow way with custom translation files
If you want to use the slow way, but prefer to load the language files from a different URL, add a link to your application like so:
In this case, don't set
useBrowserLocale (or set it explicitly to false).
Inlining (aka embedding) the language files
Alternatively, you can provide the translations as a Json file. This Json file has to be part of an HTML page. That's especially useful if you need only one or two languages, because the are loaded a lot faster. To get familiar with this approach, embed the Json file in the
index.html like so:
node_modules/ngx-extended-pdf-viewer/assets/inline-locale-files contains snippet files you can simply copy into your HTML page.
Hint: You can also add the language definition in another HTML file. The bottom line is that the HTML snippet is already part of the DOM when the PDF viewer is initialized. Cluttering the root index file with the translations is an ugly and inflexible hack, but it works.
If you're using the "inline" approach, don't set
useBrowserLocale (or set it explicitly to
Feedback, pull requests and bug reports
Pull requests and bug reports are welcome. Please send them to the bug tracker of the project page: https://github.com/stephanrauh/ngx-extended-pdf-viewer/issues
Building the library from scratch (and updating to the latest version of Mozilla's pdf.js)
Have a look at this walkthrough.
License and Kudos
The license of the
ngx-extended-pdf-viewer is the Apache V2 license.
The library is based on https://github.com/mozilla/pdf.js, which has been published under an Apache V2 license.
Some of the default icons have been published under a SIL Open Font License 1.1 license at Material Design Icons. The other icons have either been published under an Apache V2 license by Google or by the pdf.js team at Mozilla.
Thanks to the awesome pdf.js team and to all the users you've reported bugs and even sent me pull requests!