Some test text!

Discord Logo

Chat with us

PDFTron is now Apryse, learn more here.

Web / Guides / Offline

Platform


PDFTron is now Apryse, learn more here.

Viewing documents offline with WebViewer

There are two parts to loading WebViewer in an offline scenario: loading the web resources (e.g. JS, HTML, CSS, Web Workers, etc) and loading the actual document in the viewer. Loading the resources can be accomplished using service worker, AppCache or using local resources embedded in a native app. Then using IndexedDB makes it straightforward to save and load the actual document.

In this guide, we will be using the service worker to cache and serve WebViewer files and localforage to simplify storing and retrieving documents in web storage. This guide assumes you have some basic knowledge about service workers, so you can read this guide for an overview.

To see a complete project with the code in this guide, visit this repo.

Register a service worker

There isn't anything special about registering a service worker for WebViewer. You can follow the exact steps here.

Cache files

After the service worker has been registered, it's time to cache WebViewer files in the service worker. Depending on the type of the documents you are going to load, not every file needs to be cached. In theory, you will want to cache every file inside the lib folder after unnecessary files have been removed using the optimizing script .

// In your service worker file
const CACHE_NAME = 'YOUR_CACHE_NAME';
// This file is cached only because we are using this library in this guide
const localforage = 'path/to/localforage.js';

// The following files are required to load WebViewer with the default UI
const externalFiles = [
  'path/to/lib/core/external/decode.min.js',
  'path/to/lib/core/external/rawinflate.js',
  'path/to/lib/core/external/pako_inflate.min.js',
  'path/to/lib/core/external/jquery-3.2.1.min.js',
  'path/to/lib/core/external/html2canvas.min.js',
  'path/to/lib/core/external/Promise.js'
];
const uiFiles = [
  'path/to/lib/ui/build/index.html',
  'path/to/lib/ui/build/style.css',
  'path/to/lib/ui/build/webviewer-ui.min.js',
  'path/to/lib/ui/build/i18n/translation-en.json'
];
const webViewerFiles = [
  'path/to/lib/core/webviewer-core.min.js',
  'path/to/lib/webviewer.min.js',
  'path/to/lib/core/CoreWorker.js'
];

// The following files are optional

// If you want to load a PDF file
const PDFWorkerFiles = [
  'path/to/lib/core/pdf/pdfnet.res',
  'path/to/lib/core/pdf/PDFworker.js',
  'path/to/lib/core/pdf/lean/PDFNetC.gz.js.mem',
  'path/to/lib/core/pdf/lean/PDFNetC.gz.mem',
  'path/to/lib/core/pdf/lean/PDFNetCWasm.br.js.mem',
  'path/to/lib/core/pdf/lean/PDFNetCWasm.br.wasm',
];
// If you want to load an Office file
const OfficeWorkerFiles = [
  'path/to/lib/core/office/OfficeWorker.js',
  'path/to/lib/core/office/WebOfficeWorker.gz.js.mem',
  'path/to/lib/core/office/WebOfficeWorker.gz.mem',
  'path/to/lib/core/office/WebOfficeWorkerWasm.br.js.mem',
  'path/to/lib/core/office/WebOfficeWorkerWasm.br.wasm',
];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        return cache.addAll([
          localforage,
          ...externalFiles,
          ...uiFiles,
          ...webViewerFiles,
          ...PDFWorkerFiles,
          ...OfficeWorkerFiles
        ]);
      })
  );
});

Return cached responses

WebViewer will append a query string when requesting the worker files depending on if you are using the full API . In order for the service worker to return the correct cached files we need to set the ignoreSearch.

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request, { ignoreSearch: true })
      .then(response => {
        if (response) {
          return response;
        }
        return fetch(event.request);
      })
  );
});

Store documents

Fetching the document and storing it as a blob are easy using the fetch API and localforage.

const store = localforage.createInstance({
  name: 'store'
});
const filePath = 'path/to/your/file';
const fileName = 'fileName';

fetch(filePath)
  .then(response => response.blob())
  .then(blob => {
    store.setItem(fileName, blob);
  })
  .catch(error => {
    console.log(error);
  });

Load documents

The loadDocument API supports loading a blob so all we need to do is to get the blob from the store and call the API with it.

store
  .getItem(fileName)
  .then((blob) => {
    viewerInstance.loadDocument(blob, {
      filename: fileName
    });
  });

To see a complete project with the code in this guide, visit this repo.

Get the answers you need: Support