Available Now: Explore our latest release with enhanced accessibility and powerful IDP features

Build an Angular PDF Viewer with PDF.js

By Apryse | 2018 Nov 26

Sanity Image
Read time

5 min

Angular is an open-source, JavaScript framework based on TypeScript, and is used to create web applications. PDF.js is a general purpose JavaScript library for parsing and rendering PDFs.

In this post, you'll see how to use an open-source library based off PDF.js within the Angular framework to build your own PDF viewer. We’ll look at what else you can do when you integrate the Apryse WebViewer into our Angular project.

You'll also see how to display PDFs in Angular 5+ using the PDF.js-based ng2-pdf-viewer component and then wrapping it with a custom UI.

Need more clarification why PDF.js is a better choice for you? Check out this blog.

The Angular PDF Viewer includes a handful of widgets, making it ideal for basic PDF viewing use cases:

  • Opening a local file
  • Toggling document outline
  • Rotating left/right
  • Zooming in/out
  • Toggling fit page
  • Toggling the display of all pages
  • Previous/next page navigation
  • Searching text

The source code for this project is available in our Git repo.

PDF.js vs. Apryse

Copied to clipboard

PDF.js is a good option when you need a fast, low-cost solution for viewing small and simple PDFs on the web. Installation requires NPM, NGX and NG2 to repackage PDF.js, and the out-of-the-box UI is provided by Mozilla. It comes with a basic set of features – such as zoom, pagination, and search – that should suffice for projects that don’t require a heavy set of functionality. Plus, most of its dependencies rely on universal web standards, while its UI can be restyled quickly using CSS and HTML.

However, PDF.js may be limiting for anyone who needs more complex features or support. After surveying some current organizations that came to us after trying PDF.js, here are the top reasons they made the switch:

  • Functionality: They wanted to view a range of documents, from PDFs to Word docs, CAD projects, images, and video. Other considered features include annotations, rich search capabilities, forms and signatures, page encryption, and more.
  • Reliability: A need for robust support and efficient memory usage for even large and complex documents. Note: Search on default PDF.js does not pick up word breaks if the word goes to the next paragraph.
  • Customizability: Control over functionality, behavior, and appearance, and the ability to add or remove features to reflect the brand and UX they wanted.
  • Performance: Features to optimize documents, compress images, and reduce document complexity, as well as linearized PDF to stream content into the browser and experience blazing-fast display times.
  • Support: Some dedicated technical support and professional services to smooth out the process of integrating, developing, and customizing the viewer.

Open-Source Angular PDF Viewer Libraries

Copied to clipboard

There are several good open-source Angular PDF viewer libraries available. Two of the most prominent include:

  • Ngx-extended-pdf-viewer: With around 47k weekly downloads on NPM, this ranks among the most popular Angular PDF libraries. It comes with a full suite of UI widgets, a programmatic API, and many other features.
  • Ng2-pdf-viewer: This has more than 136k weekly downloads on NPM, making it the most popular library. While it only comes with a handful of widgets, it is ideal for those with only basic PDF viewing needs.

For this tutorial, we’ll use the ng2-pdf-viewer because it offers a good use case for a broad number of users. Here’s what it looks like:

Angular PDF.js PDF Viewer

Step 1 - Set Up Your Project

Copied to clipboard

Once you've created the directory for your project, clone and launch the repo from the Node command line:

# command line
git clone https://github.com/PDFTron/ng5-pdf-viewer-ui.git
cd ng5-pdf-viewer-ui
npm i
ng serve --open

Change the PDF file that's loaded by default in the viewer by replacing the pdftron-sdk.pdf filename in app.component.ts:

// file: ng5-pdf-viewer-ui/src/app/app.component.ts
pdfSrc: string | PDFSource | ArrayBuffer = '../assets/pdftron-sdk.pdf';

You now have a basic PDF viewer! The next step is customizing the UI.

Step 2 - Configuration

Copied to clipboard

The viewer has numerous configurable options in app.component.html:

<! -- file: ng5-pdf-viewer-ui/src/app/app.component.html -- >
  <pdf-viewer
    [src]="pdfSrc"
    [(page)]="page"
    [rotation]="rotation"
    [original-size]="originalSize"
    [fit-to-page]="fitToPage"
    (after-load-complete)="afterLoadComplete($event)"
    [zoom]="zoom"
    [show-all]="showAll"
    [stick-to-page]="stickToPage"
    [render-text]="renderText"
    [external-link-target]="'blank'"
    [autoresize]="autoresize"
    (error)="onError($event)"
    (on-progress)="onProgress($event)"
    (page-rendered)="pageRendered($event)"
  ></pdf-viewer>

We won't drill into this because the README for ng2-pdf-viewer explains this thoroughly.

Step 3 - Removing Widgets From the UI

Copied to clipboard

The code for displaying widgets is located in app.component.html, with each widget wrapped inside <span> tags. To remove (or re-order) the widgets, find the tag and adjust accordingly.

<! -- file: ng5-pdf-viewer-ui/src/app/app.component.html -- >
<input
  (change)="onFileSelected()"
  type="file"
  id="file"
  class="d-none"
  accept=".pdf"
/>
<div class="toolbar d-flex">
  <span
    class="my-icon"
    [inlineSVG]="'/assets/icon/baseline-open_in_browser-24px.svg'"
    (click)="openLocalFile()"
    title="Open File"
  ></span>
  <span
    class="my-icon"
    [ngClass]="{ active: isOutlineShown }"
    [inlineSVG]="'/assets/icon/baseline-toc-24px.svg'"
    (click)="toggleOutline()"
    title="Toggle Outline"
  ></span>
  <span
    class="my-icon"
    [inlineSVG]="'/assets/icon/baseline-rotate_left-24px.svg'"
    (click)="rotate(-90)"
    title="Rotate Left"
  ></span>
  <span
    class="my-icon"
    [inlineSVG]="'/assets/icon/baseline-rotate_right-24px.svg'"
    (click)="rotate(90)"
    title="Rotate Right"
  ></span>
  <span
    class="my-icon"
    [inlineSVG]="'/assets/icon/baseline-zoom_in-24px.svg'"
    (click)="incrementZoom(0.1)"
    title="Zoom In"
  ></span>
  <span
    class="my-icon"
    [inlineSVG]="'/assets/icon/baseline-zoom_out-24px.svg'"
    (click)="incrementZoom(-0.1)"
    title="Zoom Out"
  ></span>
  <span
    class="my-icon"
    [ngClass]="{ active: fitToPage }"
    [inlineSVG]="'/assets/icon/baseline-insert_drive_file-24px.svg'"
    (click)="fitToPage = !fitToPage"
    title="Toggle Fit Page"
  ></span>
  <span
    class="my-icon"
    [ngClass]="{ active: showAll }"
    [inlineSVG]="'/assets/icon/baseline-multi-pages-24px.svg'"
    (click)="showAll = !showAll"
    title="Toggle Show All"
  ></span>

  <ng-container *ngIf="!showAll">
    <span
      class="my-icon ml-auto"
      [inlineSVG]="'/assets/icon/baseline-navigate_before-24px.svg'"
      (click)="incrementPage(-1)"
      title="Previous Page"
    ></span>
    <div>
      <input
        type="number"
        class="page-num"
        placeholder="Page"
        [(ngModel)]="page"
        pattern="-?[0-9]*(\.[0-9]+)?"
      />
      <span *ngIf="pdf">of {{ pdf.numPages }}</span>
    </div>
    <span
      class="my-icon"
      [inlineSVG]="'/assets/icon/baseline-navigate_next-24px.svg'"
      (click)="incrementPage(1)"
      title="Next Page"
    ></span>
  </ng-container>

  <label
    class="my-icon ml-auto"
    [inlineSVG]="'/assets/icon/baseline-search-24px.svg'"
    for="searchbox"
    title="Search.."
  ></label>
  <input
    #queryInp
    type="text"
    id="searchbox"
    name="searchbox"
    class="searchbox mr-5"
    placeholder="Search..."
    [value]="pdfQuery"
    (input)="searchQueryChanged($event.target.value)"
    (keyup.enter)="searchQueryChanged(queryInp.value)"
  />
</div>

For example, delete this code to remove the Document Outline toggle:

