DZone
Integration Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Integration Zone > Improving the Reader Experience With Adobe Embed API

Improving the Reader Experience With Adobe Embed API

Learn how to make life easier for readers.

Raymond Camden user avatar by
Raymond Camden
·
Dec. 04, 21 · Integration Zone · Tutorial
Like (1)
Save
Tweet
14.96K Views

Join the DZone community and get the full member experience.

Join For Free

The Adobe PDF Embed API provides a simple way for web developers to display PDFs on their web pages. While browsers provide good support for rendering PDFs, they do so in an "out of context" manner that takes over the entire screen. The PDF Embed API however lets you place a PDF within your site's layout, providing much better control over the position and size of the rendered document. This improved experience also provides deeper integration into the PDF viewer, letting developers listen for events and perform operations on the document. In this article, I'm going to demonstrate a simple, but hopefully really useful example of this.

Imagine a large document covering many pages, for example, a textbook. Your website users, potentially students, could work with the document over many weeks while school is in session. If the document is a few hundred pages long, it would be incredibly useful to remember where they were in the document when they start reading again. While a large PDF may have bookmarks, even then they could only be for chapters or other sections, not the exact page the user last read. Luckily, the Embed API provides a method to help with this. Let's take a look!

Handling Events

The first thing we need to do is recognize what page a user is on. We can do this by using the Embed API's event handling system. This system lets you listen to a variety of events:

  • A keyboard or mouse event.
  • The document being scrolled.
  • Something being selected.
  • The document being zoomed.
  • The current page changing.
  • There's even an event thrown for things Embed doesn't support yet, like XFA form fields. This lets you handle the issue more gracefully.

To enable events, you need to figure out which event, or events, you care about, and then register a callback to fire when the event happens. Here's a basic example modified from our docs:

JavaScript
 
adobeDCView.registerCallback(
	AdobeDC.View.Enum.CallbackType.EVENT_LISTENER,
	function(event) {
		console.log(event)
	}, {
		listenOn: [ AdobeDC.View.Enum.Events.APP_RENDERING_START, AdobeDC.View.Enum.FilePreviewEvents.PREVIEW_KEY_DOWN ],
        enableFilePreviewEvents: true
	}
);

The example above contains the event handler (which just logs the event) as well as the list of events to monitor. Notice the last bit of code, enableFilePreviewEvents. This is required for the event handling system to work and it changes based on the types of events your working with. So for example, analytics events require enablePDFAnaltics and annotations will need enableAnnotationEvents.

