Embracing Native Capabilities: Viewing PDFs in Angular Without External Packages

Saunak Surani
Widle Studio LLP
Published in
7 min readAug 8, 2023

Angular, a popular frontend framework, provides a rich set of features for building modern web applications. One common requirement in many projects is the ability to view PDF documents directly within the application. While there are numerous third-party packages available for this purpose, we will explore how to achieve this functionality without relying on external libraries.

In this article, we will walk through the process of integrating native capabilities to display PDFs in Angular applications. By leveraging built-in browser features and APIs, we can achieve a seamless PDF viewing experience for our users. Let's dive in!

Table of Contents

  1. Understanding PDF Rendering in Browsers
  2. Project Setup and Angular Configuration
  3. Fetching PDF Data and Creating Blob
  4. Rendering PDF Using the object element
  5. Enhancing the PDF Viewer
    5.1. Adding Navigation Controls
    5.2. Implementing Zoom Functionality
    5.3. Supporting Page Navigation
    5.4. Improving Responsiveness
  6. Handling Error Scenarios
  7. Performance Optimization
  8. Integrating PDF Text Selection
  9. Implementing Text Search in PDF
  10. Supporting Annotations and Interactivity
  11. PDF Security Considerations
  12. Compatibility and Cross-Browser Support
  13. PDF Viewing Best Practices
  14. Future Scope and Improvements
  15. Conclusion

1. Understanding PDF Rendering in Browsers

Before we proceed with implementation, it's essential to understand how browsers handle PDF rendering. Modern browsers have native support for rendering PDF files without any external plugins or libraries. They achieve this through the built-in object element, which allows embedding documents directly within the page.

2. Project Setup and Angular Configuration

Let's begin by setting up our Angular project and configuring it to enable PDF rendering. If you don't have Angular CLI installed, you can do so by running the following command:

npm install -g @angular/cli

Next, create a new Angular project:

ng new pdf-viewer-app
cd pdf-viewer-app

3. Fetching PDF Data and Creating Blob

To view PDFs within our Angular application, we need to fetch the PDF data from a server or an external source. For this article, we will use a sample PDF file hosted on the server.

First, create a service to fetch the PDF data:

ng generate service pdf

Next, in the pdf.service.ts file, add the following code:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
providedIn: 'root'
})
export class PdfService {
private pdfUrl = 'https://example.com/sample.pdf'; // Replace with your PDF URL

constructor(private http: HttpClient) {}

getPDF(): Promise<Blob> {
return this.http.get(this.pdfUrl, { responseType: 'blob' })
.toPromise();
}
}

4. Rendering PDF Using the object element

With the PDF data fetched, we can now proceed to render it using the object element. In the component where you want to display the PDF (e.g., pdf-viewer.component.ts), add the following code:

import { Component, OnInit } from '@angular/core';
import { PdfService } from '../pdf.service';

@Component({
selector: 'app-pdf-viewer',
templateUrl: './pdf-viewer.component.html',
styleUrls: ['./pdf-viewer.component.css']
})
export class PdfViewerComponent implements OnInit {
pdfData: Blob;

constructor(private pdfService: PdfService) {}

async ngOnInit() {
this.pdfData = await this.pdfService.getPDF();
}
}

5. Enhancing the PDF Viewer

5.1. Adding Navigation Controls

To provide users with a better reading experience, we can add navigation controls for easy traversal of PDF pages. In the pdf-viewer.component.html file, add the following code:

<div class="pdf-viewer">
<object [data]="pdfData" type="application/pdf"></object>
<div class="controls">
<button (click)="goToPreviousPage()">Previous</button>
<span>{{ currentPage }} / {{ totalPages }}</span>
<button (click)="goToNextPage()">Next</button>
</div>
</div>

In the pdf-viewer.component.ts file, add the following code:

// ...

export class PdfViewerComponent implements OnInit {
pdfData: Blob;
currentPage = 1;
totalPages = 1;

constructor(private pdfService: PdfService) {}

async ngOnInit() {
this.pdfData = await this.pdfService.getPDF();
this.totalPages = this.getPdfTotalPages();
}

getPdfTotalPages(): number {
// Implement logic to get the total number of pages from the PDF data
return 10; // Replace with the actual total pages count
}

goToPreviousPage(): void {
if (this.currentPage > 1) {
this.currentPage--;
}
}

goToNextPage(): void {
if (this.currentPage < this.totalPages) {
this.currentPage++;
}
}
}

5.2. Implementing Zoom Functionality

Allowing users to zoom in and out of the PDF can greatly enhance the reading experience. We can add zoom controls to the PDF viewer component. In the pdf-viewer.component.html file, add the following code:

<!-- ... (previous code) ... -->

<div class="controls">
<button (click)="zoomOut()">Zoom Out</button>
<span>{{ zoomLevel }}%</span>
<button (click)="zoomIn()">Zoom In</button>
</div>

In the pdf-viewer.component.ts file, add the following code:

// ...

export class PdfViewerComponent implements OnInit {
// ... (previous code) ...

zoomLevel = 100;

zoomIn(): void {
this.zoomLevel += 10;
}

zoomOut(): void {
if (this.zoomLevel > 10) {
this.zoomLevel -= 10;
}
}
}

5.3. Supporting Page Navigation

Users may want to jump to a specific page in the PDF document. Let's add an input field where they can enter the page number and navigate to it. In the pdf-viewer.component.html file, add the following code:

<!-- ... (previous code) ... -->

<div class="controls">
<!-- ... (previous controls) ... -->
<div class="page-navigation">
<label for="pageInput">Go to Page:</label>
<input id="pageInput" type="number" min="1" max="{{ totalPages }}" [(ngModel)]="currentPage">
<button (click)="goToPage()">Go</button>
</

div>
</div>

In the pdf-viewer.component.ts file, add the following code:

// ...

export class PdfViewerComponent implements OnInit {
// ... (previous code) ...

goToPage(): void {
if (this.currentPage < 1) {
this.currentPage = 1;
} else if (this.currentPage > this.totalPages) {
this.currentPage = this.totalPages;
}
}
}

5.4. Improving Responsiveness

To ensure that the PDF viewer adapts well to different screen sizes, we can add some responsive styles. In the pdf-viewer.component.css file, add the following code:

.pdf-viewer {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}

object {
width: 100%;
height: 100%;
}

.controls {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}

.page-navigation {
display: flex;
align-items: center;
gap: 5px;
}

@media screen and (max-width: 768px) {
.controls {
flex-direction: column;
}
}

6. Handling Error Scenarios

While integrating native PDF rendering, we need to consider scenarios where the PDF file might not load correctly or the user might encounter errors. To handle such cases, we can implement error handling and display appropriate messages. Let's update the pdf-viewer.component.ts file as follows:

// ...

export class PdfViewerComponent implements OnInit {
// ... (previous code) ...

loadingError = false;

async ngOnInit() {
try {
this.pdfData = await this.pdfService.getPDF();
this.totalPages = this.getPdfTotalPages();
} catch (error) {
this.loadingError = true;
console.error('Error loading PDF:', error);
}
}
}

In the pdf-viewer.component.html file, we can now add the error message:

<!-- ... (previous code) ... -->

<div class="pdf-viewer">
<object [data]="pdfData" type="application/pdf"></object>
<div *ngIf="loadingError" class="error-message">Error loading PDF. Please try again later.</div>
<!-- ... (previous controls) ... -->
</div>

7. Performance Optimization

Rendering large PDFs can impact performance, especially on low-end devices. To optimize performance, we can adopt various techniques:

  • Lazy Loading: Load the PDF only when it comes into the viewport, using Angular's built-in lazy loading or Intersection Observer API.
  • Virtualization: Render only the visible pages, and load others as the user scrolls.
  • Compression: Compress the PDF files to reduce their size for faster loading.

8. Integrating PDF Text Selection

In a typical PDF viewer, users expect the ability to select and copy text from the document. By enabling text selection, we can enhance the user experience. However, this functionality is not directly available when using the object element for PDF rendering. To achieve text selection, we can use a combination of CSS and JavaScript.

/* pdf-viewer.component.css */
/* ... (previous styles) ... */

/* Enable text selection */
object ::selection {
background-color: rgba(0, 123, 255, 0.3);
}

9. Implementing Text Search in PDF

Allowing users to search for specific text within the PDF is a valuable addition. To implement text search, we need to extract the text content from the PDF data and enable search functionality.

10. Supporting Annotations and Interactivity

PDFs often contain interactive elements such as form fields, links, and annotations. By leveraging PDF.js or other libraries, we can implement support for these interactive features.

11. PDF Security Considerations

Depending on the nature of your application, you may need to address security concerns related to PDFs. For sensitive documents, you might want to implement additional encryption, access controls, or watermarking.

12. Compatibility and Cross-Browser Support

Before deploying your PDF viewer, thoroughly test it across different browsers and platforms to ensure cross-browser compatibility and a consistent user experience.

13. PDF Viewing Best Practices

  • Optimize PDF Files: Compress PDFs to reduce file size without compromising quality.
  • Responsive Design: Ensure the PDF viewer is responsive and works well on both desktop and mobile devices.
  • Error Handling: Provide clear error messages and fallback options in case PDF rendering fails.

14. Future Scope and Improvements

  • Annotation and Commenting: Implement support for user annotations and comments in the PDF viewer.
  • Continuous Scrolling: Enable continuous scrolling for a smoother reading experience.

15. Conclusion

In this article, we explored how to leverage native capabilities to view PDFs within Angular applications without relying on external packages. We covered techniques for rendering PDFs using the object element, implementing navigation and zoom functionality, and enhancing the viewer’s responsiveness. We also discussed handling errors, optimizing performance, and enabling text selection.

By harnessing the browser’s built-in capabilities, we can provide a seamless and efficient PDF viewing experience for our users. As you further develop your PDF viewer, consider additional features such as text search, interactive annotations, and security enhancements to create a robust and user-friendly solution.

--

--

Saunak Surani
Widle Studio LLP

Passionate about technology, design, startups, and personal development. Bringing ideas to life at https://widle.studio