<! -- file: ng5-pdf-viewer-ui/src/app/app.component.html -- >
  <span
    class="my-icon"
    [ngClass]="{ active: isOutlineShown }"
    [inlineSVG]="'/assets/icon/baseline-toc-24px.svg'"
    (click)="toggleOutline()"
    title="Toggle Outline"
  ></span>

Step 4 - Replacing Widget Icons

Copied to clipboard

Each widget icon is referenced individually from app.component.html, so it's easy to swap out the SVG with another image:

<! -- file: ng5-pdf-viewer-ui/src/app/app.component.html -- >
  <span
    class="my-icon"
    [inlineSVG]="'/assets/icon/baseline-open_in_browser-24px.svg'"
    (click)="openLocalFile()"
    title="Open File"
  ></span>

In the example above, replace /assets/icon/baseline-open_in_browser-24px.svg with your preferred image, or swap it out with Angular's mat-icon component (read more).

Step 5 - Changing the Widget or Toolbar Styling

Copied to clipboard

Style widgets and the toolbar in the app.component.scss file. You can adjust the toolbar background, widget sizing or padding, the search box transition animation, and more.

/* file: ng5-pdf-viewer-ui/src/app/app.component.scss  */
.toolbar {
  background: #303030;
  padding: 10px;
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  z-index: 9999;
  span {
    color: white;
  }
}
.my-icon {
  margin: 0 7px;
  cursor: pointer;
  &:hover {
    opacity: 0.5;
  }
  &.active {
    opacity: 0.5;
  }
  &.active:hover {
    opacity: 1;
  }
}
.page-num {
  width: 50px;
  border: 0;
  margin-right: 2px;
  text-align: center;
}
.searchbox {
  width: 0;
  opacity: 0;
  transition: all 0.3s ease-out;
  border: 0px;
  padding: 0 3px;
  &:focus,
  &:active {
    outline: 0;
    box-shadow: none;
  }
}
.searchbox:focus {
  opacity: 1;
  width: 200px;
}
.pdf-body {
  margin-top: 46px;
}
input {
  &:focus,
  &:active {
    outline: 0;
    box-shadow: none;
  }
}
.outline {
  position: fixed;
  left: 0;
  top: 0;
  padding: 60px 5px 5px;
  height: 100vh;
  width: 250px;
  z-index: 9000;
  box-shadow: 3px 0px 5px 0px rgba(0, 0, 0, 0.75);
  background: white;
  ul {
    padding-left: 25px;
  }
}

And that's it⁠— you're done! You now have a basic Angular PDF viewer that matches your app's look and feel.

This is a great solution for basic viewing capabilities, where you're not concerned with rendering inconsistencies or failures in PDF.js Angular. If you need more robust functionality, such as annotation, form filling, or other features, you must implement them yourself. See our Guide to Evaluating PDF.js to learn more.

For 100% rendering reliability, hundreds of features out-of-the-box, and more granular control of UX, consider Apryse WebViewer, a JavaScript PDF Viewer:

Check out our online demo.

Additionally, it's easy to integrate WebViewer into your Angular project.

Integrating Apryse WebViewer Into Angular Projects

Copied to clipboard

Start by cloning our WebViewer Angular sample project:

# command line
git clone https://github.com/PDFTron/webviewer-angular-sample.git
cd webviewer-angular-sample

Download the latest version of WebViewer. Once downloaded, copy the files from the WebViewer/lib folder and paste them to your project's src/assets/webviewer folder.

Then, from the command line, enter:

# command line
npm install
npm start

And that's it! You've now got a robust and reliable PDF viewer that's easy to customize.

Conclusion

Copied to clipboard

Building a PDF Viewer with Angular and PDF.js is straightforward, but if you need more advanced features, significantly improved reliability, or high-fidelity rendering, open source is not always the best approach.

Apryse WebViewer is a JavaScript-based PDF library with hundreds of features, support for 30+ file formats, with a proven rendering engine built right in, and is simple to integrate with Angular projects. Try our JavaScript PDF library today for free!

Check out our online demo or try it free.

If you have any questions about Apryse PDF SDK, contact us!

You can find the source code for this blog post at Github.

Sanity Image

Apryse

Share this post

email
linkedIn
twitter