So now that we know how to listen for an event, which do we need? There's a few possible events we could consider. We have an event for scrolling, PREVIEW_PAGE_VIEW_SCROLLED. In theory we could use this to note that precisely where a user is on a page, but that's probably overkill. (And speaking for myself, when I pick up a page I'm reading I'll always start at the top of the page and then scan down to where I left off. This helps me remember the details of where I finished.

We've then got two events related to page changes: CURRENT_ACTIVE_PAGE and PAGES_IN_VIEW_CHANGED. The later returns information related to navigation and will tell you the previous and current page. But the first one, CURRENT_ACTIVE_PAGE, simply just returns the current page. As the simpler option, we'll make use of it instead.

It's event object is just:

JSON
 
{ 
	"pageNumber": "Some number"
}

So we now have a way of recognizing when the user changes the current page, let's see how this would look in a full example:

JavaScript
 
async function displayPDF() {
	let adobeDCView = new AdobeDC.View({clientId: ADOBE_KEY, divId: "pdfBox"});
	let viewer = await adobeDCView.previewFile({
		content:{location: {url: "https://documentcloud.adobe.com/view-sdk-demo/PDFs/Bodea%20Brochure.pdf"}},
		metaData:{fileName: "Bodea Brochure.pdf"}
	}, {embedMode: "SIZED_CONTAINER"});	
		
	const eventOptions = {
		listenOn: [ AdobeDC.View.Enum.FilePreviewEvents.CURRENT_ACTIVE_PAGE ],
		enableFilePreviewEvents: true
	}

	adobeDCView.registerCallback(
		AdobeDC.View.Enum.CallbackType.EVENT_LISTENER,
		async function(event) {
			let page = event.data.pageNumber;
			console.log('page event, viewing page ',page);
		}, eventOptions
	);
}

if(window.AdobeDC) displayPDF();
else {
	document.addEventListener("adobe_dc_view_sdk.ready", () => displayPDF());
}

Let's focus on displayPDF as the code at the end is simply how we listen for the Embed library to be ready. I create an AdobeDCView with my key and pointing to a div in my HTML (not shared above but also not relevant). I then create an object, viewer, that handles the result of the adobeDCView.previewFile call. Many code samples don't do this because it's not necessary if you're just rendering a PDF and not needing to programatically interact with it. Later on we'll need this so we've set the code up to be ready for that.

Next, we define an eventOptions object to store, you guessed it, our event options. Specifically the event we care about and the toggle to enable the feature in general.

Lastly, we register our callback. I grab the page out of the event data and then render it to the console. If I run this and navigate through the PDF, I'll see the following:

At this point, we now know when a user has changed to a new page. In order to support restoring the user to the page later on, we need a way to remember the page number. The browser supports multiple ways of doing this, but the simplest is localStorage and the Web Storage API. If you've never used this before, localStorage provides a key/value system that stores data for a web site. The data is only available for that web site so you don't have to worry about other web sites messing with it.

Writing data to the localStorage is as simple as: localStorage['setting'] = 'value'. You can also use an API method: localStorage.setItem('setting', 'value'). In general that's all there is to it, but note that you can only store simple values in localStorage. To store complex values, like arrays or objects, you need to JSON serialize them first.

Ok, so give we know how to store, let's update our callback:

JavaScript
 
adobeDCView.registerCallback(
	AdobeDC.View.Enum.CallbackType.EVENT_LISTENER,
	async function(event) {
		let page = event.data.pageNumber;
		storePage(page);
		console.log('page event, viewing page ',page);
	}, eventOptions
);

Our callback now calls storePage to persist the page viewed. That function is:

JavaScript
 
function storePage(x) {
	localStorage['page'] = x;
}

Alright, it's a bit overkill to write a function for one simple line of code, but if we ever change how the value is persisted, or make the value more complex, we'll be prepared!

Manipulating the PDF

Let's do a quick check. We've now got a PDF Embed that notices, via events, what page your on and then stores that value in the browser's persistent memory. The next part of the puzzle is to check for that value when our code starts up and update the page to the previous value.

The Embed API has a rich Viewer API that lets you do multiple different things with the document in the browser. You can change the zoom, search for text, get PDF metadata and more. Thankfully you can also set the current page.

The Viewer API supports a method, gotoLocation, that lets you set the PDF to a particular page and X and Y coordinate. All we need is the page though so our use will be simple.

Here's a simple example that immediately sets a PDF to page 7:

JavaScript
 
let adobeDCView = new AdobeDC.View({clientId: ADOBE_KEY, divId: "pdfBox"});
let viewer = await adobeDCView.previewFile({
	content:{location: {url: "https://documentcloud.adobe.com/view-sdk-demo/PDFs/Bodea%20Brochure.pdf"}},
	metaData:{fileName: "Bodea Brochure.pdf"}
}, {embedMode: "SIZED_CONTAINER"});	

let apis = await viewer.getAPIs();
apis.gotoLocation(7);

Remember earlier when I said we needed to store the result of previewFile? We do that so we can do two things. First, we ask for our APIs with getAPIs. This gives us an object we can use to call arbitrary methods. In our case, it's just gotoLocation with a hard coded page.

To get the real page, we need to read from localStorage. To do this, we have a new function:

JavaScript
 
function getPage() {
	let page = localStorage['page'];
	if(page) return parseInt(page, 10);
}

There's a few things to note here. If the user has never been to the site before, they won't have a page stored in localStorage. That's why the second line checks to see if the value was returned.

Secondly, all values in localStorage are strings. To get the page into a number, we use parseInt.

The final result is either, well, no result (the user never viewed a page) or a number. Now that we have all the parts together, let's look at the final version:

JavaScript
 
async function displayPDF() {
	let adobeDCView = new AdobeDC.View({clientId: ADOBE_KEY, divId: "pdfBox"});
	let viewer = await adobeDCView.previewFile({
		content:{location: {url: "https://documentcloud.adobe.com/view-sdk-demo/PDFs/Bodea%20Brochure.pdf"}},
		metaData:{fileName: "Bodea Brochure.pdf"}
	}, {embedMode: "SIZED_CONTAINER"});	

	let apis = await viewer.getAPIs();
		
	const eventOptions = {
		listenOn: [ AdobeDC.View.Enum.FilePreviewEvents.CURRENT_ACTIVE_PAGE ],
		enableFilePreviewEvents: true
	}

	adobeDCView.registerCallback(
		AdobeDC.View.Enum.CallbackType.EVENT_LISTENER,
		async function(event) {
			let page = event.data.pageNumber;
			storePage(page);
			console.log('page event, viewing page ',page);
		}, eventOptions
	);
	
	let page = getPage();
	if(page) {
		console.log('need to set page to', page);
		apis.gotoLocation(page);
	}

}

function getPage() {
	let page = localStorage['page'];
	if(page) return parseInt(page, 10);
}

function storePage(x) {
	localStorage['page'] = x;
}

if(window.AdobeDC) displayPDF();
else {
	document.addEventListener("adobe_dc_view_sdk.ready", () => displayPDF());
}

This version has the "store the page when it changes logic" as well as the "if I was on a page in the past, set me there." You can view the entire example on CodePen: https://codepen.io/cfjedimaster/pen/NWveoMv

To test, switch to another page, and then reload the CodePen. When the CodePen loads, you should see it automatically start on the last page you viewed. Note that this works just fine in the "FULL_WINDOW" embed mode (the default) where users scroll from page to page. If you wish to see this for yourself, check out this CodePen. If you want to start playing with PDF Embed today, sign up for a free set of credentials and start embedding!

API Event PDF Document

Published at DZone with permission of Raymond Camden, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How to Submit a Post to DZone
  • DZone's Article Submission Guidelines
  • How to Properly Format SQL Code
  • Datafaker: An Alternative to Using Production Data

Comments

Integration Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo