> This is part 1 of 33 of the full documentation (pages 1–100 of 3232).
> The content is paginated: fetch every part to see all of it.
> Next part: https://docs.apryse.com/llms-full.txt/1
> Page index: https://docs.apryse.com/llms.txt

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
This site has an agentic ask interface you may use to query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.apryse.com/index.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

---

# Developer Platform

Welcome to your team’s developer platform

<h2 align="center">Build document features fast with Apryse</h2>

<p align="center">Try Apryse Developer Suite for free and see how quickly you can add viewing, editing, and processing to your app.</p>

<p align="center"><a href="https://docs.apryse.com/guides/get-started" class="button primary">Get a free trial license</a></p>

<p align="center"><sub>No credit card required. Set up takes just a few minutes.</sub></p>

### Apryse Developer Suite

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th></tr></thead><tbody><tr><td><strong>WebViewer - Web SDK</strong></td><td>Robust viewer and client-side processing for web applications.<br><br><a href="/web" class="button primary medium">Web Guides</a> <a href="https://showcase.apryse.com/?docLink=homeWV2Show&#x26;_gl=1*165gzch*_gcl_au*NzAwNTA3MTY1LjE3NjM0ODQwMDA." class="button primary medium">Launch Demo</a><br></td><td></td><td></td></tr><tr><td><strong>Server SDK</strong></td><td>Server / Desktop SDK for large jobs and secure environments.<br><br><a href="/core/get-started/get-started" class="button primary medium">Server Guides</a> <a href="https://apryse.gitbook.io/apryse-docs/core/get-started/samples" class="button primary medium">Samples</a></td><td></td><td></td></tr><tr><td><strong>Mobile SDK</strong></td><td>Add document viewing to your mobile native apps.<br><br><a href="https://docs.apryse.com/mobile/" class="button primary medium">Mobile SDK</a></td><td></td><td></td></tr><tr><td><strong>Scanbot SDK</strong></td><td>Add scanning to your mobile native apps.<br><br><a href="https://docs.scanbot.io/" class="button primary medium">Scanbot SDK Docs</a></td><td></td><td></td></tr><tr><td><strong>Fluent for MS Office</strong></td><td>Offers powerful automation, customizable templates, and Microsoft Office integration.<br><br><a href="https://fluent.apryse.com/documentation" class="button primary medium">Fluent Docs</a></td><td></td><td></td></tr><tr><td><strong>Template Extraction</strong></td><td>Identify components to classify and extract scanned documents.<br><br><a href="https://docs.apryse.com/template-extraction/" class="button primary">Extraction Guides</a></td><td></td><td></td></tr></tbody></table>

### Quick Start

<table data-column-title-hidden data-view="cards"><thead><tr><th>Framework</th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><picture><source srcset="/files/9j0Ff8LjfgdhQaVrq6e0" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FEA5FHuW31UrabWKIrtS4%2FFrame.svg?alt=media&amp;token=cb1b716b-5afa-4c98-b436-1f85f146f169" alt="" data-size="line"></picture> <strong>NET (C#, VB)</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/core/get-started/frameworks/dotnet">https://apryse.gitbook.io/apryse-docs/core/get-started/frameworks/dotnet</a></td></tr><tr><td><picture><source srcset="/files/dpjKF14nZQURUDBkrYvl" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2F89K7TBagyN7CZNMqTIaa%2FFrame-1.svg?alt=media&amp;token=4c82d221-bd7a-4e1c-a25d-1abe62880dfe" alt="" data-size="line"></picture> <strong>React</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/web/get-started/libraries-and-frameworks/react">https://apryse.gitbook.io/apryse-docs/web/get-started/libraries-and-frameworks/react</a></td></tr><tr><td><picture><source srcset="/files/dpjKF14nZQURUDBkrYvl" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2F89K7TBagyN7CZNMqTIaa%2FFrame-1.svg?alt=media&amp;token=4c82d221-bd7a-4e1c-a25d-1abe62880dfe" alt="" data-size="line"></picture> <strong>React Native</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/android/get-started/frameworks/react-native/react-native">https://apryse.gitbook.io/apryse-docs/android/get-started/frameworks/react-native/react-native</a></td></tr><tr><td><picture><source srcset="/files/ZCURvu1nmkQu65HRezQs" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FfQeOUOsWPGRa8BuN0TVY%2FFrame-2.svg?alt=media&amp;token=a94e9771-f055-4a48-ac60-695b1a5341fc" alt="" data-size="line"></picture> <strong>C++</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/core/get-started/languages/cpp">https://apryse.gitbook.io/apryse-docs/core/get-started/languages/cpp</a></td></tr><tr><td><picture><source srcset="/files/FE8GN6w1YSWtdzQHMcrv" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FjKKrW6aRqWePjUHXCVXz%2FFrame-5.svg?alt=media&amp;token=6f7a8d76-2f02-42a1-b071-54767e1088ca" alt="" data-size="line"></picture> <strong>Angular</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/web/get-started/libraries-and-frameworks/angular">https://apryse.gitbook.io/apryse-docs/web/get-started/libraries-and-frameworks/angular</a></td></tr><tr><td><picture><source srcset="/files/qpNIcFGFTlx9nRaUyVps" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FP1jum8BVaJWS6PEpnwyz%2FFrame-4.svg?alt=media&amp;token=d369e2e1-65a8-4680-a987-2973c2fa2189" alt="" data-size="line"></picture> <strong>Xamarin</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/xamarin/get-started/get-started">https://apryse.gitbook.io/apryse-docs/xamarin/get-started/get-started</a></td></tr><tr><td><picture><source srcset="/files/iOo64RG22OIJWR8ugBwh" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FIrmxyVmP50LD05oSIwR9%2FFrame-3.svg?alt=media&amp;token=028db89b-c6fa-4c5d-9eef-eb8991f8ba9b" alt="" data-size="line"></picture> <strong>Node.js</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/core/get-started/frameworks/nodejs">https://apryse.gitbook.io/apryse-docs/core/get-started/frameworks/nodejs</a></td></tr><tr><td><picture><source srcset="/files/D2vG5lBH93b38hkUYQTL" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FRV28l6NbdLWCAkXwe02A%2FFrame-6.svg?alt=media&amp;token=db62c975-a0b4-45f8-9bed-b1904b7d24aa" alt="" data-size="line"></picture> <strong>Next.js</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/web/get-started/libraries-and-frameworks/nextjs">https://apryse.gitbook.io/apryse-docs/web/get-started/libraries-and-frameworks/nextjs</a></td></tr><tr><td><picture><source srcset="/files/XyRLqM267RAEN4fn196k" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2Fpo5ZTsq55YiP2iVQkLRu%2FFrame-7.svg?alt=media&amp;token=f233fc52-bc75-4558-bb72-29211c50efe0" alt="" data-size="line"></picture> <strong>Flutter</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/android/get-started/frameworks/flutter/flutter">https://apryse.gitbook.io/apryse-docs/android/get-started/frameworks/flutter/flutter</a></td></tr><tr><td><picture><source srcset="/files/QeNiuV5bOsIfKEIrKiRY" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FX9tKjwZwK23wG2RofLCC%2FFrame-8.svg?alt=media&amp;token=a7f65574-42d7-46be-9083-bc41df6fae89" alt="" data-size="line"></picture> <strong>Java</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/core/get-started/languages/java">https://apryse.gitbook.io/apryse-docs/core/get-started/languages/java</a></td></tr><tr><td><picture><source srcset="/files/tOirLKsGXMTWW0IDRViD" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FkkHEWFBmf1H16OmP2hiF%2FFrame-9.svg?alt=media&amp;token=3b23ab9d-520d-4331-82bc-2e2972e99fcd" alt="" data-size="line"></picture> <strong>Vue</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/web/get-started/libraries-and-frameworks/vue">https://apryse.gitbook.io/apryse-docs/web/get-started/libraries-and-frameworks/vue</a></td></tr><tr><td><strong>See more options</strong></td><td><a href="https://docs.apryse.com/guides/get-started">https://docs.apryse.com/guides/get-started</a></td></tr></tbody></table>

### Highlighted Features

<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><strong>DOCX Editor</strong></td><td>Embed creating and editing DOCX documents without server-side or MS Office dependencies.</td><td><a href="/web/docx-editor/docx-editor" class="button primary medium">DOCX Guides</a><a href="/web/docx-editor/edit-docx-file" class="button primary medium">DOCX Demo</a></td></tr><tr><td><strong>Spreadsheet Editor</strong></td><td>Embed creating and editing Excel-like documents without server-side or MS Office dependencies.</td><td><a href="/web/spreadsheet-editor/spreadsheet-editor" class="button primary medium">Spreadsheet Editor Guides</a><br><a href="https://showcase.apryse.com/spreadsheet-editor" class="button primary medium">Spreadsheet Editor Demo</a></td></tr><tr><td><strong>Smart Data Extraction</strong></td><td>Use Smart Data Extraction (SDE) to transform unstructured doc data into structured, labeled JSON built for downstream AI, analytics or automation with Apryse Server SDK.</td><td><a href="/core/smart-data-extraction/smart-data-extraction" class="button primary medium">Smart Data Extraction Guides</a></td></tr></tbody></table>

### Integrations

Deploy WebViewer SDK on any of the Low-Code platforms.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><picture><source srcset="/files/1oFi7V08qCwqDaXvvFIx" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FECehQlQdkkRESfLqv13x%2FFrame.svg?alt=media&amp;token=e751ddb9-0e84-48df-8a42-c4f1f9383271" alt="" data-size="line"></picture> <strong>Salesforce</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/salesforce/get-started/readme">https://apryse.gitbook.io/apryse-docs/salesforce/get-started/readme</a></td></tr><tr><td><picture><source srcset="/files/5xFBOTy1hGhRQgrSheg9" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2Fx1MByFjwptkpL7HrNjeM%2FFrame-1.svg?alt=media&amp;token=82eb9ea1-f512-4abf-a8b9-6caf48c61329" alt="" data-size="line"></picture> <strong>SharePoint</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/web/get-started/integrations/sharepoint/sharepoint">https://apryse.gitbook.io/apryse-docs/web/get-started/integrations/sharepoint/sharepoint</a></td></tr><tr><td><picture><source srcset="/files/PByDbQHbFlAsRAeJunMp" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FqrhgwFTk6j2bY3rK9U2Y%2FFrame-2.svg?alt=media&amp;token=b72a8ad6-71c9-45ca-bd83-85ce7415970f" alt="" data-size="line"></picture> <strong>OutSystems</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/web/get-started/integrations/outsystems">https://apryse.gitbook.io/apryse-docs/web/get-started/integrations/outsystems</a></td></tr><tr><td><picture><source srcset="/files/VWbl6a4yn82eE7JOWFgT" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FefkiuFYk9Sx3XcpDzaSO%2FFrame-3.svg?alt=media&amp;token=cf6ad283-1308-4d56-8949-f769ef4888f0" alt="" data-size="line"></picture> <strong>Appian</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/appian/get-started/guides">https://apryse.gitbook.io/apryse-docs/appian/get-started/guides</a></td></tr><tr><td><picture><source srcset="/files/YsjLFzfGBdYX1drp6dnG" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FxeS6HmrY4womHd6e8NoW%2FFrame-4.svg?alt=media&amp;token=8f5a9b97-f757-4c3e-83ac-ba8bba524b18" alt="" data-size="line"></picture> <strong>Microsoft Teams</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/web/get-started/integrations/microsoft-teams">https://apryse.gitbook.io/apryse-docs/web/get-started/integrations/microsoft-teams</a></td></tr><tr><td><picture><source srcset="/files/pSi55rY2LmAy45SbzVbA" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2FGGXUsJKbWwlXkLpeBcnC%2FFrame-5.svg?alt=media&amp;token=1ed50fe9-12e1-46ac-90b6-327aac69942c" alt=""></picture> <strong>Mendix</strong></td><td><a href="https://apryse.gitbook.io/apryse-docs/web/get-started/integrations/mendix/mendix">https://apryse.gitbook.io/apryse-docs/web/get-started/integrations/mendix/mendix</a></td></tr><tr><td><picture><source srcset="/files/WmbFK2VoUNvDUnTVYyj1" media="(prefers-color-scheme: dark)"><img src="https://2726692679-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5NM9sFWuAFRh5wnwNuXD%2Fuploads%2Fptzw18ZFGcYqsZqTwSEB%2FFrame-6.svg?alt=media&amp;token=d333ed3a-3e32-4b46-9c6d-faf8acb2b678" alt=""></picture> <strong>MS Office (Fluent)</strong></td><td><a href="https://fluent.apryse.com/documentation">https://fluent.apryse.com/documentation</a></td></tr></tbody></table>

### Get Help

<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><strong>Join our Discord</strong></td><td>By using our Discord platform, you can receive quick responses for your trial setup.</td><td><a href="https://apryse.com/discord" class="button primary medium">Chat with us</a></td></tr><tr><td><strong>Support</strong></td><td>Get tech support to resolve issues.</td><td><a href="https://support.apryse.com/?docLink=homeSup" class="button primary medium">Raise a ticket</a></td></tr><tr><td><strong>Downloads</strong></td><td>Download latest versions of the SDK.</td><td><a href="https://docs.apryse.com/downloads/" class="button primary">Download a version</a></td></tr></tbody></table>


# Get Started with WebViewer - Integrations, Frameworks, Samples

Discover the power of WebViewer, a top-tier JavaScript document library for lightning-fast and reliable document processing. Seamlessly integrate with any frameworks like React, Angular, Vue, and Next

[WebViewer](/web/get-started/guides) is a cutting-edge JavaScript document library that empowers web applications with accurate, reliable, and lightning-fast [document processing capabilities](https://apryse.com/capabilities). With WebViewer, integration is seamless with any JavaScript framework, as it only requires a DOM element to host the document viewing component. Pre-built samples are readily available for quick and hassle-free integration, and setting up WebViewer with any framework is a breeze.

<a href="/web/get-started/npm" class="button primary">Integrate via NPM</a><a href="/web/get-started/manually" class="button primary">Manual integration</a><a href="/web/get-started/without-viewer" class="button primary">Without viewer</a>

## Libraries and Frameworks

<a href="/web/get-started/libraries-and-frameworks/react" class="button secondary">React</a><a href="/web/get-started/libraries-and-frameworks/angular" class="button secondary">Angular</a><a href="/web/get-started/libraries-and-frameworks/vue" class="button secondary">Vue</a><a href="/web/get-started/libraries-and-frameworks/nextjs" class="button secondary">NextJS</a><a href="/web/get-started/libraries-and-frameworks/nuxt" class="button secondary">NuxtJS</a><a href="/web/get-started/libraries-and-frameworks/electron" class="button secondary">Electron</a><a href="/web/get-started/libraries-and-frameworks/svelte" class="button secondary">Svelte</a><a href="/web/get-started/libraries-and-frameworks/vite" class="button secondary">Vite</a><a href="/web/get-started/readme/cordova" class="button secondary">Cordova</a><a href="/web/get-started/libraries-and-frameworks/blazor" class="button secondary">Blazor</a><a href="/core/get-started/frameworks/nodejs" class="button secondary">Node.js</a>

## Integrations

<a href="https://docs.apryse.com/salesforce/" class="button secondary">Salesforce</a><a href="/appian/get-started/guides" class="button secondary">Appian</a><a href="/web/get-started/integrations/mendix/mendix" class="button secondary">Mendix</a><a href="/web/get-started/integrations/outsystems" class="button secondary">OutSystems</a><a href="/web/get-started/integrations/sharepoint/sharepoint" class="button secondary">Sharepoint</a>

## Clone a sample project

#### Clone framework specific sample apps

<a href="https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-react" class="button secondary">React</a><a href="https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-vue" class="button secondary">Vue</a><a href="https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-nuxtjs" class="button secondary">NuxtJS</a><a href="https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-angular" class="button secondary">Angular</a><a href="https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-electron" class="button secondary">Electron</a><a href="https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-svelte" class="button secondary">Svelte</a><a href="https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-blazor" class="button secondary">Blazor</a><a href="https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-nextjs-14" class="button secondary">NextJS</a>

#### Clone full sample apps

<a href="https://github.com/ApryseSDK/pdftron-sign-app" class="button secondary">Digital signing app</a><a href="https://github.com/ApryseSDK/pdftron-document-search" class="button secondary">Search across multiple documents</a><a href="https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-barcode" class="button secondary">Stamp barcodes</a><a href="https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-server-side-search" class="button secondary">Server-side search</a><a href="https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-realtime-collaboration-sqlite3" class="button secondary">Collaboration sample</a>

See more on our [GitHub](https://github.com/ApryseSDK/webviewer-samples).


# React and WebViewer

Learn how to integrate WebViewer into a React app.

Content.

{% tabs %}
{% tab title="JavaScript" %}
{% code overflow="wrap" lineNumbers="true" %}

```javascript
import { useEffect, useRef } from 'react';
import WebViewer from '@pdftron/webviewer';

function App() {
  // Create reference to DOM element where WebViewer will mount
  const viewerRef = useRef(null);

  useEffect(() => {
    // Initialize WebViewer once when component mounts
    WebViewer(
      {
        // Path to copied WebViewer static assets
        path: '/lib/webviewer',
        // Replace with your Apryse license key
        licenseKey: 'YOUR_LICENSE_KEY',
        // Initial document to load when WebViewer starts (optional)
        initialDoc: 'https://pdftron.s3.amazonaws.com/downloads/pl/demo-annotated.pdf',
      },
      // Attach WebViewer to container element
      viewerRef.current
    ).then((instance) => {
      // Access WebViewer instance here if needed
      // const { UI, Core } = instance;
    });
  // Empty dependency array ensures this runs only once  
  }, []);
  return (
    // Container element for WebViewer
    <div
	    ref={viewerRef}
	    style={{ height: '100vh', width: '100%', margin: '0 auto' }}
	  />
  );
}

export default App;
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
sfjskfjk
{% endhint %}

|   |   |   |
| - | - | - |
|   |   |   |
|   |   |   |
|   |   |   |

{% tabs %}
{% tab title="First Tab" %}

{% endtab %}

{% tab title="windows" %}

{% endtab %}

{% tab title="Untitled" %}

{% endtab %}
{% endtabs %}

{% code lineNumbers="true" %}

```mermaid
graph TD
  Mermaid --> Diagram
```

{% endcode %}


# How to integrate WebViewer into a JQuery PDF Viewer

Generate, view, edit PDF files with JQuery PDF viewer

This guide will show you how to integrate [WebViewer Document Viewer & Editor](https://apryse.com/products/webviewer) into a jQuery application.

You can also download a ready-to-go sample on [GitHub](https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-jquery).

## Prerequisites

Prior to starting, you should have already installed [Node](https://nodejs.org/) and [npm](https://www.npmjs.com/).

Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## Get started video

In this 4-minute video, learn the fundamentals of Apryse WebViewer, including key concepts for installation and initialization in any web application.

{% embed url="<https://www.youtube.com/embed/UIIWdERmBtU?si=ZIOH-IIi9PqhqEDv>" %}
Overview of Apryse WebViewer fundamentals, including installation and initialization.
{% endembed %}

## Get Started with the Sample

### 1. Clone the Sample

Clone the [jQuery sample](https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-jquery) by executing

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
git clone --depth=1 https://github.com/ApryseSDK/webviewer-samples.git
cd webviewer-samples
```

{% endcode %}
{% endtab %}
{% endtabs %}

Once cloned, navigate into the `webviewer-jquery` directory and install all the required dependencies by executing

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
npm install
```

{% endcode %}
{% endtab %}
{% endtabs %}

npm will also download the Apryse WebViewer and extract it to `/src/lib`.

### 2. Run the sample

To run the sample, navigate to your `webviewer-jquery` directory and execute

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
npm start
```

{% endcode %}
{% endtab %}
{% endtabs %}

Then navigate to `http://localhost:3000`. You should see the application load on your browser.

### 3. Usage

To call more WebViewer API's, navigate to `/src/app/app.js` and add the API calls in the callback of the WebViewer constructor (you may need to make one if it is not provided). Add your own license key in place of 'YOUR\_LICENSE\_KEY'.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">WebViewer({
    path: '../lib',
    licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>', // sign up to get a key at https://dev.apryse.com
    initialDoc: '../files/webviewer-demo-annotated.pdf'
}, document.getElementById('viewer'))
    .then(instance => {
        // In this callback WebViewer will be initialized, so you can call WebViewer API here.
        instance.UI.setTheme('light');
    });
</code></pre>

{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer({
    path: '../lib',
    initialDoc: '../files/webviewer-demo-annotated.pdf'
}, document.getElementById('viewer'))
    .then(instance => {
        // In this callback WebViewer will be initialized, so you can call WebViewer API here.
        instance.setTheme('light');
    });
```

{% endcode %}
{% endtab %}
{% endtabs %}

If your jQuery application is already running, you should see the theme change by refreshing the page. Otherwise execute `npm start` again, then navigate to `http://localhost:3000` and you should see that the theme has changed.

## Next step

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# Integrate WebViewer JavaScript PDF Viewer & Editor into a Cordova App

Learn how to integrate WebViewer Document Viewer & Editor into a Cordova app with this step-by-step guide. Download a sample on GitHub & get started easily. The Apryse Web SDK streamlines secure, serv

This guide will show you how to integrate [WebViewer Document Viewer & Editor](https://apryse.com/products/webviewer) into a Cordova application.

You can also download a ready-to-go sample on [GitHub](https://github.com/ApryseSDK/webviewer-cordova-sample/).

## Prerequisites

Prior to starting, you should have already installed [Node](https://nodejs.org/) and [npm](https://www.npmjs.com/).

* [Cordova CLI](https://cordova.apache.org/docs/en/latest/guide/cli/index.html)
* Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## Get Started with the Sample

### 1. Clone the Sample

Clone the `webviewer-cordova-sample` repository:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
git clone https://github.com/ApryseSDK/webviewer-cordova-sample.git
```

{% endcode %}
{% endtab %}
{% endtabs %}

Enter the directory and run npm install:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
cd webviewer-cordova-sample
npm install
```

{% endcode %}
{% endtab %}
{% endtabs %}

This will automatically download and extract the Apryse WebViewer Package.

### 2. Run the sample

Run the application by executing:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
npm start
```

{% endcode %}
{% endtab %}
{% endtabs %}

If your build fails, use the following command to see a list of requirements for your added platforms:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
cordova requirements
```

{% endcode %}
{% endtab %}
{% endtabs %}

### 3. Usage

To call more WebViewer APIs, open `/www/js/index.js` in your favorite text editor and add the API calls to the callback for the WebViewer instantiation:

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">WebViewer({
  path: "js/lib",
  licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>', // sign up to get a key at https://dev.apryse.com
  webviewerServerURL: 'https://demo.apryse.com/', // Make sure to change this option to point to your own server in production
  initialDoc: 'https://pdftron.s3.amazonaws.com/downloads/pl/sample.pdf',
  enableAnnotations: true,
  disabledElements: [
    'menuButton'
  ]
}, document.getElementById('viewer'))
  .then(function(instance) {
    const { documentViewer, annotationManager, Annotations, Tools } = instance.Core;

    // See https://docs.apryse.com/web/guides/ for more info.
    documentViewer.addEventListener('documentLoaded', function() {
      // call methods relating to the loaded document
    });
  });
</code></pre>

{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">WebViewer({
  path: "js/lib",
  licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>', // sign up to get a key at https://dev.apryse.com
  pdftronServer: 'https://demo.apryse.com/', // Make sure to change this option to point to your own server in production
  initialDoc: 'https://pdftron.s3.amazonaws.com/downloads/pl/sample.pdf',
  enableAnnotations: true,
  disabledElements: [
    'menuButton'
  ]
}, document.getElementById('viewer'))
  .then(function(instance) {
    const docViewer = instance.docViewer;
    const annotManager = instance.annotManager;
    // call methods from instance, docViewer and annotManager as needed.
    // you can also access major namespaces from the instance as follows:
    // const Tools = instance.Tools;
    // const Annotations = instance.Annotations;

    // See https://docs.apryse.com/web/guides/ for more info.
    docViewer.on('documentLoaded', function() {
      // call methods relating to the loaded document
    });
  });
</code></pre>

{% endtab %}
{% endtabs %}

For example, if you want to change the theme of the WebViewer to dark mode, you would add the following:

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
instance.UI.setTheme('dark');
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
instance.setTheme('dark');
```

{% endcode %}
{% endtab %}
{% endtabs %}

Execute `npm start` again and the theme of the viewer will change.

## Next step

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# How to integrate WebViewer into a Outsystems PDF Viewer

Integrate Outsystems pdf viewer for viewing, annotating, editing, and creating PDFs in Mendix. Process documents natively in Mendix without third-party applications.

This guide will help you **integrate a free trial of WebViewer into an OutSystems Reactive Web App**.

The easiest way to get started would be to download the WebViewer Forge component available here:

{% @apryse-download-button/apryse-download-button id="WebViewer\_Forge\_Component\_web\_web" title="WebViewer Forge Component" showVersion="false" %}

Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

From here, you will have a working skeleton of how to integrate Apryse into an OutSystems Reactive Web App.

More detailed instructions are coming soon, but if you have any questions about this integration, please feel free to contact us at the [Trial Support Form](https://apryse.com/form/trial-support) and we'd be happy to provide further guidance.

Should you need OutSystems support, the OutSystems team can be reached at [`developers@outsystems.com`](mailto:developers@outsystems.com).


# Get started with WebViewer Video - Video Collaboration SDK

Collaborate and review videos frame by frame with WebViewer video. Annotate, comment, and collaborate on specific frames. Learn how to get started with a free trial and access the source code. The Apr

{% hint style="info" %}
New licenses for WebViewer Video are no longer offered.
{% endhint %}

Collaborate and review videos frame by frame. WebViewer video allows users to annotate video frames, comment on frames, collaborate and review videos.

Annotations will be tied to specific frames and will only be rendered, when the video is seeked to the specific frames. This guide will show you how to get started with a free trial.

The source code for this project is available in our [Git repo](https://github.com/ApryseSDK/webviewer-video-sample/).

{% embed url="<https://www.youtube.com/embed/xzvd9oN5Ngw>" %}

## Prerequisites

Prior to starting, you should have already installed [Node](https://nodejs.org/) and [npm](https://www.npmjs.com/).

Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## Initial setup

Clone the repo to a specific directory and then navigate to it:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
git clone --depth=1 https://github.com/ApryseSDK/webviewer-samples.git
cd webviewer-samples/webviewer-video
```

{% endcode %}
{% endtab %}
{% endtabs %}

## How to run

1. Navigate to the `webviewer-video` directory and install the required dependencies to run the samples by executing:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
npm install
```

{% endcode %}
{% endtab %}
{% endtabs %}

1. Next run the sample by executing:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
npm start
```

{% endcode %}
{% endtab %}
{% endtabs %}

1. Navigate to `http://localhost:3000/` and you will see the sample WebViewer running with a video, similar to above.

## How it works

HTML5 Videos can be loaded into WebViewer with use of the [@pdftron/webviewer-video](https://www.npmjs.com/package/@pdftron/webviewer-video/) package.

### Install

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
npm install @pdftron/webviewer-video
```

{% endcode %}
{% endtab %}
{% endtabs %}

Below is a sample setup of the video package with webviewer.

[`initializeVideoViewer`](https://sdk.apryse.com/api/video/module-@pdftron_webviewer-video.html#.initializeVideoViewer__anchor) is called to give WebViewer the ability to handle the loading of video files. Afterwards, a video can be loaded with `loadVideo`. Lastly settings to customize the behavior of the controls can be passed in when calling [`initializeVideoViewer`](https://sdk.apryse.com/api/video/module-@pdftron_webviewer-video.html#.initializeVideoViewer__anchor).

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
import WebViewer from '@pdftron/webviewer';
import { initializeVideoViewer, renderControlsToDOM } from '@pdftron/webviewer-video';

WebViewer({
    path: '/webviewer/lib',
    selectAnnotationOnCreation: true,
  },
  viewer.current,
).then(async instance => {
  // Extends WebViewer to allow loading HTML5 videos (.mp4, ogg, webm).
  const {
      loadVideo,
  } = await initializeVideoViewer(
      instance,
      {
        license: '---- Insert commercial license key here after purchase ----',
      }
  );
  // Load a video at a specific url. Can be a local or public link
  // If local it needs to be relative to lib/ui/index.html.
  // Or at the root. (eg '/video.mp4')
  const videoUrl = 'https://pdftron.s3.amazonaws.com/downloads/pl/video/video.mp4';
  loadVideo(videoUrl);
});
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [initializeVideoViewer](https://sdk.apryse.com/api/video/module-@pdftron_webviewer-video.html#~initializeVideoViewer__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v7.0+)" %}
{% code lineNumbers="true" %}

```js
import WebViewer from '@pdftron/webviewer';
import { initializeVideoViewer } from '@pdftron/webviewer-video';

WebViewer({
    path: '/webviewer/lib',
    selectAnnotationOnCreation: true,
  },
  viewer.current,
).then(async instance => {
  // Extends WebViewer to allow loading HTML5 videos (.mp4, ogg, webm).
  const {
    loadVideo,
  } = await initializeVideoViewer(
    instance,
    {
      license: '---- Insert commercial license key here after purchase ----',
    }
  );
  // Load a video at a specific url. Can be a local or public link
  // If local it needs to be relative to lib/ui/index.html.
  // Or at the root. (eg '/video.mp4')
  const videoUrl = 'https://pdftron.s3.amazonaws.com/downloads/pl/video/video.mp4';
  loadVideo(videoUrl);
});
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [initializeVideoViewer](https://sdk.apryse.com/api/video/module-@pdftron_webviewer-video.html#.initializeVideoViewer__anchor)
{% endtab %}
{% endtabs %}

## Import and Exporting Annotations

[Importing and exporting annotations](/web/annotation/import-export) works the exact same way as it does on WebViewer, with the video annotations having additional the properties `start-time` and `end-time`.

## Server configuration

Make sure your server has [byte-range requests](/ios/viewer/view-online-pdfs#byterange-requests) enabled to allow video streaming.

## API

[@pdftron/webviewer-video](https://sdk.apryse.com/api/video/)


# How to integrate WebViewer into a Mendix PDF Viewer

Integrate mendix pdf viewer for viewing, annotating, editing, and creating PDFs in Mendix. Process documents natively in Mendix without third-party applications.

This guide will help you **integrate a free trial of WebViewer into Mendix application** on the browser.

{% embed url="<https://www.youtube.com/embed/a9HNVzbmDLM>" %}

<a href="https://github.com/ApryseSDK/webviewer-mendix-sample" class="button primary">Get WebViewer Mendix Widget</a>

## Prerequisites

Prior to starting, you should have already installed [Node](https://nodejs.org/) and [npm](https://www.npmjs.com/).

Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## 1. Download WebViewer Mendix Web Widget

Download [WebViewer Mendix Web Widget](https://github.com/ApryseSDK/webviewer-mendix-sample/) and extract it.

## 2. Create a new or use an existing Mendix App

Open [Mendix Studio Pro](https://docs.mendix.com/howto/general/install/) and create a new project by selecting `File > New Project` from the top menu bar, and choose the `Blank` app.

After creating a new app, or inside of the existing app navigate to the app's directory and create a new folder called `CustomWidgets/webViewer` and place the extracted contents from [WebViewer Mendix Web Widget](https://github.com/ApryseSDK/webviewer-mendix-sample/).

By default, Mendix projects are stored in:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
C:\Users\$your_username\Documents\Mendix\
```

{% endcode %}
{% endtab %}
{% endtabs %}

In terminal or command line navigate to `CustomWidgets/webViewer` and run:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
npm install
```

{% endcode %}
{% endtab %}
{% endtabs %}

After the command completes, run:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
npm run dev
```

{% endcode %}
{% endtab %}
{% endtabs %}

This will make a build of the Mendix Web Widget with the latest version of WebViewer, and it will be complete when you see something like this in your terminal:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
[08:57:07] Finished 'bound runWebpack' after 4.09 s
[08:57:07] Starting 'createMpkFile'...
[08:57:07] Finished 'createMpkFile' after 32 ms
[08:57:07] Starting 'copyToDeployment'...
Files generated in dist and ..\..\ folder
[08:57:07] Finished 'copyToDeployment' after 18 ms
```

{% endcode %}
{% endtab %}
{% endtabs %}

Next, we must copy the static assets required for WebViewer to run. The files are located in `CustomWidgets/webViewer/node_modules/@pdftron/webviewer/public` and must be moved into a location that will be served and publicly accessible. In Mendix, we can place it into `theme/resources`. Create a new folder called `lib` and place the contents from `node_modules/@pdftron/webviewer/public` there. `theme/resources` should have a directory structure like so:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
/path/to/your/mendix/app/theme/resources
└───lib
    ├───core
    ├───ui
    └───ui-legacy
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 3. Place WebViewer into a Page

1. Access the `Domain Model` of the module where the viewer will be integrated, and create a new `Entity`.
2. Right-click the newly created `Entity`, click `Add > Attribute`. Leave everything as is, and ensure its `Type` is set to `String`.
3. Next, open the page inside of your module.
4. Add a `Data view` widget by dragging it from the Toolbox. If you do not have a the `Toolbox` window open on the right, the bottom right corner of the window should have the `Toolbox` tab as an option. Double-click the widget, and give it a data source microflow by selecting `Data source > Type > Microflow`.
5. In the microflow field, click the `Select` button and press `New`.
6. Open newly created microflow, delete the `parameter` object (the object that has has `U` and `(Not set)` underneath) and drag `Create object` from the toolbox onto the arrow. Open the object by double-clicking on it and select the entity we created earlier.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-fbdefb69f84b9ae1bdf2573da3fd9395a176a49d%2Ff2458c6bf4b7749703c333e4187654f4981dcab8-689x415.png?alt=media)

1. Right-click the `Create Entity` activity, then click `Set $NewEntity as Return Value`.
2. Go back to the page you placed the `data view`, and drag `Text box` into `data view`. Open the textbox's properties, find the `Data source` panel, and change the `Attribute` (path) to the string attribute you created in Steps 1 and 2.
3. Press F4, or from the top menu bar select `Project > Synchronize Project Directory`.
4. Go back to the page you placed the `data view`, in the `Toolbar`, under `Add-on widgets` you should now see `Web Viewer`. Drag into the `data view`. Right-click on `Web Viewer` widget and select Attribute by selecting Attribute string you have created.
5. You can now run the app by clicking `Run Locally` at the top.

## 4. Connect Attribute to WebViewer

The WebViewer now loads inside of your page with the default file. Now let's connect the Attribute parameter to be accessible by the WebViewer. Attribute parameter can be used to pass a URL to open specific document.

1. Navigate to the WebViewer location inside of `App/CustomWidgets/webViewer` and open it in your favourite code editor.
2. Open WebViewer component available in `src/components/PDFViewer.tsx`. Inside of it, you can see WebViewer constructor where you can pass various customization options and call APIs on the instance object. The Attribute that you have created in previous steps is passed in `props.value`:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
useEffect(() => {
    if (instance && props.value !== "") {
        instance.loadDocument(props.value);
    }
}, [props.value]);
```

{% endcode %}
{% endtab %}
{% endtabs %}

In the code snippet, we are listening for any of the changes in `props` and then calling `loadDocument` API to load a new document. You can connect it with your existing flows or pass URLs from your file storage. Make sure you have the [CORS configured](/web/get-started/faq/cors-support) in case you run into any errors.

You can now customize the widget by checking out other guides we have available. Perform your customizations inside of `src/components/PDFViewer.tsx`. Do not forget to run `npm run dev` within the Widget's console or terminal and update the files in your App by pressing F4, or from the top menu bar selecting `Project > Synchronize Project Directory`.

You can now checkout other guides like [how to open your own documents](/web/open-save-document/open/url) or [how to disable certain features](/web/ui-customization/hiding-elements).

## Next step

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# Learn More About WebViewer, the JavaScript PDF Viewer Library

Discover the essentials of WebViewer guides. Learn to annotate, view, and edit PDFs across all major browsers. Explore demos, samples, and more to kickstart your journey. The Apryse Web SDK streamline

WebViewer is a JavaScript library for viewing, annotating, and editing PDFs, Office Documents, images, videos, and other [formats](/web/what-is-webviewer/file-format-support) inside all major browsers.

WebViewer's UI is open source and extremely customizable through a [robust API](/web/ui-customization/ui-customization), and can be customized even further by forking the repo.

<a href="https://github.com/ApryseSDK/webviewer-ui" class="button primary">View on GitHub</a>

<a href="https://showcase.apryse.com" class="button primary">Online demo</a><a href="/web" class="button primary">Get started</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://docs.apryse.com/downloads/WebViewer.zip" class="button primary">Download</a>

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-74418867d7b18499dc1dbe8bbb53a4ec84d5b3bb%2F16aa772a9d8d9e6441241c82bf8297a9987ceedd-1147x775.png?alt=media)

## WebViewer Basics

[Introduction to WebViewer](/web/what-is-webviewer/overview) An overview of how it works.

[Open a document](/web/open-save-document/open) Load a document, load multiple documents, load WebViewer without a document.

[Customize UI](/web/ui-customization/ui-customization) Hide buttons from the header, hide panels, remove print or download options.

[Save and load annotations](/web/annotation/import-export) Save annotations to server, database or merge them onto a PDF. Import/export of annotations in XFDF.

## Features

***

#### [Annotation and Collaboration](/web/annotation/annotation)

Choose from 35+ annotations or build custom markup tools. Then, leverage out-of-the-box modules to enable real-time collaboration.

#### [Digital Signatures](/web/digital-signature/signature)

Create complete, custom signing workflows, including signature field assignment, signature creation, collection, and validation.

#### [Redaction](/web/redaction/redaction)

Eliminate confidential information by searching and redacting sensitive keywords, strings, and patterns. Redact areas of images and text, or entire pages.

#### [DOCX Viewing & Editing](/web/ms-office/office)

Edit and create DOCX files client-side: Write and edit text, apply styling, adjust fonts and sizes, or add bulleted and numbered lists. Eliminate post-edit formatting inconsistencies with native DOCX editing, producing true WYSIWYG output quality across all Word processors.

#### [Page Manipulation](/web/page-manipulation/manipulation)

Add, remove, rearrange, or merge pages easily by clicking or drag-and-drop page thumbnails, or set up a programmatic workflow.

#### [Forms](/web/forms/forms)

Simplify form filling with features that automate data entry into your database. Fill, edit, and create new forms. Populate data from any source into existing templates.

#### [Document Generation](/web/generate/create)

Embed a scalable and secure document generator that merges Office templates with JSON data client-side, right in the browser.

#### [Document Comparison](/web/compare-files/compare)

Easily identify changes in drawings and documents with intuitive overlays, side-by-side comparisons, and semantic text differences in highlighted word pairs.

#### [Text Editing](/web/edit-page-content/text-edit)

Have users edit PDF text and images in your app with a leading-accuracy editor that embeds directly. Apply or change existing styling or font sizes.

#### [Measurements](/web/measurement/measurement)

Streamline estimates via a fast, precise measurement tool. Set drawing scale, and measure distance, area, perimeters, and more with snap-to geometry.

#### [PDF/A](/web/pdf-a/pdfa)

Convert PDF to PDF/A and validate PDF/A files to certain conformance levels.

#### [Text and image extraction](/web/extraction/extraction)

Extract text, images, and other content from documents for further processing.

{% hint style="info" %}
Not seeing what you're looking for? Contact our experts on [Discord](https://apryse.com/discord?docLink=docDis) for help building your POC.
{% endhint %}

## Learn more

[Best practices](/web/best-practices/best-practices) Learn best practices when it comes to WebViewer.

[WebViewer API](/web/what-is-webviewer/usage) Document viewing and basic PDF manipulation APIs.

[Default UI](/web/ui-customization/modular-ui/getting-started) A complete, responsive and fully customizable user interface

[Deployment options](/web/what-is-webviewer/deployment-options) Three main options for deploying WebViewer.

[WebViewer Server](/web/webviewer-server/wv-server-deployment) WebViewer Server increases performance on older browsers including IE9, older mobile devices.

[Server integration](/web/what-is-webviewer/deployment-options#webviewer-server-docker-container) Optional approach to enhancing performance and provide support for more file formats.

[Full API](/web/full-api/full-api-overview) More advanced document processing APIs.

[Deploy to production](/web/best-practices/optimizing-lib-folder) Only include what you need from the library when you are ready to deploy.


# Get file data without a viewer

Learn how to get file data without a viewer. Retrieve document data from a PDF using Document or PDFDoc objects using JavasScript. Opt for getFileData for easy control and safety over saveMemoryBuffer

A PDF can be loaded into a [Document](https://sdk.apryse.com/api/web/Core.Document.html) or [PDFDoc](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html) object using their respective functions to acquire the document data.

## Getting data from a Document object

To get document data by creating a new `Document` object, the following example shows how to use a `createDocument` to retrieve document data from an external URL.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>';
const documentURL = 'Enter Document URL Here';
const extension = 'pdf'; // pass extension to option if there is no file extension in documentURL

// create Core.Document instance
Core.createDocument(documentURL, { l: licenseKey, extension })
  .then(doc => {

    // optionally perform some document processing using read write operations 
    // found under 'Editing Page Content' or 'Page Manipulation'

    doc.getFileData().then(data => {
      const arr = new Uint8Array(data);
      const blob = new Blob([arr], { type: 'application/pdf' });
      // add code for handling Blob here
    });
  })
  .catch(err => { })
</code></pre>

[Core.createDocument](https://sdk.apryse.com/api/web/Core.html#.createDocument__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>';
const documentURL = 'Enter Document URL Here';
const extension = 'pdf'; // pass extension to option if there is no file extension in documentURL

// create CoreControls.Document instance
CoreControls.createDocument(documentURL, { l: licenseKey, extension })
  .then(doc => {

    // optionally perform some document processing using read write operations 
    // found under 'Editing Page Content' or 'Page Manipulation'

    doc.getFileData().then(data => {
      const arr = new Uint8Array(data);
      const blob = new Blob([arr], { type: 'application/pdf' });
      // add code for handling Blob here
    });
  })
  .catch(err => { })
</code></pre>

[CoreControls.createDocument](https://sdk.apryse.com/api/web/Core.html#.createDocument__anchor)
{% endtab %}
{% endtabs %}

{% hint style="info" %}
`Document` objects also have a `getPDFDoc` function for retrieving an associated `PDFDoc` object. However `getFileData` is preferred over `saveMemoryBuffer` in most cases because `getFileData` gives easy control over the output and is safer (it ensure everything is loaded and it take care of locks). The only reason to use `saveMemoryBuffer` over `getFileData`, is if there isn't an easy way of acquiring a `Document` object or if you need other `PDFDoc` features for controlling the download process.
{% endhint %}

## Getting data from a PDFDoc object

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

Another way of getting document data is by using the [`saveMemoryBuffer`](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#saveMemoryBuffer__anchor) function found on `PDFDoc` objects. An example can be found below

<pre class="language-js" data-line-numbers><code class="lang-js">const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>';

async function main() {
  try {
    const documentURL = 'Enter Document URL Here';
    const pdfDoc = await PDFNet.PDFDoc.createFromURL(documentURL);
    
    pdfDoc.initSecurityHandler();
    pdfDoc.lock();
    
    // optionally perform some document processing using read write operations 
    // found under 'Editing Page Content' or 'Page Manipulation'

    const data = await pdfDoc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_remove_unused);
    const arr = new Uint8Array(data);
    const blob = new Blob([arr], { type: 'application/pdf' });
  } catch(err) {
    console.log(err.stack)
  }
}
PDFNet.runWithCleanup(main, licenseKey);
</code></pre>

After acquiring a `PDFDoc` object, use the [`saveMemoryBuffer`](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#saveMemoryBuffer__anchor) function to retrieve the document data. `saveMemoryBuffer` takes in an enum flag similar to `getFileData` flags. The flags can be found in `PDFNet.SDFDoc.SaveOptions` and their values are:

* `e_remove_unused`
* `e_hex_strings`
* `e_omit_xref`
* `e_linearized`
* `e_compatibility`

There is also an `e_incremental` flag but it's ignored when using `saveMemoryBuffer`. `saveMemoryBuffer` modifies the `PDFDoc` data stored in memory, so it's best practice to acquire a write lock for the document.


# Create rectangle annotation using JavaScript

Learn how to create a rectangle annotation using JavaScript with this example. Explore how to generate various annotations easily. Start annotating now! The Apryse Web SDK streamlines secure, serverle

Here is an example of creating a rectangle annotation using JavaScript, but creating other types of annotations are similar.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { Annotations, annotationManager, documentViewer } = instance.Core;

    documentViewer.addEventListener('documentLoaded', () => {
      const rectangleAnnot = new Annotations.RectangleAnnotation();
      rectangleAnnot.PageNumber = 1;
      // values are in page coordinates with (0, 0) in the top left
      rectangleAnnot.X = 100;
      rectangleAnnot.Y = 150;
      rectangleAnnot.Width = 200;
      rectangleAnnot.Height = 50;
      rectangleAnnot.Author = annotationManager.getCurrentUser();

      annotationManager.addAnnotation(rectangleAnnot);
      // need to draw the annotation otherwise it won't show up until the page is refreshed
      annotationManager.redrawAnnotation(rectangleAnnot);
    })
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [Annotations.RectangleAnnotation](https://sdk.apryse.com/api/web/Core.Annotations.RectangleAnnotation.html) [AnnotationManager.getCurrentUser](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#getCurrentUser__anchor) [AnnotationManager.addAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#addAnnotation__anchor) [AnnotationManager.redrawAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#redrawAnnotation__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { Annotations, annotManager, docViewer } = instance;

    docViewer.on('documentLoaded', () => {
      const rectangleAnnot = new Annotations.RectangleAnnotation();
      rectangleAnnot.PageNumber = 1;
      // values are in page coordinates with (0, 0) in the top left
      rectangleAnnot.X = 100;
      rectangleAnnot.Y = 150;
      rectangleAnnot.Width = 200;
      rectangleAnnot.Height = 50;
      rectangleAnnot.Author = annotManager.getCurrentUser();

      annotManager.addAnnotation(rectangleAnnot);
      // need to draw the annotation otherwise it won't show up until the page is refreshed
      annotManager.redrawAnnotation(rectangleAnnot);
    })
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [Annotations.RectangleAnnotation](https://sdk.apryse.com/api/web/Core.Annotations.RectangleAnnotation.html) [AnnotationManager.getCurrentUser](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#getCurrentUser__anchor) [AnnotationManager.addAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#addAnnotation__anchor) [AnnotationManager.redrawAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#redrawAnnotation__anchor)
{% endtab %}
{% endtabs %}


# React PDF Editor Library

WebViewer is a pure client-side JavaScript editor library to view, annotate and edit documents. It is compatible with any JavaScript framework, including React.

WebViewer is a pure client-side JavaScript library to view, annotate and edit documents. It is compatible with any JavaScript framework, including SharePoint, by using a DOM element to place the document viewing component. WebViewer has a pre-built SharePoint sample specifically designed for integrating into a SharePoint project.

<a href="https://showcase.apryse.com" class="button primary">Launch Online Demo</a>

Apryse's fully supported WebViewer is trusted by thousands of innovative startups, governments, and Fortune 500 businesses (see [customers](https://apryse.com/customers)). Apryse technology is built from the ground up and is not dependent on any external third-party open-source software.

<a href="/web/get-started/libraries-and-frameworks/react" class="button primary">Get started</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="https://github.com/ApryseSDK/webviewer-react-sample/" class="button primary">Github sample</a>

{% embed url="<https://www.youtube.com/embed/bVhWXuLSL0k>" %}

## Customizable

* [UI source code is included (built in React)](/web/ui-customization/ui-customization)
* [Create custom annotations](/web/annotation/customize/custom-annotations)
* [Change annotation appearance](/web/annotation/customizing-default-annotation-styles)
* [Customize annotation behaviour](/web/annotation/customizing-tools)
* [Internationalization through JSON language files](/web/ui-customization/localization/languages-and-internationalization)
* [Rich set of APIs for more advanced customization](https://sdk.apryse.com/api/web/index.html)

## File Type Support

* All versions of PDF and PDF/A
* DOC, DOCX, XLS, XLSX, PPT, PPTX (MS Office software or licenses **not** required)
* JPG, PNG, JFIF, TIF, TIFF
* MP4, WEBM, OGG (via [WebViewer Video build ](/web/video/video))
* MP3, WAV, OGG, FLAC (via [WebViewer Video build ](/web/video/video))
* And [many more](/web/what-is-webviewer/file-format-support)

[Additional formats](/web/what-is-webviewer/file-format-support) available with the optional [WebViewer Server](/web/webviewer-server/wv-server-deployment) and [Custom Server](/web/custom-server/custom-server-deployment) setups.

## Browser Support

WebViewer supports the **last 3 major versions** of each browser, in line with industry standard. This keeps compatibility predictable without requiring ongoing maintenance of this page.

| Browser        | Client only | WebViewer Server | Custom server |
| -------------- | ----------- | ---------------- | ------------- |
| Chrome         | ✓           | ✓                | ✓             |
| Firefox        | ✓           | ✓                | ✓             |
| Safari         | ✓           | ✓                | ✓             |
| Edge           | ✓           | ✓                | ✓             |
| Android Chrome | ✓           | ✓                | ✓             |

{% hint style="info" %}
**What "last 3 major versions" means:** As browsers release new versions, support automatically shifts forward. This matches the policy used by most major platforms and frameworks — no manual updates to this page are required.
{% endhint %}

{% hint style="warning" %}
Changed in WebViewer version 12.0: We previously listed specific minimum version numbers per browser. That table has been replaced with the last-3-versions policy above. If you were relying on an older browser version, please review your compatibility requirements.
{% endhint %}

## Free Trial

* There are no feature limitations during the trial
* [Sign-up](https://dev.apryse.com) to get your trial key
* Chat with us on [Discord](https://apryse.com/discord?docLink=freDis)


# Electron PDF SDK

Discover the ultimate file type support with customizable features for Windows 7+, Linux, Ubuntu, Fedora, and Debian. Try it out with a free trial today! The Apryse Web SDK streamlines secure, serverl

WebViewer is a pure client-side JavaScript library to view, annotate and edit documents. It is compatible with any JavaScript framework, including SharePoint, by using a DOM element to place the document viewing component. WebViewer has a pre-built SharePoint sample specifically designed for integrating into a SharePoint project.

<a href="https://showcase.apryse.com" class="button primary">Launch Online Demo</a>

Apryse's fully supported WebViewer is trusted by thousands of innovative startups, governments, and Fortune 500 businesses (see [customers](https://apryse.com/customers)). Apryse technology is built from the ground up and is not dependent on any external third-party open-source software.

{% embed url="<https://www.youtube.com/embed/FyZ40lNE-pY>" %}

<a href="/web/get-started/libraries-and-frameworks/electron" class="button primary">Get started</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="https://github.com/ApryseSDK/webviewer-electron-sample/" class="button primary">Github sample</a>

## Customizable

* [UI source code is included (built in React)](/web/ui-customization/ui-customization)
* [Create custom annotations](/web/annotation/customize/custom-annotations)
* [Change annotation appearance](/web/annotation/customizing-default-annotation-styles)
* [Customize annotation behaviour](/web/annotation/customizing-tools)
* [Internationalization through JSON language files](/web/ui-customization/localization/languages-and-internationalization)
* [Rich set of APIs for more advanced customization](https://sdk.apryse.com/api/web/index.html)

## File type support

* All versions of PDF and PDF/A
* DOC, DOCX, XLS, XLSX, PPT, PPTX (MS Office software or licenses **not** required)
* JPG, PNG, JFIF, TIF, TIFF
* MP4, WEBM, OGG (via [WebViewer Video build ](/web/video/video))
* MP3, WAV, OGG, FLAC (via [WebViewer Video build ](/web/video/video))
* And [many more](/web/what-is-webviewer/file-format-support)

[Additional formats](/web/what-is-webviewer/file-format-support) available with the optional [WebViewer Server](/web/webviewer-server/wv-server-deployment) and [Custom Server](/web/custom-server/custom-server-deployment) setups.

## OS support

* Windows 7+
* Linux:
  * Ubuntu 12.04 and newer
  * Fedora 21
  * Debian 8

## Free trial

* There are no feature limitations during the trial
* [Sign-up](https://dev.apryse.com) to get your trial key
* Chat with us on [Discord](https://apryse.com/discord?docLink=freDis)


# Opening MS Office documents using JavaScript

Learn how to open MS Office documents using JavaScript by initializing the Office worker. Follow this snippet for a seamless process similar to creating a PDF document. The Apryse Web SDK streamlines

Opening an MS Office document is the same as creating a PDF document except that we need to initialize the Office worker.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">// Instantiate a Document object.
const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>';
const source = '/url/to/file.docx';

try {
  // If you are loading the Office file as a Blob, you need pass Blob directly 
  // as a first paramater to createDocument
  const doc = await Core.createDocument(source, { l: licenseKey });
  // The PDF file has been created, you can call APIs of the Document class
} catch (error) {
  
}
</code></pre>

[Core.createDocument](https://docs.apryse.com/api/web/Core.html#.createDocument__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">// Instantiate a Document object.
const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>';
const source = '/url/to/file.docx';

try {
  // If you are loading the Office file as a Blob, you need pass Blob directly 
  // as a first paramater to createDocument
  const doc = await CoreControls.createDocument(source, { l: licenseKey });
  // The PDF file has been created, you can call APIs of the Document class
} catch (error) {
  
}
</code></pre>

[CoreControls.createDocument](https://docs.apryse.com/api/web/Core.html#.createDocument__anchor)
{% endtab %}
{% endtabs %}


# Applying page manipulations to PDFs directly in the client

Enhance PDFs directly in the client with page manipulations using WebViewer. Rotate, crop, reorder, add, or delete pages seamlessly for visible changes in any PDF viewer. The Apryse Web SDK streamline

When WebViewer is running in [client only mode](/web/what-is-webviewer/deployment-options#client-only) it is able to perform many different kinds of page manipulations. These include rotating, cropping, reordering, adding, and deleting pages. Instead of temporarily modifying the document in the viewer, these operations modify the actual PDF data.

This means that when the download button is pressed or [getFileData](https://sdk.apryse.com/api/web/Core.Document.html#getFileData__anchor) is called, the returned PDF will have the manipulations applied and they will be visible in any PDF viewer.

{% hint style="warning" %}
If you're using the WebViewer Server deployment option then you'll need to enable the `forceClientSideInit: true` constructor option. This will initialize the document with WebViewer Server and then switch to client side processing.
{% endhint %}


# Angular PDF Library

Unlock the power of customizable file type support and browser compatibility with a free trial. Try it now! The Apryse Web SDK streamlines secure, serverless document processing with robust support fo

WebViewer is a pure client-side JavaScript library to view, annotate and edit documents. It is compatible with any JavaScript framework, including SharePoint, by using a DOM element to place the document viewing component. WebViewer has a pre-built SharePoint sample specifically designed for integrating into a SharePoint project.

<a href="https://showcase.apryse.com" class="button primary">Launch Online Demo</a>

Apryse's fully supported WebViewer is trusted by thousands of innovative startups, governments, and Fortune 500 businesses (see [customers](https://apryse.com/customers)). Apryse technology is built from the ground up and is not dependent on any external third-party open-source software.

<a href="/web/get-started/libraries-and-frameworks/angular" class="button primary">Get started</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="https://github.com/ApryseSDK/webviewer-angular-sample/" class="button primary">Github sample</a>

{% embed url="<https://www.youtube.com/embed/OxNjs4dc6zY>" %}

## Customizable

* [UI source code is included (built in React)](/web/ui-customization/ui-customization)
* [Create custom annotations](/web/annotation/customize/custom-annotations)
* [Change annotation appearance](/web/annotation/customizing-default-annotation-styles)
* [Customize annotation behaviour](/web/annotation/customizing-tools)
* [Internationalization through JSON language files](/web/ui-customization/localization/languages-and-internationalization)
* [Rich set of APIs for more advanced customization](https://sdk.apryse.com/api/web/index.html)

## File Type Support

* All versions of PDF and PDF/A
* DOC, DOCX, XLS, XLSX, PPT, PPTX (MS Office software or licenses **not** required)
* JPG, PNG, JFIF, TIF, TIFF
* MP4, WEBM, OGG (via [WebViewer Video build ](/web/video/video))
* MP3, WAV, OGG, FLAC (via [WebViewer Video build ](/web/video/video))
* And [many more](/web/what-is-webviewer/file-format-support)

[Additional formats](/web/what-is-webviewer/file-format-support) available with the optional [WebViewer Server](/web/webviewer-server/wv-server-deployment) and [Custom Server](/web/custom-server/custom-server-deployment) setups.

## Browser Support

WebViewer supports the **last 3 major versions** of each browser, in line with industry standard. This keeps compatibility predictable without requiring ongoing maintenance of this page.

| Browser        | Client only | WebViewer Server | Custom server |
| -------------- | ----------- | ---------------- | ------------- |
| Chrome         | ✓           | ✓                | ✓             |
| Firefox        | ✓           | ✓                | ✓             |
| Safari         | ✓           | ✓                | ✓             |
| Edge           | ✓           | ✓                | ✓             |
| Android Chrome | ✓           | ✓                | ✓             |

{% hint style="info" %}
**What "last 3 major versions" means:** As browsers release new versions, support automatically shifts forward. This matches the policy used by most major platforms and frameworks — no manual updates to this page are required.
{% endhint %}

{% hint style="warning" %}
Changed in WebViewer version 12.0: We previously listed specific minimum version numbers per browser. That table has been replaced with the last-3-versions policy above. If you were relying on an older browser version, please review your compatibility requirements.
{% endhint %}

## Free Trial

* There are no feature limitations during the trial
* [Sign-up](https://dev.apryse.com) to get your trial key
* Chat with us on [Discord](https://apryse.com/discord?docLink=freDis)


# Adding walkthrough or product tour in WebViewer

Enhance user experience with WebViewer Walkthrough. Learn how to create interactive product tours or tutorials seamlessly within WebViewer using step-by-step guidance. Optimize user engagement and nav

[WebViewer Walkthrough](https://www.npmjs.com/package/@pdftron/webviewer-walkthrough/) is a package that allows you to create step-by-step walkthroughs or tutorials in WebViewer.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-b487672c9f82afc868d0e055775a2f2f8292de7e%2Fc7bb683924523763946aceb59cb8dd8fd7b464a3-2126x1228.png?alt=media)

## Create a walkthrough

### Install

To create the walkthrough, ensure that you also have `@pdftron/webviewer-walkthrough` installed by running `npm i @pdftron/webviewer-walkthrough`. Then import the package and destructure the `initializeWalkthrough` function.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
import WebViewer from '@pdftron/webviewer';
import { initializeWalkthrough } from '@pdftron/webviewer-walkthrough';
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Define steps

Create an array of `Step` objects that contain the follow keys:

* `dataElement`: The UI element where you want to get the user's attention
* `header` (optional): The header text that is rendered in the pop-up
* `text`: The descriptive text that is rendered in the pop-up

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
const steps = [
  {
    dataElement: 'leftPanelButton',
    header: 'Page Thumbnails',
    text: 'You can see all of the page thumbnails here.'
  },
  {
    dataElement: 'pageNavOverlay',
    text: 'Navigate pages'
  }
]
```

{% endcode %}
{% endtab %}
{% endtabs %}

To find the correct `dataElement` please refer to the [guide about how to show/hide elements](/web/ui-customization/hiding-elements).

### Initialize the walkthrough

Once you have an array of `Step` objects defined, call `initializeWalkthrough` function with the following parameters:

* `viewerElement`: The same element that WebViewer is mounted on (i.e. the second argument to the WebViewer constructor)
* `steps`: The array of `Step` objects defined above
* `callback`: An `onComplete` callback function that is invoked upon completion of all the steps
* `options`: An object that customizes the pop-up, see the section [Customizing the steps](https://docs.apryse.com)

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer(
  {
    ...
  },
  viewerElement
).then((instance) => {
  const { start, exit } = initializeWalkthrough(
    viewerElement,
    steps,
    () => {
      console.log('tutorial complete...');
    },
  );
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Start the walkthrough

The `initializeWalkthrough` returns an object containing two functions: `start` and `exit`. `start` function begins the walkthrough and `exit` ends the walkthrough when invoked. The walkthrough can also be terminated by the user via the `X` button in the top-right corner of any pop-up.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
start();
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Customizing the steps

You can customize the appearance of the card as well, by passing `options` object containing colors:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
const options = {
  focusColor: '#FA4E32',
  backgroundColor: '#FA4E32',
  headerColor: '#36110B',
  textColor: '#1F0D06',
  iconColor: '#fff'
};
const { start, exit } = initializeWalkthrough(
  // First 3 arguments
  options,
);

start();
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Svelte PDF library

Discover the benefits of customizable file type support, browser compatibility, and a free trial with our innovative software. Try it now! The Apryse Web SDK streamlines secure, serverless document pr

WebViewer is a pure client-side JavaScript library to view, annotate and edit documents. It is compatible with any JavaScript framework, including SharePoint, by using a DOM element to place the document viewing component. WebViewer has a pre-built SharePoint sample specifically designed for integrating into a SharePoint project.

<a href="https://showcase.apryse.com" class="button primary">Launch Online Demo</a>

Apryse's fully supported WebViewer is trusted by thousands of innovative startups, governments, and Fortune 500 businesses (see [customers](https://apryse.com/customers)). Apryse technology is built from the ground up and is not dependent on any external third-party open-source software.

<a href="/web/get-started/libraries-and-frameworks/svelte" class="button primary">Get started</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="https://github.com/ApryseSDK/webviewer-svelte-sample/" class="button primary">Github sample</a>

## Customizable

* [UI source code is included (built in React)](/web/ui-customization/ui-customization)
* [Create custom annotations](/web/annotation/customize/custom-annotations)
* [Change annotation appearance](/web/annotation/customizing-default-annotation-styles)
* [Customize annotation behaviour](/web/annotation/customizing-tools)
* [Internationalization through JSON language files](/web/ui-customization/localization/languages-and-internationalization)
* [Rich set of APIs for more advanced customization](https://sdk.apryse.com/api/web/index.html)

## File Type Support

* All versions of PDF and PDF/A
* DOC, DOCX, XLS, XLSX, PPT, PPTX (MS Office software or licenses **not** required)
* JPG, PNG, JFIF, TIF, TIFF
* MP4, WEBM, OGG (via [WebViewer Video build ](/web/video/video))
* MP3, WAV, OGG, FLAC (via [WebViewer Video build ](/web/video/video))
* And [many more](/web/what-is-webviewer/file-format-support)

[Additional formats](/web/what-is-webviewer/file-format-support) available with the optional [WebViewer Server](/web/webviewer-server/wv-server-deployment) and [Custom Server](/web/custom-server/custom-server-deployment) setups.

## Browser Support

WebViewer supports the **last 3 major versions** of each browser, in line with industry standard. This keeps compatibility predictable without requiring ongoing maintenance of this page.

| Browser        | Client only | WebViewer Server | Custom server |
| -------------- | ----------- | ---------------- | ------------- |
| Chrome         | ✓           | ✓                | ✓             |
| Firefox        | ✓           | ✓                | ✓             |
| Safari         | ✓           | ✓                | ✓             |
| Edge           | ✓           | ✓                | ✓             |
| Android Chrome | ✓           | ✓                | ✓             |

{% hint style="info" %}
**What "last 3 major versions" means:** As browsers release new versions, support automatically shifts forward. This matches the policy used by most major platforms and frameworks — no manual updates to this page are required.
{% endhint %}

{% hint style="warning" %}
Changed in WebViewer version 12.0: We previously listed specific minimum version numbers per browser. That table has been replaced with the last-3-versions policy above. If you were relying on an older browser version, please review your compatibility requirements.
{% endhint %}

## Free Trial

* There are no feature limitations during the trial
* [Sign-up](https://dev.apryse.com) to get your trial key
* Chat with us on [Discord](https://apryse.com/discord?docLink=freDis)


# Create file attachment annotation using JavaScript

Learn how to create a file attachment annotation using JavaScript with this helpful snippet. Discover how to easily implement this feature and explore other annotation options. The Apryse Web SDK stre

Here is an example of creating a file attachment annotation using JavaScript, but creating other types of annotations are similar.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { Annotations, annotationManager, documentViewer } = instance.Core;

    documentViewer.addEventListener('documentLoaded', async() => {
      const fileAttachmentAnnot = new Annotations.FileAttachmentAnnotation();
      fileAttachmentAnnot.PageNumber = 1;
      fileAttachmentAnnot.X = 100;
      fileAttachmentAnnot.Y = 150;
      fileAttachmentAnnot.Author = annotationManager.getCurrentUser();

      const { data, mimeType, filename } = await getFileData(filePath);
      await fileAttachmentAnnot.setFileData(data, mimeType, filename);

      annotationManager.addAnnotation(fileAttachmentAnnot);
      annotationManager.redrawAnnotation(fileAttachmentAnnot);
    });
  });


const getFileData = async(filePath) => {
  const response = await fetch(filePath);
  const blob = await response.blob();

  return new Promise((resolve) => {
    const reader = new FileReader();

    reader.addEventListener('load', (e) => {
      const data = e.target.result;

      resolve({
        data,
        mimeType: response.headers.get('Content-Type'),
        filename: 'download.pdf',
      });
    });

    reader.readAsArrayBuffer(blob);
  });
};
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [Annotations.FileAttachmentAnnotation](https://sdk.apryse.com/api/web/Core.Annotations.FileAttachmentAnnotation.html) [Annotations.FileAttachmentAnnotation.setFileData](https://sdk.apryse.com/api/web/Core.Annotations.FileAttachmentAnnotation.html#setFileData__anchor) [AnnotationManager.getCurrentUser](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#getCurrentUser__anchor) [AnnotationManager.addAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#addAnnotation__anchor) [AnnotationManager.redrawAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#redrawAnnotation__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { Annotations, annotManager, docViewer } = instance;

    docViewer.on('documentLoaded', async() => {
      const fileAttachmentAnnot = new Annotations.FileAttachmentAnnotation();
      fileAttachmentAnnot.PageNumber = 1;
      fileAttachmentAnnot.X = 100;
      fileAttachmentAnnot.Y = 150;
      fileAttachmentAnnot.Author = annotManager.getCurrentUser();

      const { data, mimeType, filename } = await getFileData(filePath);
      await fileAttachmentAnnot.setFileData(data, mimeType, filename);

      annotManager.addAnnotation(fileAttachmentAnnot);
      annotManager.redrawAnnotation(fileAttachmentAnnot);
    });
  });


const getFileData = async(filePath) => {
  const response = await fetch(filePath);
  const blob = await response.blob();

  return new Promise((resolve) => {
    const reader = new FileReader();

    reader.addEventListener('load', (e) => {
      const data = e.target.result;

      resolve({
        data,
        mimeType: response.headers.get('Content-Type'),
        filename: 'download.pdf',
      });
    });

    reader.readAsArrayBuffer(blob);
  });
};
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [Annotations.FileAttachmentAnnotation](https://sdk.apryse.com/api/web/Core.Annotations.FileAttachmentAnnotation.html) [Annotations.FileAttachmentAnnotation.setFileData](https://sdk.apryse.com/api/web/Core.Annotations.FileAttachmentAnnotation.html#setFileData__anchor) [AnnotationManager.getCurrentUser](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#getCurrentUser__anchor) [AnnotationManager.addAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#addAnnotation__anchor) [AnnotationManager.redrawAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#redrawAnnotation__anchor)
{% endtab %}
{% endtabs %}


# Typescript PDF library

Discover WebViewer, a client-side JavaScript library for viewing, annotating, and editing documents. Compatible with any JavaScript framework, including TypeScript. Trusted by startups, governments, a

WebViewer is a pure client-side JavaScript library to view, annotate and edit documents. It is compatible with any JavaScript framework, including a superset like TypeScript. WebViewer has a declaration file, as well as a pre-built TypeScript sample, specifically designed for integrating into a TypeScript project.

<a href="https://showcase.apryse.com" class="button primary">Launch Online Demo</a>

Apryse’s fully supported WebViewer is trusted by thousands of innovative startups, governments, and Fortune 500 businesses (see [customers](https://apryse.com/customers)). Apryse technology is built from the ground up and is not dependent on any external third-party open-source software.

<a href="/web/get-started/typescript" class="button primary">Get started</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="https://github.com/ApryseSDK/webviewer-typescript-sample/" class="button primary">Github sample</a>

## Customizable

* [UI source code is included (built in React)](/web/ui-customization/ui-customization)
* [Create custom annotations](/web/annotation/customize/custom-annotations)
* [Change annotation appearance](/web/annotation/customizing-default-annotation-styles)
* [Customize annotation behaviour](/web/annotation/customizing-tools)
* [Internationalization through JSON language files](/web/ui-customization/localization/languages-and-internationalization)
* [Rich set of APIs for more advanced customization](https://sdk.apryse.com/api/web/index.html)

## File Type Support

* All versions of PDF and PDF/A
* DOC, DOCX, XLS, XLSX, PPT, PPTX (MS Office software or licenses **not** required)
* JPG, PNG, JFIF, TIF, TIFF
* MP4, WEBM, OGG (via [WebViewer Video build ](/web/video/video))
* MP3, WAV, OGG, FLAC (via [WebViewer Video build ](/web/video/video))
* And [many more](/web/what-is-webviewer/file-format-support)

[Additional formats](/web/what-is-webviewer/file-format-support) available with the optional [WebViewer Server](/web/webviewer-server/wv-server-deployment) and [Custom Server](/web/custom-server/custom-server-deployment) setups.

## Browser Support

WebViewer supports the **last 3 major versions** of each browser, in line with industry standard. This keeps compatibility predictable without requiring ongoing maintenance of this page.

| Browser        | Client only | WebViewer Server | Custom server |
| -------------- | ----------- | ---------------- | ------------- |
| Chrome         | ✓           | ✓                | ✓             |
| Firefox        | ✓           | ✓                | ✓             |
| Safari         | ✓           | ✓                | ✓             |
| Edge           | ✓           | ✓                | ✓             |
| Android Chrome | ✓           | ✓                | ✓             |

{% hint style="info" %}
**What "last 3 major versions" means:** As browsers release new versions, support automatically shifts forward. This matches the policy used by most major platforms and frameworks — no manual updates to this page are required.
{% endhint %}

{% hint style="warning" %}
Changed in WebViewer version 12.0: We previously listed specific minimum version numbers per browser. That table has been replaced with the last-3-versions policy above. If you were relying on an older browser version, please review your compatibility requirements.
{% endhint %}

## Free Trial

* There are no feature limitations during the trial
* [Sign-up](https://dev.apryse.com) to get your trial key
* Chat with us on [Discord](https://apryse.com/discord?docLink=freDis)


# Create XOD document

Create XOD documents easily with our guide. Learn how to efficiently generate XOD files using simple steps. Start creating XOD documents now! The Apryse Web SDK streamlines secure, serverless document

To create a XOD document.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
// The extension option is optional if it can be inferred from the URL
// but required if it cannot, or if a blob or file is loaded
const options = { extension: 'xod' };
const doc = await Core.createDocument('/path/file.xod', {});
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
// The extension option is optional if it can be inferred from the URL
// but required if it cannot, or if a blob or file is loaded
const options = { extension: 'xod' };
const doc = await CoreControls.createDocument('/path/file.xod', {});
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Creating Office documents using JavaScript

Create, view, and manipulate Office documents using JavaScript with ease. Learn how to work with DOCX, XLSX, and PPTX files directly in your browser with WebViewer. The Apryse Web SDK streamlines secu

View DOCX, XLSX, PPTX in a browser without any server-side dependencies. WebViewer allows viewing of MS Office files similar to PDF, images and other formats.

If you have a URL for a document, you can pass it to the [WebViewer constructor](#webviewer-constructor) or [loadDocument](#load-document) function to open it.

If you are hoping to convert to a PDF without UI, refer to our [conversion guide](/web/ms-office/convert-office).

## WebViewer constructor

Load the office document with the WebViewer constructor and it will load with the Viewer.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  ...,
  initialDoc: 'https://myserver.com/myfile.docx',
}, document.getElementById('viewer'));
```

{% endcode %}
{% endtab %}
{% endtabs %}

[WebViewer](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor)

[Display & view documents using JavaScript](/web/get-started/samples) Full sample code shows how to call WebViewer constructor to instantiate and load document. You can load local/remote files of your choice.

## Load document

Load the office document with the load document method after WebViewer is initalized.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    instance.UI.loadDocument('https://myserver.com/myfile.docx', { filename: 'myfile.docx' });
  });
```

{% endcode %}

[WebViewer](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor) [WebViewerInstance.UI.loadDocument](https://sdk.apryse.com/api/web/UI.html#loadDocument__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    instance.loadDocument('https://myserver.com/myfile.docx', { filename: 'myfile.docx' });
  });
```

{% endcode %}

[WebViewer](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor) [WebViewerInstance.loadDocument](https://sdk.apryse.com/api/web/UI.html#.loadDocument)
{% endtab %}
{% endtabs %}

[Display & view documents using JavaScript](/web/get-started/samples) Full sample code shows how to call WebViewer constructor to instantiate and load document. You can load local/remote files of your choice.

## Load document options

Starting version 8, Office documents can be loaded with [Office conversion options](https://sdk.apryse.com/api/PDFTronSDK/cpp/classpdftron_1_1_p_d_f_1_1_office_to_p_d_f_options.html).

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
      const opts = { ExcelMaxAllowedCellCount: 250000 };
      const doc = await instance.Core.createDocument(file, { officeOptions : { officeToPDFOptions: opts }});
    instance.UI.loadDocument('https://myserver.com/myfile.xslx', { filename: 'myfile.xslx' });
  });
```

{% endcode %}
{% endtab %}
{% endtabs %}

Available conversion options:

| Option                      | Description                                                                                                                                                                                                                                                                                                                                                                                                    |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ApplyPageBreaksToSheet      | Whether we should split Excel workheets into pages so that the output resembles print output. Defaults to false.                                                                                                                                                                                                                                                                                               |
| DisplayChangeTracking       | If this option is true, will display office change tracking markup present in the document (i.e, red strikethrough of deleted content and underlining of new content). Otherwise displays the resolved document content, with no markup. Defaults to true.                                                                                                                                                     |
| ExcelDefaultCellBorderWidth | Cell border width for table cells that would normally be drawn with no border. In units of points. Can be used to achieve a similar effect to the "show gridlines" display option within Microsoft Excel.                                                                                                                                                                                                      |
| ExcelMaxAllowedCellCount    | Conversion will throw an exception if the number of cells in a Microsoft Excel document is above the set MaxAllowedCellCount. Used for early termination of resource intensive conversions. Setting this value to 250000 will allow the vast majority of Excel documents to convert without issue, while keeping RAM usage to a reasonable level. By default there is no limit to the number of allowed cells. |
| LayoutResourcesPluginPath   | The path at which the pdftron-provided font resource plugin resides.                                                                                                                                                                                                                                                                                                                                           |
| Locale                      | ISO 639-1 code of the current system locale. For example: 'en-US', 'ar-SA', 'de-DE', etc.                                                                                                                                                                                                                                                                                                                      |
| ResourceDocPath             | The path at which a .docx resource document resides.                                                                                                                                                                                                                                                                                                                                                           |
| SmartSubstitutionPluginPath | The path at which the pdftron-provided font resource plugin resides.                                                                                                                                                                                                                                                                                                                                           |

## Advanced office loading

Creating an Office document is similar to creating a PDF document except the Office worker needs to be initialized.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">&#x3C;html>
  &#x3C;body>
    &#x3C;script src="../lib/core/webviewer-core.min.js">&#x3C;/script>
    &#x3C;script>
    (async function() {
      Core.setWorkerPath('../lib/core');
      const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>';
      // Instantiate a Document object.
      const doc = await Core.createDocument('/path/to/file.docx', { l: licenseKey });
    })()
    &#x3C;/script>
  &#x3C;/body>
&#x3C;/html>
</code></pre>

[Core.setWorkerPath](https://sdk.apryse.com/api/web/Core.html#.setWorkerPath__anchor) [Core.createDocument](https://sdk.apryse.com/api/web/Core.html#.createDocument__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">&#x3C;html>
  &#x3C;body>
    &#x3C;script src="../lib/core/CoreControls.js">&#x3C;/script>
    &#x3C;script>
    (async function() {
      CoreControls.setWorkerPath('../lib/core');
      const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>';
      // Instantiate a Document object.
      const doc = await CoreControls.createDocument('/path/to/file.docx', { l: licenseKey });
    })()
    &#x3C;/script>
  &#x3C;/body>
&#x3C;/html>
</code></pre>

[CoreControls.setWorkerPath](https://sdk.apryse.com/api/web/Core.html#.setWorkerPath__anchor) [CoreControls.createDocument](https://sdk.apryse.com/api/web/Core.html#.createDocument__anchor)
{% endtab %}
{% endtabs %}

Alternatively, Core is available with the WebViewer instance as well. Note that in version < 8, it is called CoreControls.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">WebViewer(...)
  .then(async instance => {
    const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>';
    // Instantiate a Document object.
    const doc = await instance.Core.createDocument('/path/to/file.docx', { l: licenseKey });
  });
</code></pre>

[WebViewer](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor) [WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [Core](https://sdk.apryse.com/api/web/Core.html) [Core.createDocument](https://sdk.apryse.com/api/web/Core.html#.createDocument__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">WebViewer(...)
  .then(async instance => {
    const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>';
    // Instantiate a Document object.
    const doc = await instance.CoreControls.createDocument('/path/to/file.docx', { l: licenseKey });
  });
</code></pre>

[WebViewer](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor) [WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [CoreControls.createDocument](https://sdk.apryse.com/api/web/Core.html#.createDocument__anchor)
{% endtab %}
{% endtabs %}

## Related blogs

**Viewing DOCX Files** Angular: [How to View DOCX in an Angular Web App](https://apryse.com/blog/webviewer/how-to-view-docx-in-angular-v2)- 10/15/2024 Vue: [How to Open DOCX in a Vue Web App](https://apryse.com/blog/webviewer/how-to-open-docx-in-vue) - 7/11/2023

**Viewing XLSX Docs** React: [How to View XLSX Documents in a React Web App](https://apryse.com/blog/webviewer/how-view-xlsx-documents-in-a-react-app) - 7/11/2022 Angular: [How to View XLSX Documents in a Angular Web App](https://apryse.com/blog/webviewer/how-view-xlsx-documents-in-an-angular-viewer) - 7/25/2022 Vue: [How to View XLSX Documents in a Vue Web App](https://apryse.com/blog/webviewer/how-view-xlsx-documents-in-vue) - 7/27/2022


# Blazor PDF Library

Discover the benefits of customizable file type support, browser support, and a free trial with our innovative software in your Blazor app. Try it now!

WebViewer is a pure client-side JavaScript library to view, annotate and edit documents. It is compatible with any JavaScript framework, including SharePoint, by using a DOM element to place the document viewing component. WebViewer has a pre-built SharePoint sample specifically designed for integrating into a SharePoint project.

<a href="https://showcase.apryse.com" class="button primary">Launch Online Demo</a>

Apryse's fully supported WebViewer is trusted by thousands of innovative startups, governments, and Fortune 500 businesses (see [customers](https://apryse.com/customers)). Apryse technology is built from the ground up and is not dependent on any external third-party open-source software.

<a href="/web/get-started/libraries-and-frameworks/blazor" class="button primary">Get started</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="https://github.com/ApryseSDK/webviewer-blazor-sample/" class="button primary">Github sample</a>

## Customizable

* [UI source code is included (built in React)](/web/ui-customization/ui-customization)
* [Create custom annotations](/web/annotation/customize/custom-annotations)
* [Change annotation appearance](/web/annotation/customizing-default-annotation-styles)
* [Customize annotation behaviour](/web/annotation/customizing-tools)
* [Internationalization through JSON language files](/web/ui-customization/localization/languages-and-internationalization)
* [Rich set of APIs for more advanced customization](https://sdk.apryse.com/api/web/index.html)

## File Type Support

* All versions of PDF and PDF/A
* DOC, DOCX, XLS, XLSX, PPT, PPTX (MS Office software or licenses **not** required)
* JPG, PNG, JFIF, TIF, TIFF
* MP4, WEBM, OGG (via [WebViewer Video build ](/web/video/video))
* MP3, WAV, OGG, FLAC (via [WebViewer Video build ](/web/video/video))
* And [many more](/web/what-is-webviewer/file-format-support)

[Additional formats](/web/what-is-webviewer/file-format-support) available with the optional [WebViewer Server](/web/webviewer-server/wv-server-deployment) and [Custom Server](/web/custom-server/custom-server-deployment) setups.

## Browser Support

WebViewer supports the **last 3 major versions** of each browser, in line with industry standard. This keeps compatibility predictable without requiring ongoing maintenance of this page.

| Browser        | Client only | WebViewer Server | Custom server |
| -------------- | ----------- | ---------------- | ------------- |
| Chrome         | ✓           | ✓                | ✓             |
| Firefox        | ✓           | ✓                | ✓             |
| Safari         | ✓           | ✓                | ✓             |
| Edge           | ✓           | ✓                | ✓             |
| Android Chrome | ✓           | ✓                | ✓             |

{% hint style="info" %}
**What "last 3 major versions" means:** As browsers release new versions, support automatically shifts forward. This matches the policy used by most major platforms and frameworks — no manual updates to this page are required.
{% endhint %}

{% hint style="warning" %}
Changed in WebViewer version 12.0: We previously listed specific minimum version numbers per browser. That table has been replaced with the last-3-versions policy above. If you were relying on an older browser version, please review your compatibility requirements.
{% endhint %}

## Free Trial

* There are no feature limitations during the trial
* [Sign-up](https://dev.apryse.com) to get your trial key
* Chat with us on [Discord](https://apryse.com/discord?docLink=freDis)


# Samples for PDF Viewing using JavaScript

Discover how to view PDFs using JavaScript with these helpful samples. Learn how to load local/remote files, use WebViewer Server, handle XOD files, and optimize PDF viewing. Launch demos and explore

#### Viewing

Shows how to call WebViewer constructor to instantiate and load document. You can load local/remote files of your choice.

<a href="https://sdk.apryse.com/samples/web/samples/viewing/viewing/" class="button primary">Launch Demo</a><a href="/web/get-started/samples/viewing" class="button primary">View Source Code</a>

#### Viewing with WebViewer Server

Shows how to call WebViewer constructor to instantiate and load document using WebViewer with WebViewer Server. You can upload local files or load files that are publicly accessible.

<a href="https://sdk.apryse.com/samples/web/samples/viewing/viewing-with-webviewer-server/" class="button primary">Launch Demo</a><a href="/web/get-started/samples/viewing-with-webviewer-server" class="button primary">View Source Code</a>

#### Viewing with Custom Server (XOD)

Shows how to load XOD files, which are generated from a custom server.

<a href="https://sdk.apryse.com/samples/web/samples/viewing/viewing-with-custom-server/" class="button primary">Launch Demo</a><a href="/web/get-started/samples/viewing-with-custom-server" class="button primary">View Source Code</a>

#### Viewer optimized PDFs

Compares viewing an unoptimized PDF with the same viewer optimized PDF side by side

<a href="https://sdk.apryse.com/samples/web/samples/viewing/fast-viewing/" class="button primary">Launch Demo</a><a href="/web/get-started/samples/fast-viewing" class="button primary">View Source Code</a>


# Migrating to v4

Migrating to v4? Learn about the key changes: Case updates, jQuery removal, async annotations, default viewer switch, and more. Ensure a smooth transition with these essential insights. The Apryse Web

There are a few **breaking changes** when migrating to v4 from older versions.

## Case change

UpperCamelCase in the main files, `WebViewer.min.js` and `WebViewer.js`, have been updated to lowercase.

{% tabs %}
{% tab title="HTML" %}
{% code lineNumbers="true" %}

```html
<!-- Version 3.x and older -->
<script src='lib/WebViewer.min.js'></script>

<!-- Version 4.0 and after -->
<script src='lib/webviewer.min.js'></script>
```

{% endcode %}
{% endtab %}
{% endtabs %}

## No more jQuery

From 4.0, you don't need to load jquery before WebViewer anymore.

{% tabs %}
{% tab title="HTML" %}
{% code lineNumbers="true" %}

```html
<!-- Version 3.x and older -->
<script src='lib/html5/external/jquery-3.2.1.min.js'></script>

<!-- Version 4.0 and after -->
<!-- nothing! -->
```

{% endcode %}
{% endtab %}
{% endtabs %}

This means that event-listening functions need to be updated too.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
// Version 3.x and older
$(viewerElement).on('documentLoaded', function() {
  ...
});

// Version 4.0 and after
viewerElement.addEventListener('documentLoaded', function() {
  ...
});

// Version 3.x and older
$(viewerElement).on('pageChanged', function(e, pageNumber) {
  console.log(pageNumber);
});

// Version 4.0 and after
viewerElement.addEventListener('pageChanged', function(e) {
  const [ pageNumber ] = e.detail;
  console.log(pageNumber);
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

## AnnotationManager drawAnnotations is asynchronous

The [drawAnnotations](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#drawAnnotations__anchor) function is now asynchronous and returns a promise that resolves when drawing has completed.

Generally this won't cause problems for existing code as long as subsequent code isn't relying on the annotation drawing to have completed. If you do rely on this then you'll just need to move the code into a then block. For example:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
// Version 3.x and older
annotManager.drawAnnotations(1);
// something that relies on the annotations to be finished drawing

// Version 4.0 and after
annotManager.drawAnnotations(1).then(function() {
  // something that relies on the annotations to be finished drawing
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

## New default viewer

The default viewer has been updated to WebViewer UI v1, which means that customizations you've made to the UI for the legacy viewer will not work anymore. Note that the new default viewer does not support IE9 or 10. If you want to support IE9 and 10 you will need to use the legacy viewer.

Also note that customizations using the [WebViewer core](/web/ui-customization/core) APIs remain mostly unchanged so those customizations will continue to work.


# Create text highlighting annotations using JavaScript

Learn how to create text highlighting annotations using JavaScript with this example. Easily implement various types of annotations for your web projects. The Apryse Web SDK streamlines secure, server

Here is an example of creating highlight annotations using JavaScript, but creating other types of annotations are similar.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { Annotations, annotationManager, documentViewer } = instance.Core;

    documentViewer.addEventListener('documentLoaded', () => {
      const highlight = new Annotations.TextHighlightAnnotation();
      highlight.PageNumber = 4;
      highlight.X = 405;
      highlight.Y = 165;
      highlight.Width = 275;
      highlight.Height = 25;
      highlight.StrokeColor = new Annotations.Color(255, 255, 0);
      // you might get the quads from text selection, a server calculation, etc
      highlight.Quads = [
        { x1: 644, y1: 178, x2: 682, y2: 178, x3: 682, y3: 168, x4: 644, y4: 168 },
        { x1: 408, y1: 190, x2: 458, y2: 190, x3: 458, y3: 180, x4: 408, y4: 180 }
      ];

      annotationManager.addAnnotation(highlight);
      annotationManager.drawAnnotations(highlight.PageNumber);
    })
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [Annotations.TextHighlightAnnotation](https://sdk.apryse.com/api/web/Core.Annotations.TextHighlightAnnotation.html) [AnnotationManager.addAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#addAnnotation__anchor) [AnnotationManager.redrawAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#redrawAnnotation__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { Annotations, annotManager, docViewer } = instance;

    docViewer.on('documentLoaded', () => {
      const highlight = new Annotations.TextHighlightAnnotation();
      highlight.PageNumber = 4;
      highlight.X = 405;
      highlight.Y = 165;
      highlight.Width = 275;
      highlight.Height = 25;
      highlight.StrokeColor = new Annotations.Color(255, 255, 0);
      // you might get the quads from text selection, a server calculation, etc
      highlight.Quads = [
        { x1: 644, y1: 178, x2: 682, y2: 178, x3: 682, y3: 168, x4: 644, y4: 168 },
        { x1: 408, y1: 190, x2: 458, y2: 190, x3: 458, y3: 180, x4: 408, y4: 180 }
      ];

      annotManager.addAnnotation(highlight);
      annotManager.drawAnnotations(highlight.PageNumber);
    })
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [Annotations.TextHighlightAnnotation](https://sdk.apryse.com/api/web/Core.Annotations.TextHighlightAnnotation.html) [AnnotationManager.addAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#addAnnotation__anchor) [AnnotationManager.redrawAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#redrawAnnotation__anchor)
{% endtab %}
{% endtabs %}


# Cordova PDF SDK

Enhance your Cordova project with Apryse's PDF library for seamless document viewing and editing solutions. Explore the benefits of integrating WebViewer for a consistent UI and extensive API support.

{% hint style="info" %}
This project is no longer in development. If you are looking for cross-platform solution, check out [React Native](https://github.com/ApryseSDK/pdftron-react-native/) or [Flutter](https://github.com/ApryseSDK/pdftron-flutter/). If you are looking to add to your current Cordova/Ionic project, check out the [WebViewer sample](https://github.com/ApryseSDK/webviewer-cordova-sample/).
{% endhint %}

Apryse's Cordova PDF library brings smooth, flexible and stand-alone document viewing and editing solutions for local and server codebases setup using Cordova for Web.

PDTron's PDF SDK is trusted by thousands of innovative startups, governments, and Fortune 500 businesses (see [customers](https://apryse.com/customers)). It is built from the ground up and is not dependent on any external third-party open-source software with support available from Developers and Solution Engineers.

<a href="/web/get-started/readme/cordova" class="button primary">Get started on WebViewer</a>

## Implementation

Depending on your requirements and preferred development environment, Apryse can be integrated into a Cordova project through our pure JavaScript library (WebViewer).

The relative strengths of each approach are outlined below. This will help you decide which approach with Cordova best suits your needs.

**Relative strengths of WebViewer**

* **Extensive API**: The full WebViewer API is immediately available. (The native plugins currently expose a subset of their APIS to JavaScript.)
* **Identical API**: The API used for a website written using WebViewer will be 100% identical that used in the Ionic/Cordova app.
* **Identical UI**: The UI is the same across all devices (rather than adopting the platform's UI norms).
* **.xod files**: The WebViewer can open and render .xod files, which are PDF files that have been optimized for web-viewing. (PDF files can also be rendered directly, but with degradation in performance.)

Get started using Cordova with Apryse technology:

<a href="/web" class="button primary">WebViewer</a>

### GitHub samples

* [WebViewer & Cordova](https://github.com/ApryseSDK/webviewer-cordova-sample/)

## WebViewer

WebViewer is a pure client-side JavaScript library to view, annotate and edit documents. It is compatible with any JavaScript framework, including SharePoint, by using a DOM element to place the document viewing component. WebViewer has a pre-built SharePoint sample specifically designed for integrating into a SharePoint project.

<a href="https://showcase.apryse.com" class="button primary">Launch Online Demo</a>

Apryse's fully supported WebViewer is trusted by thousands of innovative startups, governments, and Fortune 500 businesses (see [customers](https://apryse.com/customers)). Apryse technology is built from the ground up and is not dependent on any external third-party open-source software.

### Works with

* Desktop Browsers
* Mobile Browsers
* Windows Desktop Applications

### Customizable

* [UI source code is included (built in React)](/web/ui-customization/ui-customization)
* [Create custom annotations](/web/annotation/customize/custom-annotations)
* [Change annotation appearance](/web/annotation/customizing-default-annotation-styles)
* [Customize annotation behaviour](/web/annotation/customizing-tools)
* [Internationalization through JSON language files](/web/ui-customization/localization/languages-and-internationalization)
* [Rich set of APIs for more advanced customization](https://sdk.apryse.com/api/web/index.html)

### File type support - web

* PDF, PDF/A
* DOCX, XLSX, PPTX (MS Office software or licenses are **not** required)
* JPG, PNG
* MP4, WEBM, OGG (via [WebViewer Video build](/web/video/video))

### Browser support

| **Browser**       | **Supported versions** |
| ----------------- | ---------------------- |
| Chrome            | All ✓                  |
| Firefox           | All ✓                  |
| Safari            | All ✓                  |
| Edge              | All ✓                  |
| Internet Explorer | 10+                    |
| Android Chrome    | 59+                    |
| Mobile Safari     | 11+                    |

## Free trial

* There are no feature limitations during the trial
* [Sign-up](https://dev.apryse.com) to get your trial key
* Chat with us on [Discord](https://apryse.com/discord?docLink=freDis)


# Vue.js PDF library

WebViewer is a pure client-side JavaScript library to view, annotate and edit documents. It is compatible with any JavaScript framework, including Vue...

WebViewer is a pure client-side JavaScript library to view, annotate and edit documents. It is compatible with any JavaScript framework, including SharePoint, by using a DOM element to place the document viewing component. WebViewer has a pre-built SharePoint sample specifically designed for integrating into a SharePoint project.

<a href="https://showcase.apryse.com" class="button primary">Launch Online Demo</a>

Apryse's fully supported WebViewer is trusted by thousands of innovative startups, governments, and Fortune 500 businesses (see [customers](https://apryse.com/customers)). Apryse technology is built from the ground up and is not dependent on any external third-party open-source software.

<a href="/web/get-started/libraries-and-frameworks/vue" class="button primary">Get started</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="https://github.com/ApryseSDK/webviewer-vue-sample/" class="button primary">Github sample</a>

{% embed url="<https://www.youtube.com/embed/fCHkJ5_PLS0>" %}

## Customizable

* [UI source code is included (built in React)](/web/ui-customization/ui-customization)
* [Create custom annotations](/web/annotation/customize/custom-annotations)
* [Change annotation appearance](/web/annotation/customizing-default-annotation-styles)
* [Customize annotation behaviour](/web/annotation/customizing-tools)
* [Internationalization through JSON language files](/web/ui-customization/localization/languages-and-internationalization)
* [Rich set of APIs for more advanced customization](https://sdk.apryse.com/api/web/index.html)

## File Type Support

* All versions of PDF and PDF/A
* DOC, DOCX, XLS, XLSX, PPT, PPTX (MS Office software or licenses **not** required)
* JPG, PNG, JFIF, TIF, TIFF
* MP4, WEBM, OGG (via [WebViewer Video build ](/web/video/video))
* MP3, WAV, OGG, FLAC (via [WebViewer Video build ](/web/video/video))
* And [many more](/web/what-is-webviewer/file-format-support)

[Additional formats](/web/what-is-webviewer/file-format-support) available with the optional [WebViewer Server](/web/webviewer-server/wv-server-deployment) and [Custom Server](/web/custom-server/custom-server-deployment) setups.

## Browser Support

WebViewer supports the **last 3 major versions** of each browser, in line with industry standard. This keeps compatibility predictable without requiring ongoing maintenance of this page.

| Browser        | Client only | WebViewer Server | Custom server |
| -------------- | ----------- | ---------------- | ------------- |
| Chrome         | ✓           | ✓                | ✓             |
| Firefox        | ✓           | ✓                | ✓             |
| Safari         | ✓           | ✓                | ✓             |
| Edge           | ✓           | ✓                | ✓             |
| Android Chrome | ✓           | ✓                | ✓             |

{% hint style="info" %}
**What "last 3 major versions" means:** As browsers release new versions, support automatically shifts forward. This matches the policy used by most major platforms and frameworks — no manual updates to this page are required.
{% endhint %}

{% hint style="warning" %}
Changed in WebViewer version 12.0: We previously listed specific minimum version numbers per browser. That table has been replaced with the last-3-versions policy above. If you were relying on an older browser version, please review your compatibility requirements.
{% endhint %}

## Free Trial

* There are no feature limitations during the trial
* [Sign-up](https://dev.apryse.com) to get your trial key
* Chat with us on [Discord](https://apryse.com/discord?docLink=freDis)


# APIs for JavaScript PDF Viewer

Enhance document viewing with APIs for JavaScript PDF Viewer. Annotate, view, and edit PDFs seamlessly in all major browsers using this pure JavaScript library. The Apryse Web SDK streamlines secure,

[WebViewer](https://sdk.apryse.com/api/web/index.html) WebViewer is a pure JavaScript library to annotate, view, and edit documents inside all major browsers.

[DocumentViewer](https://sdk.apryse.com/api/web/Core.DocumentViewer.html) A namespace containing document viewer methods.


# Creating PDF free text annotations using JavaScript

Create free text annotations in PDFs using JavaScript with this example. Learn how to add annotations easily using web. The Apryse Web SDK streamlines secure, serverless document processing with robus

Here is an example of creating a free text annotation using JavaScript, but creating other types of annotations are similar.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { Annotations, annotationManager, documentViewer } = instance.Core;

    documentViewer.addEventListener('documentLoaded', () => {
      const freeText = new Annotations.FreeTextAnnotation();
      freeText.PageNumber = 1;
      freeText.X = 150;
      freeText.Y = 200;
      freeText.Width = 150;
      freeText.Height = 50;
      freeText.setPadding(new Annotations.Rect(0, 0, 0, 0));
      freeText.setContents('My Text');
      freeText.FillColor = new Annotations.Color(0, 255, 255);
      freeText.FontSize = '16pt';

      annotationManager.addAnnotation(freeText, { autoFocus: false });
      annotationManager.redrawAnnotation(freeText);
    })
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [Annotations.FreeTextAnnotation](https://sdk.apryse.com/api/web/Core.Annotations.FreeTextAnnotation.html) [AnnotationManager.addAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#addAnnotation__anchor) [AnnotationManager.redrawAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#redrawAnnotation__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { Annotations, annotManager, docViewer } = instance;

    docViewer.on('documentLoaded', () => {
      const freeText = new Annotations.FreeTextAnnotation();
      freeText.PageNumber = 1;
      freeText.X = 150;
      freeText.Y = 200;
      freeText.Width = 150;
      freeText.Height = 50;
      freeText.setPadding(new Annotations.Rect(0, 0, 0, 0));
      freeText.setContents('My Text');
      freeText.FillColor = new Annotations.Color(0, 255, 255);
      freeText.FontSize = '16pt';

      annotManager.addAnnotation(freeText, { autoFocus: false });
      annotManager.redrawAnnotation(freeText);
    })
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [Annotations.FreeTextAnnotation](https://sdk.apryse.com/api/web/Core.Annotations.FreeTextAnnotation.html) [AnnotationManager.addAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#addAnnotation__anchor) [AnnotationManager.redrawAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#redrawAnnotation__anchor)
{% endtab %}
{% endtabs %}


# JavaScript PDF Viewer Library

Discover the JavaScript PDF document viewer with advanced features like color separation, diffing, and streaming. Optimize your viewing experience offline and learn about transforming coordinates effi

The JavaScript PDF document viewer is a viewing and rasterization scroll view component that handles a number of viewing operations including rendering, caching, high level zoom rendering and layout modes.

The viewer is a viewing and rasterization scroll view component that handles a number of viewing operations including rendering, caching, high level zoom rendering and layout modes.

Viewing represents reading a document and displaying it as the author intended. Through the process of rasterization, it can take document data and convert it into pixels that come together to be rendered on a display.

Apryse SDK offers the following benefits:

* Interactive PDF Viewing or Rasterization
* Full transparency support (Transparency Groups, Soft Masks, including all supported Blend Modes)
* Core viewing operations (scrolling, panning, zooming, jumping to pages)
* Standard & custom page viewing modes (e.g. single, facing, cover, scrolling, fit-page, fit-width, etc.)
* Extensive support for PDF forms & digital signatures
* Extensive annotation & real-time collaboration support
* Regex text search across a page or an entire document
* Manipulate pages (rotate, crop, move, copy/export, delete, merge documents)
* View PDF separations
* Easy navigation (with thumbnails, bookmarks, links, and history)
* Selective viewing and hiding of contents in different layers (OCGs)
* Security settings (custom encryption & DRM controls, dynamic watermarks)
* PDF/A viewing mode
* Support for R2L (Right to Left) languages (e.g. Arabic, Hebrew, etc)
* Keyboard shortcuts

More features:

* PDF Image Export (JPEG, TIFF, PNG, and BMP)
* Advanced image quality and compression settings controlling the rasterization process, allowing the user to choose between the output image quality and rendering speed
* Reflow
* Night, sepia and custom-color mode
* ICC color management
* Overprint control & preview
* Display-list caching (speeding-up viewing of complex documents)
* Direct & serverless rendering support for PDF, Word, PowerPoint, Excel, XPS, plain-text, mark-down, TIFF, and other image formats
* PDF package support
* Pressure-sensitive stylus support
* Infinite Undo/Redo
* Text to Speech Support
* Font-substitution control

## Get started

[Offline](/web/advanced/offline-loading) To load WebViewer for offline usage.

[Color separation](/web/extraction/color-separation) To separate colors such that they live in their own color process.

[Diffing](/web/compare-files/diffing) To input two documents and output a visual difference between them.

[Understanding coordinates](/web/edit-page-content/coordinates) An introduction to transforming between coordinate spaces.

[Preload library/worker files](/web/best-practices/preload-worker-files) To preload library/worker files for faster viewing.

[Streaming XOD documents](/web/best-practices/streaming-option) How the "streaming" constructor option controls XOD document loading.

[Streaming PDF documents](/web/best-practices/usedownloader-option) To stream linearized PDF files for faster viewing response.


# Learn more about WebViewer

Discover the power of WebViewer for seamless document processing on web. View, annotate, and collaborate effortlessly with this versatile library. Embed it in your web app for a seamless user experien

WebViewer can help you solve your document processing needs on the web.

It is the ultimate library for document viewing, annotation, or collaboration. Working with all major web platforms, the viewer can be directly embedded and customized within any web application. The content can be instantly accessed from any browser or device.

## Get started

[Inside WebViewer](/web/what-is-webviewer/wv-inside) An introduction to the inner workings of the WebViewer app.

[Framework compatibility](/web/get-started/libraries-and-frameworks/frameworks) An introduction to using WebViewer with other web frameworks.

[Deployment options](/web/what-is-webviewer/deployment-options) To deploy pure client-side, WebViewer Server, or a custom server.

[Best practices](/web/best-practices/best-practices) To create the best results for most use cases.

[WebViewer events](/web/events/viewer-events) To go over the order of events when WebViewer is instantiated.

[Viewer optimized PDF](/web/best-practices/viewer-optimized-pdf) An introduction to using viewer optimized PDF files in WebViewer.


# Create PDF document using JavaScript

Generate PDF documents effortlessly with JavaScript by following these steps to initialize the PDF worker based on your browser's backend type. Start creating PDFs now! The Apryse Web SDK streamlines

Creating a PDF document is similar to creating a XOD document except it requires a few more steps: we need to [initialize the PDF worker](https://sdk.apryse.com/api/web/Core.html#.initPDFWorkerTransports__anchor) based on the [backend type](https://sdk.apryse.com/api/web/Core.html#.getDefaultBackendType__anchor) for the browser we are using.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">// Instantiate a Document object.
const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>';
const doc = await Core.createDocument('/path/file.pdf', { l: licenseKey });

// doc is Document instance and ready use
</code></pre>

[Core.createDocument](https://sdk.apryse.com/api/web/Core.html#.createDocument__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">// Instantiate a Document object.
const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>';
const doc = await CoreControls.createDocument('/path/file.pdf', { l: licenseKey });

// doc is Document instance and ready use
</code></pre>

[CoreControls.createDocument](https://sdk.apryse.com/api/web/Core.html#.createDocument__anchor)
{% endtab %}
{% endtabs %}


# Features

{% content-ref url="/pages/UPcnAFUS1DKShTOMVpEv" %}
[PDF Generation Library using JavaScript](/web/get-started/guides/features/create)
{% endcontent-ref %}

{% content-ref url="/pages/RtGI3hLk9gZybDr6FsNB" %}
[JavaScript PDF Annotation Library](/web/get-started/guides/features/annotation)
{% endcontent-ref %}

{% content-ref url="/pages/fylQKbefP7XwlhZhXXSG" %}
[Forms](/web/get-started/guides/features/forms)
{% endcontent-ref %}

{% content-ref url="/pages/HlX7F7TS0pCZHx6Scqj1" %}
[Search](/web/get-started/guides/features/search)
{% endcontent-ref %}

{% content-ref url="/pages/lXYGnNOwKakVakC41oSp" %}
[Portfolio](/web/get-started/guides/features/portfolio)
{% endcontent-ref %}

{% content-ref url="/pages/K9F49E8i9sHscyu2dPib" %}
[Manipulation](/web/get-started/guides/features/manipulation)
{% endcontent-ref %}

{% content-ref url="/pages/iEJpvviohTvfC3QucWYI" %}
[Security](/web/get-started/guides/features/security)
{% endcontent-ref %}

{% content-ref url="/pages/TosQnwWdaTq0OnXLL7am" %}
[Extraction](/web/get-started/guides/features/extraction)
{% endcontent-ref %}

{% content-ref url="/pages/1mCosx9AnDftSK0pC0Ia" %}
[Basics](/web/get-started/guides/features/basics)
{% endcontent-ref %}

{% content-ref url="/pages/UlUiOn9iGVzdyEn10Mei" %}
[Redaction](/web/get-started/guides/features/redaction)
{% endcontent-ref %}

{% content-ref url="/pages/Mei0yZiVuRxtRyeFyHz1" %}
[Bookmark](/web/get-started/guides/features/bookmark)
{% endcontent-ref %}


# PDF Generation Library using JavaScript

Create new documents easily with templates or blank PDFs. Learn how to generate from a template or start fresh with a blank document. Perfect for all your document needs. The Apryse Web SDK streamline

Document creation can be as simple as a blank PDF or it could be part of capturing content and combining it with a template PDF in order to streamline a report process.

Apryse SDK benefits include:

* Create new documents from scratch or use existing documents as templates
* Copy page content (individual graphical elements) from one page to another
* Read/write a PDF document from/to memory buffer or a file on a disk
* Create new text, vector art and images
* Quick image embedding for TIFF, JPEG, and PNG
* Support for Type1, OpenType, TrueType, Type3, and CID Font embedding for accurate text reproduction
* Support for Unicode text and standard PDF encodings
* Dynamic font subsetting resulting in smaller files
* Full control over text positioning and character spacing
* Support for all PDF color spaces and advanced pre-press options
* Full control over content positioning and all graphical attributes available in PDF
* Support for PDF patterns (colored and uncolored tiling patterns) and shadings (7 types)
* Support for image masks
* Reuse of resources such as images, fonts and color spaces through object sharing resulting in smaller and more efficient documents
* Intelligent element serialization resulting in small and efficient content streams
* Support for explicit and named destinations. Named destinations allow for changes to the document that will not invalidate existing bookmarks
* Extensive bookmark creation and editing API
* Creation of PDF documents with 3D content (U3D support)
* Apply security to new documents
* Linearization (Fast Web View)

## Get started

[Generate from Template](/web/generate/template-based-generation/generate-via-template) To create a new document from a Template.

[Create a new blank PDF document](/web/generate/blank) To create a new document as a blank PDF.


# Generate a PDF page thumbnail using JavaScript

Learn how to create a PDF page thumbnail in various image formats like JPG, PNG, BMP, and TIFF. Convert PDF to images with code samples for PNG and JPEG. Explore more image format options like RAW, BM

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="https://apryse.com/capabilities#PageManipulation" class="button primary">Package: Page Manipulation</a><a href="https://showcase.apryse.com/create-thumbnail" class="button primary">Live demo</a>
{% endhint %}

To create a PDF page thumbnail in multiple image formats.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // set the output resolution
  const pdfdraw = await PDFNet.PDFDraw.create(92);

  // Rasterize the first page in the document and save the result as PNG.
  const pg = await doc.getPage(1);
  const buf = await pdfdraw.exportStream(pg, 'PNG');

  //optionally save the blob to a file or upload to a server
  const blob = new Blob([buf], { type: 'application/pdf' });
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Convert PDF to image (JPG, PNG, BMP, TIFF)](/web/get-started/samples/pdfdrawtest) Full code sample illustrates how to use the built-in rasterizer in order to render PDF images on the fly and how to save resulting images in PNG and JPEG format.

More image format options:

* **"RAW"** : RAW format.There are four possibilities:
  * `e_rgba` - if transparent and color page;
  * `e_gray_alpha` - if transparent and gray page;
  * `e_rgb` - if opaque and color page;
  * `e_gray` - if opaque and gray page. NOTE that if page is set to be transparent (SetPageTransparent), the output color channels are already multiplied by the alpha channel.
* **"BMP"** : Bitmap image format (BMP)
* **"JPEG"** : Joint Photographic Experts Group (JPEG) image format
* **"PNG"** : 24-bit W3C Portable Network Graphics (PNG) image format
* **"PNG8"** : 8-bit, palettized PNG format. The exported file size should be smaller than the one generated using "PNG", possibly at the expense of some image quality.
* **"TIFF"** : Tag Image File Format (TIFF) image format.
* **"TIFF8"** : Tag Image File Format (TIFF) image format (with 8-bit palete).


# JavaScript PDF Annotation Library

Learn how to import and export annotations, set style properties, add sticky notes, links, and stamps, and remove annotations from PDF documents. The Apryse Web SDK streamlines secure, serverless docu

Annotations allow users to mark up and comment on PDFs without altering the original authors content.

Apryse SDK Benefits include:

* Infinite Undo/Redo
* Support for annotation flattening
* Support for annotation appearances
* XFDF Import
* Export annotations to FDF, XFDF, text, or PDF annotation summary
* Sticky (Note)
* Comments
* Text markup (highlight, squiggly, underline, strikeout)
* Add shapes (line, arrow, polyline, rectangle, ellipse, polygon, cloud)
* Cloud annotations
* Apply freehand ink annotations
* Signatures
* Save the annotations into the PDF file
* File attachments
* Sound, movie, and 3D annotations
* Support for custom annotations
* FreeText, Callout
* TextHighlight, TextUnderline, TextSquiggly, TextStrikeout
* Stamp
* Redaction

## Get started

[Import annotations](/web/get-started/guides/features/annotation/import) To import XFDF into FDF, then merge data from FDF into PDF.

[Export annotations](/web/get-started/guides/features/annotation/export) To extract data from PDF to FDF, then export FDF as XFDF.

[Style properties](/web/get-started/guides/features/annotation/style) To set an annotation style property such as color, interior color, border style, padding, etc to an annotation.

[Add sticky note](/web/get-started/guides/features/annotation/text-annotation) To add sticky note (text annotation) to a PDF Document.

[Add link annotation](/web/get-started/guides/features/annotation/link-annotation) To add a hyperlink or intra-document link annotation to a PDF Document page.

[Add stamp annotation](broken://pages/htpFml1WvhzALbIby9pN) To add a stamp annotation with an image, text, or page to a PDF Document page.

[Remove annotation](/web/get-started/guides/features/annotation/remove) To remove an annotation from a document.


# Import annotations using JavaScript

Learn how to efficiently manage XFDF and FDF files with PDFNet APIs. Explore methods for replacing, adding, and merging annotations in PDF documents. Choose the best API for your specific use case. Th

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

Depending on the type and origin of the XFDF or FDF, one of the following methods should be used:

1. Replace all annotations in the PDF document with the annotations from XFDF or FDF using [FDFUpdate](https://docs.apryse.com) API.
2. Add all annotations from XFDF or FDF to the PDF document using [FDFMerge](https://docs.apryse.com) API (potentially introducing duplicates).
3. Merge XFDF directly into the PDF document using [MergeXFDF](https://docs.apryse.com) API (introduced in PDFNet 9.2)

* In order for the API to function, annotations in XFDF should be named ([see below](https://docs.apryse.com))
* `MergeXFDF` API is the recommended solution for command XFDF files

See below, which API is best suited to the given [use case](https://docs.apryse.com).

### Replacing all annotations in the document

This is a 2-step process: first XFDF needs to be converted into FDF, then FDF needs to be passed to `FDFUpdate` API. As a result of the import operation, the PDF document will have all annotations from XFDF and no other:

1. Annotations that are in XFDF only will be added
2. Annotations that are both in XFDF and PDF will be modified as needed
3. Annotations that are **only** in PDF and not in XFDF will be removed

`FDFUpdate` API works best for the scenarios like restoring annotations from a backup. Also it would be a good option when bringing in annotations creating in WebViewer, i.e. use `FDFUpdate` to import XFDF written by WebViewer.

{% hint style="warning" %}
Note, *FDFUpdate* should be used **only** for the full XFDF files exported by Apryse API (Apryse SDK or WebViewer) from the same document. This method is not applicable to partial (incomplete) XFDF, because all the annotations that are not included in XFDF will be **removed** from the document. If XFDF was created by another tool, all widgets will be removed.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
// Import annotations from XFDF to FDF
const fdf_doc = await PDFNet.FDFDoc.createFromXFDF(xfdf_filename);

// Optionally read XFDF from a string
const fdf_doc = await PDFNet.FDFDoc.createFromXFDF(xfdf_string);

// Merge FDF data into PDF doc
const doc = await PDFNet.PDFDoc.createFromURL(filename);
await doc.fdfUpdate(fdf_doc);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[PDFNet.FDFDoc.createFromXFDF](https://sdk.apryse.com/api/web/Core.PDFNet.FDFDoc.html#.createFromXFDF__anchor) [PDFNet.PDFDoc.createFromURL](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.createFromURL__anchor) [PDFNet.PDFDoc.fdfUpdate](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#fdfUpdate__anchor)

### Adding all annotations from XFDF

This is a 2-step process: first XFDF needs to be converted into FDF, then FDF needs to be passed to `FDFMerge` API. As a result of the import operation, **all** the XFDF annotations will be added to the PDF document:

1. Annotations that are in XFDF only will be added
2. Annotations that are both in XFDF and PDF will also be added, i.e. duplicating those annotations
3. Annotations that are only in PDF and not in XFDF will not be modified

`FDFMerge` API works best for bringing new annotations into a document from a partial XFDF and for adding annotations to an empty PDF document.

{% hint style="warning" %}
Note, *FDFMerge* does not modify the existing annotations. When an annotation is present in both XFDF and PDF, it will be duplicated.
{% endhint %}

It also works well for importing field-only XFDF (e.g. field data exported from other PDF solutions).

{% hint style="info" %}
**Duplicate prevention**

To prevent duplicates, remove all the annotations expected to be present in the PDF document from XFDF before using *FDFMerge* API.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
// Import annotations from XFDF to FDF
async function main() {
  const fdf_doc = await PDFNet.FDFDoc.createFromXFDF(xfdf_filename);
  // Optionally read XFDF from a string
  const fdf_doc = await PDFNet.FDFDoc.createFromXFDF(xfdf_string);
  // Merge FDF data into PDF doc
  const doc = await PDFNet.PDFDoc.createFromURL(filename);
  await doc.fdfMerge(fdf_doc);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[PDFNet.FDFDoc.createFromXFDF](https://sdk.apryse.com/api/web/Core.PDFNet.FDFDoc.html#.createFromXFDF__anchor) [PDFNet.PDFDoc.createFromURL](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.createFromURL__anchor) [PDFNet.PDFDoc.fdfMerge](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#fdfMerge__anchor)

### Merging XFDF directly into the PDF

This is a 1-step process: XFDF is passed directly to `MergeXFDF` API. The API can both add and modify the annotations, but not delete them (except when using a command file). :

1. Annotations that are in XFDF only will be added
2. Annotations that are both in XFDF and PDF will be modified (see below)
3. Annotations that are only in PDF and not in XFDF will not be modified

{% hint style="warning" %}
Note, *MergeXFDF* API relies on annotation names for matching the annotations. If XFDF file contains unnamed annotation, merge operation will be aborted with an exception. In order to proceed, set *Force* option, some annotations may be duplicated.
{% endhint %}

{% hint style="info" %}
**Name generation**

XFDF files exported using PDFNet API version 9.0 and higher always contain annotation names, even if the annotations in the PDF document don't have them. When imported, those annotations will still be matched correctly.
{% endhint %}

`MergeXFDF` is also the API that should be used with command files (a flavor of XFDF with add-delete-modify instructions, see `GetAnnotCommand`() or [`AnnotationManager.exportAnnotCommand`](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#exportAnnotCommand__anchor) in WebViewer). In case of command XFDF, all operations specified by the command file, including deletions will be performed on the document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
// Merge FDF data into PDF doc
const doc = await PDFNet.PDFDoc.createFromURL(filename);
await doc.mergeXFDF(xfdf_filename);
// Optionally read XFDF from a string
await doc.mergeXFDF(xfdf_string);
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Common use cases

| Use case                                                                                                                                                                                                                                                 | API                       |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| Restoring annotations from backup                                                                                                                                                                                                                        | `FDFUpdate`               |
| Importing all changes made to annotations in WebViewer                                                                                                                                                                                                   | `FDFUpdate`               |
| Importing form data exported from other PDF solutions                                                                                                                                                                                                    | `FDFMerge` or `MergeXFDF` |
| Introducing several new annotations to a document while keeping the existing document's annotations                                                                                                                                                      | `FDFMerge` or `MergeXFDF` |
| Importing partial XFDF that contains modifications to annotations that already exist in the document (assuming XFDF annotations are named)                                                                                                               | `MergeXFDF`               |
| Importing [command xfdf file](https://www.apryse.com/blog/annotation/how-to-export-xfdf-annotations/#how-to-export-xfdf-from-pdf-with-javascript), which allows fast application of annotation changes made on another WebViewer or Apryse SDK instance. | `MergeXFDF`               |

[PDF Form fill and form data extraction](/web/get-started/samples#fdf) Full sample code which illustrates basic FDF merge/extract functionality and full support for FDF (Forms Data Format)


# Remove annotation from document using JavaScript

Learn how to remove an annotation from a document with our step-by-step guide. Easily manage your files and annotations for a seamless experience. The Apryse Web SDK streamlines secure, serverless doc

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To remove an annotation from a document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);
  const page = await doc.getPage(1);

  // remove by index
  await page.annotRemove(0);

  // remove by annotation
  const annotation = await page.getAnnot(0);
  await page.annotRemove(annotation);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Add text annotation to a document using JavaScript

Learn how to add a sticky note (text annotation) to a PDF document and edit PDF annotations with this full code sample. Add hyperlinks, stamps, text, and more! The Apryse Web SDK streamlines secure, s

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To add a sticky note (text annotation) to a PDF Document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
    const doc = await PDFNet.PDFDoc.createFromURL(filename);
    const page = await doc.getPage(1);

    // Create the sticky note (Text annotation)
    const txt = await PDFNet.TextAnnot.create(doc, new PDFNet.Rect(10, 20, 30, 40));
    await txt.setIcon("UserIcon");
    await txt.setContents("The quick brown fox ate the lazy mouse.");
    await txt.setColor(await PDFNet.ColorPt.init(0, 1, 0));
    await txt.refreshAppearance();
    await page.annotPushBack(txt);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Add or edit PDF annotations](/web/get-started/samples#annotation) Full code sample which shows how to add or edit PDF annotations (e.g. hyperlink, intra-document link, stamp, rubber stamp, file attachment, sound, text, free-text, line, circle, square, polygon, polyline, highlight, squiggly, caret, and ink).


# Add link annotation to a document using JavaScript

Learn how to add hyperlinks or annotations to PDF documents with this full code sample. Enhance your PDFs with various annotations like hyperlinks, stamps, and more. The Apryse Web SDK streamlines sec

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To add a hyperlink or intra-document link annotation to a PDF Document page.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);
  const firstPage = await doc.getPage(1);

  // Create a hyperlink
  const createURIAction = await PDFNet.Action.createURI(doc, 'http://www.apryse.com');
  const linkRect = new PDFNet.Rect(85, 570, 503, 524);
  const hyperlink = await PDFNet.LinkAnnot.create(doc, linkRect);
  await hyperlink.setAction(createURIAction);
  await hyperlink.refreshAppearance();
  await firstPage.annotPushBack(hyperlink);

  // Create an intra-document link
  const page3 = await doc.getPage(3);
  const gotoPage3 = await PDFNet.Action.createGoto(await PDFNet.Destination.createFitH(page3, 0));
  const link = await PDFNet.LinkAnnot.create(doc, (new PDFNet.Rect(85, 458, 503, 502)));
  await link.setAction(gotoPage3);
  await link.refreshAppearance();
  await firstPage.annotPushBack(link);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Add or edit PDF annotations sample](/web/get-started/samples#annotation) Full code sample which shows how to add or edit PDF annotations (e.g. hyperlink, intra-document link, stamp, rubber stamp, file attachment, sound, text, free-text, line, circle, square, polygon, polyline, highlight, squiggly, caret, and ink).


# Export annotations using JavaScript

Extract data from PDF to FDF, export FDF as XFDF, and fill PDF forms with ease using our sample code. Learn about FDF merge/extract functionality and full support for Forms Data Format (FDF). The Apry

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To extract data from PDF to FDF, then export FDF as XFDF

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // Extract annotations to FDF.
  // Optionally use e_both to extract both forms and annotations
  const doc_fields = await doc.fdfExtract(PDFNet.PDFDoc.ExtractFlag.e_annots_only); //PDFNet.PDFDoc.ExtractFlag.e_forms_only

  // Export annotations from FDF to XFDF.
  const xfdf_data = await doc_fields.saveAsXFDFAsString();

  //optionally save the blob to a file or upload to a server
  const blob = new Blob([xfdf_data], { type: 'application/xml' });
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[PDFNet.PDFDoc.createFromURL](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.createFromURL__anchor) [PDFNet.PDFDoc.fdfExtract](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#fdfExtract__anchor) [PDFNet.FDFDoc.saveAsXFDFAsString](https://sdk.apryse.com/api/web/Core.PDFNet.FDFDoc.html#saveAsXFDFAsString__anchor)

[PDF Form Fill and Form Data Extraction](/web/get-started/samples#fdf) Full sample code which illustrates basic FDF merge/extract functionality and full support for FDF (Forms Data Format)


# Edit annotation style properties using JavaScript

Learn how to add or edit PDF annotations with this comprehensive code sample. Set annotation style properties like color, border style, and more. Perfect for enhancing your PDF documents. The Apryse W

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To set an annotation style property such as color, interior color, border style, padding, etc to an annotation.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);
  const page = await doc.getPage(1);

  const circle = await PDFNet.CircleAnnot.create(
    doc,
    new PDFNet.Rect(100, 100, 200, 200)
  );
  await circle.setColor(await PDFNet.ColorPt.init(0, 1, 0), 3);
  await circle.setInteriorColor(await PDFNet.ColorPt.init(0, 0, 1), 3);
  const dash = [2, 4];
  await circle.setBorderStyle(
    await PDFNet.AnnotBorderStyle.createWithDashPattern(
      PDFNet.AnnotBorderStyle.Style.e_dashed,
      3,
      0,
      0,
      dash
    )
  );
  await circle.setPadding(new PDFNet.Rect(2, 2, 2, 2));
  await circle.refreshAppearance();
  await page.annotPushBack(circle);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Add or edit PDF annotations sample](/web/get-started/samples#annotation) Full code sample which shows how to add or edit PDF annotations (e.g. hyperlink, intra-document link, stamp, rubber stamp, file attachment, sound, text, free-text, line, circle, square, polygon, polyline, highlight, squiggly, caret, and ink).


# Forms

{% content-ref url="/pages/04kEy37iTzuVSzr8nhTt" %}
[Filling PDF form fields using JavaScript](/web/get-started/guides/features/forms/fill-fields)
{% endcontent-ref %}

{% content-ref url="/pages/785cmDoCsbW7b19dkIwz" %}
[Extract FDF data from a PDF using JavaScript](/web/get-started/guides/features/forms/export-data)
{% endcontent-ref %}

{% content-ref url="/pages/EJ8ioa7QpVhuGLGUBASN" %}
[Merge Form Data File (FDF or XFDF) to PDF Forms using JavaScript](/web/get-started/guides/features/forms/import-data)
{% endcontent-ref %}


# Filling PDF form fields using JavaScript

We show how to fill PDF form fields using JavaScript. Guide includes sample code.

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To fill and set values for existing form fields.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // Search for a specific field
  const field = await doc.getField("employee.name.first");
  await field.setValueAsString("John");
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[PDF Interactive Forms (AcroForms)](/web/get-started/samples#interactiveforms) Full code sample which illustrates some basic PDFNet capabilities related to interactive forms (also known as AcroForms).

## About filling form fields

Form Fields can be populated using the `Field.SetValue()` method:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
field.setValueAsString("New Value");

// Regenerate appearance stream.
field.refreshAppearance();
```

{% endcode %}
{% endtab %}
{% endtabs %}

Note that, after modifying the Field's value, we refreshed its appearance stream. In the PDF format, Field's value and appearance are two different entities. Therefore, if you don't call `RefreshAppearance()`, the initial value on a PDF page will remain unchanged — it may have retain the old value or it may be blank.

One approach used by other PDF libraries is to let the PDF viewer automatically pre-generate appearance streams by setting the 'NeedAppearances' flag in AcroForm dictionary:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
doc.getAcroForm().putBool("NeedAppearances", true);
```

{% endcode %}
{% endtab %}
{% endtabs %}

This will force viewer applications to auto-generate appearance streams every time the document is opened. This method is unreliable — Acrobat does not always generate appearance streams correctly. Another disadvantage of this approach is that the user will always be prompted to save the document even if the document was never modified.

`Field.GetValueAsString()` returns the field's value as a string. The value returned varies based on the field type. A text field type varies depending on the field type. A text field will return a string:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
if (type === PDFNet.Field.Type.e_text && field.getValue()) {
  console.log("Field value: %s", field.getValueAsString());
} else {
  console.log("Field is blank");
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Other field types, such as check boxes and radio buttons, can also return text from `GetValueAsString()`. Similarly, the `Field.GetValueAsString()` method is available.

## Access interactive fields

The form shown in the following figure consists of a number of Fields:

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-d05d655d676590f4499e02d95420b4bfe8c76f2b%2Fd34fbd5ce2b4792f76058f58f4e5d391153db51e-450x434.gif?alt=media)

Every field has its name and value, as well as its annotation appearance.

In the Apryse SDK, Fields are accessed through `FieldIterators`.

For example, the list of all Fields present in the document can be traversed using the following code snippet:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
const itr = await doc.getFieldIteratorBegin();
for (; await itr.hasNext(); itr.next()) {
  const field = await itr.current();
  println("Field name: %s", field.GetName());
}
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Extract FDF data from a PDF using JavaScript

Learn how to extract data from PDF to FDF, export FDF as XFDF, and fill PDF forms with ease. Get full sample code for FDF merge/extract functionality and comprehensive support for Forms Data Format (F

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To extract data from PDF to FDF, then export FDF as XFDF

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // Extract annotations to FDF.
  // Optionally use e_both to extract both forms and annotations
  const doc_fields = await doc.fdfExtract(PDFNet.PDFDoc.ExtractFlag.e_annots_only); //PDFNet.PDFDoc.ExtractFlag.e_forms_only

  // Export annotations from FDF to XFDF.
  const xfdf_data = await doc_fields.saveAsXFDFAsString();

  //optionally save the blob to a file or upload to a server
  const blob = new Blob([xfdf_data], { type: 'application/xml' });
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[PDFNet.PDFDoc.createFromURL](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.createFromURL__anchor) [PDFNet.PDFDoc.fdfExtract](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#fdfExtract__anchor) [PDFNet.FDFDoc.saveAsXFDFAsString](https://sdk.apryse.com/api/web/Core.PDFNet.FDFDoc.html#saveAsXFDFAsString__anchor)

[PDF Form Fill and Form Data Extraction](/web/get-started/samples#fdf) Full sample code which illustrates basic FDF merge/extract functionality and full support for FDF (Forms Data Format)


# Merge Form Data File (FDF or XFDF) to PDF Forms using JavaScript

Learn how to efficiently import XFDF or FDF annotations into PDF documents using FDFUpdate, FDFMerge, and MergeXFDF APIs. Choose the best method for your use case and optimize your workflow. The Aprys

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

Depending on the type and origin of the XFDF or FDF, one of the following methods should be used:

1. Replace all annotations in the PDF document with the annotations from XFDF or FDF using [FDFUpdate](https://docs.apryse.com) API.
2. Add all annotations from XFDF or FDF to the PDF document using [FDFMerge](https://docs.apryse.com) API (potentially introducing duplicates).
3. Merge XFDF directly into the PDF document using [MergeXFDF](https://docs.apryse.com) API (introduced in PDFNet 9.2)

* In order for the API to function, annotations in XFDF should be named ([see below](https://docs.apryse.com))
* `MergeXFDF` API is the recommended solution for command XFDF files

See below, which API is best suited to the given [use case](https://docs.apryse.com).

### Replacing all annotations in the document

This is a 2-step process: first XFDF needs to be converted into FDF, then FDF needs to be passed to `FDFUpdate` API. As a result of the import operation, the PDF document will have all annotations from XFDF and no other:

1. Annotations that are in XFDF only will be added
2. Annotations that are both in XFDF and PDF will be modified as needed
3. Annotations that are **only** in PDF and not in XFDF will be removed

`FDFUpdate` API works best for the scenarios like restoring annotations from a backup. Also it would be a good option when bringing in annotations creating in WebViewer, i.e. use `FDFUpdate` to import XFDF written by WebViewer.

{% hint style="warning" %}
Note, *FDFUpdate* should be used **only** for the full XFDF files exported by Apryse API (Apryse SDK or WebViewer) from the same document. This method is not applicable to partial (incomplete) XFDF, because all the annotations that are not included in XFDF will be **removed** from the document. If XFDF was created by another tool, all widgets will be removed.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
// Import annotations from XFDF to FDF
const fdf_doc = await PDFNet.FDFDoc.createFromXFDF(xfdf_filename);

// Optionally read XFDF from a string
const fdf_doc = await PDFNet.FDFDoc.createFromXFDF(xfdf_string);

// Merge FDF data into PDF doc
const doc = await PDFNet.PDFDoc.createFromURL(filename);
await doc.fdfUpdate(fdf_doc);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[PDFNet.FDFDoc.createFromXFDF](https://sdk.apryse.com/api/web/Core.PDFNet.FDFDoc.html#.createFromXFDF__anchor) [PDFNet.PDFDoc.createFromURL](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.createFromURL__anchor) [PDFNet.PDFDoc.fdfUpdate](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#fdfUpdate__anchor)

### Adding all annotations from XFDF

This is a 2-step process: first XFDF needs to be converted into FDF, then FDF needs to be passed to `FDFMerge` API. As a result of the import operation, **all** the XFDF annotations will be added to the PDF document:

1. Annotations that are in XFDF only will be added
2. Annotations that are both in XFDF and PDF will also be added, i.e. duplicating those annotations
3. Annotations that are only in PDF and not in XFDF will not be modified

`FDFMerge` API works best for bringing new annotations into a document from a partial XFDF and for adding annotations to an empty PDF document.

{% hint style="warning" %}
Note, *FDFMerge* does not modify the existing annotations. When an annotation is present in both XFDF and PDF, it will be duplicated.
{% endhint %}

It also works well for importing field-only XFDF (e.g. field data exported from other PDF solutions).

{% hint style="info" %}
**Duplicate prevention**

To prevent duplicates, remove all the annotations expected to be present in the PDF document from XFDF before using *FDFMerge* API.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
// Import annotations from XFDF to FDF
async function main() {
  const fdf_doc = await PDFNet.FDFDoc.createFromXFDF(xfdf_filename);
  // Optionally read XFDF from a string
  const fdf_doc = await PDFNet.FDFDoc.createFromXFDF(xfdf_string);
  // Merge FDF data into PDF doc
  const doc = await PDFNet.PDFDoc.createFromURL(filename);
  await doc.fdfMerge(fdf_doc);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[PDFNet.FDFDoc.createFromXFDF](https://sdk.apryse.com/api/web/Core.PDFNet.FDFDoc.html#.createFromXFDF__anchor) [PDFNet.PDFDoc.createFromURL](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.createFromURL__anchor) [PDFNet.PDFDoc.fdfMerge](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#fdfMerge__anchor)

### Merging XFDF directly into the PDF

This is a 1-step process: XFDF is passed directly to `MergeXFDF` API. The API can both add and modify the annotations, but not delete them (except when using a command file). :

1. Annotations that are in XFDF only will be added
2. Annotations that are both in XFDF and PDF will be modified (see below)
3. Annotations that are only in PDF and not in XFDF will not be modified

{% hint style="warning" %}
Note, *MergeXFDF* API relies on annotation names for matching the annotations. If XFDF file contains unnamed annotation, merge operation will be aborted with an exception. In order to proceed, set *Force* option, some annotations may be duplicated.
{% endhint %}

{% hint style="info" %}
**Name generation**

XFDF files exported using PDFNet API version 9.0 and higher always contain annotation names, even if the annotations in the PDF document don't have them. When imported, those annotations will still be matched correctly.
{% endhint %}

`MergeXFDF` is also the API that should be used with command files (a flavor of XFDF with add-delete-modify instructions, see `GetAnnotCommand`() or [`AnnotationManager.exportAnnotCommand`](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#exportAnnotCommand__anchor) in WebViewer). In case of command XFDF, all operations specified by the command file, including deletions will be performed on the document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
// Merge FDF data into PDF doc
const doc = await PDFNet.PDFDoc.createFromURL(filename);
await doc.mergeXFDF(xfdf_filename);
// Optionally read XFDF from a string
await doc.mergeXFDF(xfdf_string);
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Common use cases

| Use case                                                                                                                                                                                                                                                 | API                       |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| Restoring annotations from backup                                                                                                                                                                                                                        | `FDFUpdate`               |
| Importing all changes made to annotations in WebViewer                                                                                                                                                                                                   | `FDFUpdate`               |
| Importing form data exported from other PDF solutions                                                                                                                                                                                                    | `FDFMerge` or `MergeXFDF` |
| Introducing several new annotations to a document while keeping the existing document's annotations                                                                                                                                                      | `FDFMerge` or `MergeXFDF` |
| Importing partial XFDF that contains modifications to annotations that already exist in the document (assuming XFDF annotations are named)                                                                                                               | `MergeXFDF`               |
| Importing [command xfdf file](https://www.apryse.com/blog/annotation/how-to-export-xfdf-annotations/#how-to-export-xfdf-from-pdf-with-javascript), which allows fast application of annotation changes made on another WebViewer or Apryse SDK instance. | `MergeXFDF`               |

[PDF Form fill and form data extraction](/web/get-started/samples#fdf) Full sample code which illustrates basic FDF merge/extract functionality and full support for FDF (Forms Data Format)


# Search

{% content-ref url="/pages/SwXqNSO3Ypl73eHKshx8" %}
[Code samples for searching PDF text or images using JavaScript](/web/get-started/guides/features/search/samples)
{% endcontent-ref %}

{% content-ref url="/pages/BkswFzfgMrouiTTlWIFA" %}
[APIs for searching PDF documents using JavaScript](/web/get-started/guides/features/search/apis)
{% endcontent-ref %}

{% content-ref url="/pages/SDE8bDk5VHPcoksBI11r" %}
[Search for text in a PDF using JavaScript](/web/get-started/guides/features/search/text-search)
{% endcontent-ref %}


# Code samples for searching PDF text or images using JavaScript

Search PDF files for text using regular expressions with TextSearch. Replace text and images in existing PDFs with ContentReplacer. Learn how to enhance your PDF search capabilities. The Apryse Web SD

{% hint style="info" %}
[Full API](/web/what-is-webviewer/full-api) is mainly used for PDF processing situations that are not featured using the Standard API. The preferred recommendation is to use the Standard API to address your situation when possible. Standard API is easy to use, loaded by default, and performs best for most situations.
{% endhint %}

[Search PDF files for text](/web/get-started/samples#textsearch) This sample shows how to use TextSearch to search text on PDF pages using regular expressions.

[Search & Replace PDF text or images](/web/get-started/samples#contentreplacer) This sample which shows how to use pdftron.PDF.ContentReplacer to search and replace text strings and images in existing PDF (e.g. business cards and other PDF templates).


# APIs for searching PDF documents using JavaScript

Discover utility classes for searching PDF documents using Web (Javascript) PDFNet.TextSearch and PDFNet.ContentReplacer. Optimize your PDF search capabilities now! The Apryse Web SDK streamlines secu

The following are utility classes that can be used for search of PDF documents.

**Web (Javascript)**

* [PDFNet.TextSearch](https://sdk.apryse.com/api/web/Core.PDFNet.TextSearch.html)
* [PDFNet.ContentReplacer](https://sdk.apryse.com/api/web/Core.PDFNet.ContentReplacer.html)


# Search for text in a PDF using JavaScript

Learn how to search for text in a PDF using regular expressions and apply link annotations on highlighted results. Full code sample included! The Apryse Web SDK streamlines secure, serverless document

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To search for text in a PDF using regular expression and then apply a link annotation on the highlighted result.

{% hint style="info" %}
In this example, we add a link annotation but any other types of annotations can be applied here such as redaction annotations in the case of a search and redact workflow.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);
  const txtSearch = await PDFNet.TextSearch.create();
  let mode = PDFNet.TextSearch.Mode.e_whole_word + PDFNet.TextSearch.Mode.e_page_stop; // Uses both whole word and page stop
  let pattern = '';

  //use regular expression to find credit card number
  mode += PDFNet.TextSearch.Mode.e_reg_expression + PDFNet.TextSearch.Mode.e_highlight;
  txtSearch.setMode(mode);
  pattern = '\\d{4}-\\d{4}-\\d{4}-\\d{4}'; // or "(\\d{4}-){3}\\d{4}"
  txtSearch.setPattern(pattern);

  //call Begin() method to initialize the text search.
  txtSearch.begin(doc, pattern, mode);
  const result = await txtSearch.run();

  if (result.code === PDFNet.TextSearch.ResultCode.e_found) {
    // add a link annotation based on the location of the found instance
    hlts = result.highlights;
    await hlts.begin(doc);
    while (await hlts.hasNext()) {
      const curPage = await doc.getPage(await hlts.getCurrentPageNumber());
      const quadArr = await hlts.getCurrentQuads();
      for (let i = 0; i < quadArr.length; ++i) {
        const currQuad = quadArr[i];
        const x1 = Math.min(Math.min(Math.min(currQuad.p1x, currQuad.p2x), currQuad.p3x), currQuad.p4x);
        const x2 = Math.max(Math.max(Math.max(currQuad.p1x, currQuad.p2x), currQuad.p3x), currQuad.p4x);
        const y1 = Math.min(Math.min(Math.min(currQuad.p1y, currQuad.p2y), currQuad.p3y), currQuad.p4y);
        const y2 = Math.max(Math.max(Math.max(currQuad.p1y, currQuad.p2y), currQuad.p3y), currQuad.p4y);

        const hyperLink = await PDFNet.LinkAnnot.create(doc, await PDFNet.Rect.init(x1, y1, x2, y2));
        await hyperLink.setAction(await PDFNet.Action.createURI(doc, 'http://www.apryse.com'));
        await curPage.annotPushBack(hyperLink);
      }
      hlts.next();
    }
  }
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Search PDF files for text](/web/get-started/samples#textsearch) Full code sample which shows how to use TextSearch to search text on PDF pages using regular expressions.


# Portfolio

{% content-ref url="/pages/cgb2JqimBkKlHKjvoFGI" %}
[Sample to create PDF packages using JavaScript](/web/get-started/guides/features/portfolio/samples)
{% endcontent-ref %}

{% content-ref url="/pages/9l0DoVwOV9eBGuJCOQDT" %}
[APIs for creating PDF packages using JavaScript](/web/get-started/guides/features/portfolio/apis)
{% endcontent-ref %}


# Sample to create PDF packages using JavaScript

Learn how to create, extract, and manipulate PDF Packages (portfolios) with this sample. Enhance your PDF skills now! The Apryse Web SDK streamlines secure, serverless document processing with robust

[PDF packages (portfolios)](/web/get-started/samples#pdfpackage) This sample illustrates how to create, extract, and manipulate PDF Packages (also known as PDF Portfolios).


# APIs for creating PDF packages using JavaScript

Discover utility classes for portfolios with PDF documents using Web (Javascript) PDFNet.PDFDoc. Enhance your PDF handling capabilities now! The Apryse Web SDK streamlines secure, serverless document

The following are utility classes that can be used for portfolios (or packages) with PDF documents.

**Web (Javascript)**

* [PDFNet.PDFDoc](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html)


# Manipulation

{% content-ref url="/pages/17AMu9IZK0JfoSgb3ZGy" %}
[How to merge or insert PDF pages using JavaScript](/web/get-started/guides/features/manipulation/merge)
{% endcontent-ref %}

{% content-ref url="/pages/nk8v0EqbvN3HXyKkPdwR" %}
[Removing pages from PDFs using JavaScript](/web/get-started/guides/features/manipulation/remove)
{% endcontent-ref %}

{% content-ref url="/pages/4Z2mef8Qr1zzrrUbkLER" %}
[Rotate PDF pages using JavaScript](/web/get-started/guides/features/manipulation/rotate)
{% endcontent-ref %}

{% content-ref url="/pages/gWv8X9cU4Q9M5iOduG3N" %}
[Split PDF pages using JavaScript](/web/get-started/guides/features/manipulation/split)
{% endcontent-ref %}

{% content-ref url="/pages/GUaTwomChSlnAbsdWsbP" %}
[Cropping PDF pages using JavaScript](/web/get-started/guides/features/manipulation/crop)
{% endcontent-ref %}

{% content-ref url="/pages/e6vs3WCXuuo48hWQErRr" %}
[Create a blank document page using JavaScript](/web/get-started/guides/features/manipulation/create)
{% endcontent-ref %}

{% content-ref url="/pages/TY4mccMO9ROMO24GfQCx" %}
[Reordering pages in a PDF document using JavaScript](/web/get-started/guides/features/manipulation/reorder)
{% endcontent-ref %}


# How to merge or insert PDF pages using JavaScript

Learn how to merge, insert, copy, rearrange, or delete pdf pages using Python

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To insert/copy/merge several PDF documents into one.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
    const newDoc = await PDFNet.PDFDoc.create();
    const page_num = 15;
    for (let i=1; i<=page_num; ++i)
    {
        const doc = await PDFNet.PDFDoc.createFromURL(filename + "_split_page_" + i + ".pdf");
        const pageCount = await doc.getPageCount();
        newDoc.insertPages(i, doc, 1, pageCount, PDFNet.PDFDoc.InsertFlag.e_none);
    }
    const buf = await newDoc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_linearized);

    //optionally save the blob to a file or upload to a server
    const blob = new Blob([buf], { type: 'application/pdf' });
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Merge, copy, delete, and rearrange PDF pages](/web/get-started/samples#pdfpage) Full code sample which illustrates how to copy pages from one document to another, how to delete, and rearrange pages and how to use ImportPages().

## About page copying/merging

The recommended way to copy pages from one document to another is with `PDFDoc.InsertPages()`. Its arguments are:

* *insertBeforeThisPage*: An integer specifying where the pages should be inserted
* *sourceDoc*: A PDFDoc from which the pages should be read
* *startPage*: An integer specifying the first page number to insert
* *endPage*: An integer specifying the last page number to insert
* *flag*: A PDFDoc.InsertFlag value

For example, suppose we want to insert the third page of one document after the first page of a second document. The following code snippet performs this with an *insertBeforeThisPage* value of 2 and *startPage* and *endPage* values of 3.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
dest_doc.insertPages(2, source_doc, 3, 3, PDFNet.PDFDoc.InsertFlag.e_none);
```

{% endcode %}
{% endtab %}
{% endtabs %}

We can also insert a range of pages. For example, the following code will insert the second, third, and fourth pages of one document into the end of the second document. We specify that we're inserting into the end of the document by using an *insertBeforeThisPage* value higher than the number of pages in the document:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
dest_doc.insertPages(dest_doc.getPageCount() + 1, source_doc, 2, 4, PDFNet.PDFDoc.InsertFlag.e_none);
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Removing pages from PDFs using JavaScript

Guide to merge, copy, delete, and rearrange PDF pages in JavaScript.

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To remove pages from a PDF document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // Remove the fifth page from the page sequence.
  doc.pageRemove(doc.getPageIterator(5));

  // Remove the third page.
  const i = await doc.getPageIterator();
  i.next();
  i.next();
  doc.pageRemove(i);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Merge, copy, delete, and rearrange PDF pages](/web/get-started/samples#pdfpage) Full code sample which illustrates how to copy pages from one document to another, how to delete, and rearrange pages and how to use ImportPages().

## About removing/deleting pages

`PDFDoc.PageRemove(itr)` only removes the page from document's page sequence. The page and its resources are still available until the document is saved in 'full save mode' with the 'remove unused objects' flag. If you are saving the file in 'incremental mode', the serialized document may contain the content of the removed page.


# Rotate PDF pages using JavaScript

Learn how to rotate pages in a PDF document with ease. Discover how to rotate pages clockwise by 90 degrees for better viewing and printing. Find out more about Page.GetRotation() and Page.SetRotation

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To rotate a page in a PDF document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // Rotate the first page 90 degrees clockwise.
  const page = await doc.getPage(1);
  const originalRotation = await page.getRotation();
  var rotation;
  switch (originalRotation)
  {
    case Page.e_0:   rotation = Page.e_90;  break;
    case Page.e_90:  rotation = Page.e_180; break;
    case Page.e_180: rotation = Page.e_270; break;
    case Page.e_270: rotation = Page.e_0;   break;
    default:         rotation = Page.e_0;   break;
  }
  page.setRotation(rotation);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

## About rotating pages

A page can be rotated clockwise, by 90 degrees, when displayed or printed. The `Page.GetRotation()` method returns the `Page.Rotate` enum specifying the current rotation. Similarly, `Page.SetRotation()` sets the current rotation.


# Split PDF pages using JavaScript

Learn how to split a PDF document into multiple pages and perform various operations like merging, copying, deleting, and rearranging PDF pages with code examples. Master the ImportPages() function. T

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To split a PDF document into multiple pages.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);
  const page_num = await doc.getPageCount();
  for (let i=1; i<=page_num; ++i)
  {
    const newDoc = await PDFNet.PDFDoc.create();
    newDoc.insertPages(0, doc, i, i, PDFNet.PDFDoc.InsertFlag.e_none);
    const buf = await newDoc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_linearized);

    //optionally save the blob to a file or upload to a server
    const blob = new Blob([buf], { type: 'application/pdf' });
  }
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Merge, Copy, Delete, and Rearrange PDF Pages](/web/get-started/samples#pdfpage) Full code sample which illustrates how to copy pages from one document to another, how to delete, and rearrange pages and how to use ImportPages().


# Cropping PDF pages using JavaScript

Learn how to crop a page in a PDF document with this guide. Understand the importance of the crop box and how to set it using Page.SetCropBox(). Master PDF editing now! The Apryse Web SDK streamlines

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To crop a page in a PDF document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // Access a PDF page
  const page = await doc.getPage(1);

  // Crop the page
  page.setCropBox(new PDFNet.Rect(0, 0, 500, 600));
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

## About cropping the page

The crop box defines the region to which the contents of the page are to be clipped (cropped) when displayed or printed. Unlike other types of boxes, the crop box has no defined meaning in terms of physical page geometry or intended use; it merely imposes clipping on the page contents. The default value is the page's media box. A new crop box can be imposed on a page with `Page.SetCropBox()`.


# Create a blank document page using JavaScript

Learn how to create a new 8.5x11 blank page in a PDF document and add it at the end of the page sequence. Follow step-by-step instructions for optimal results! The Apryse Web SDK streamlines secure, s

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To create a new 8.5x11 blank page and add it at the end of document's page sequence.

Note that, after the page is created, it does not yet belong to a document's page sequence. The page needs to be placed within the page sequence in order to become "visible". `PagePushBack()` inserts page *x* into the position of the document's last page.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // create a new page
  const x = await doc.pageCreate();
  doc.pagePushBack(x);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

## About creating a new blank page

To create a new page, use the `PDFDoc.PageCreate(media_box)` method. `PageCreate()` takes an optional Rect argument that can be used to specify page size. This Rect is called a media box.

A *media box* is a rectangle, expressed in default user space units, defining the boundaries of the physical medium on which the page is intended to be displayed or printed. A user space unit is 1/72 of an inch. If media\_box is unspecified, the default dimensions of the page are 8.5 x 11 inches (or 8.5\*72, 11\*72 units).


# Reordering pages in a PDF document using JavaScript

Learn how to reorder pages in a PDF document by reversing their order. Easily rearrange, sort, merge, copy, delete, and rearrange PDF pages with code samples. The Apryse Web SDK streamlines secure, se

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To reorder pages in a PDF document by reversing the order of pages.

Given the copy and delete page operations described in other sections, it is easy to re-arrange and sort pages.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  const page_num = await doc.getPageCount();
  for (let i=1; i<=page_num; ++i)
  {
    const itr = await doc.getPageIterator(i);
    const page = await itr.current();
    doc.pageRemove(itr);
    doc.pagePushFront(page);
  }
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Merge, copy, delete, and rearrange PDF pages](/web/get-started/samples#pdfpage) Full code sample which illustrates how to copy pages from one document to another, how to delete, and rearrange pages and how to use ImportPages().


# Security

{% content-ref url="/pages/IWyRZzPxJrI5bP9ArpS0" %}
[Apryse Custom Security Handler using JavaScript](/web/get-started/guides/features/security/custom-handler)
{% endcontent-ref %}


# Apryse Custom Security Handler using JavaScript

Protect your files with Apryse Custom Security Handler for DRM use cases. Learn how to encrypt and decrypt documents with this built-in SDK feature. The Apryse Web SDK streamlines secure, serverless d

The Apryse Custom Security Handler provides a means to encrypt files in a way that cannot be decrypted by other applications. This makes it especially suitable for Digital Rights Management (DRM) use cases. This is an alternative to defining your own custom encryption method. In particular using the Apryse Custom Security Handler is much simpler as it comes built-in with Apryse SDK.

Like other encryption methods a password is required to encrypt and decrypt the document. This custom security handler also requires the application choose a unique unsigned integer custom id (typically one per application) of their choice to encrypt and decrypt the document. Using this unique id provides an extra level of security as even other applications based on Apryse SDK will not typically be configured to open files secured with that particular id.

## Decrypting an Apryse Custom secured document using the password and application custom id

The same application custom id and password using during encryption are required for decryption. Failing to provide the correct password or application custom id will prevent opening the encrypted document.

To decrypt a PDF with Apryse Custom security:

If you're opening the file through the WebViewer UI or WebViewer Core then you can use the `customHandlerId` option in loadDocument.

With the WebViewer UI:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer({
  // options
}, viewerElement)
.then(instance => {
  instance.UI.loadDocument('secure.pdf', {
    password: 'password',
    customHandlerId: 42
  });
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

With the WebViewer Core:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
myDocumentViewerInstance.loadDocument('secure.pdf', {
  password: (callback) => {
    return callback('password');
  },
  customHandlerId: 42
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

If you would like to use the full API to do this with a `PDFDoc` in memory:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  // Register the Apryse Custom Security handler with the same custom id used in encryption.
  // Calling this function is a requirement to load files encrypted with PDFTronCustomSecurityHandler.
  await PDFNet.addPDFTronCustomHandler(my_custom_id);
  const doc_enc = await PDFNet.PDFDoc.createFromFilePath(filename);
  if (await doc_enc.initStdSecurityHandlerUString(my_password)) {
  // The password is correct! Document can now be used for reading and editing
  }
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}

{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
<html>
  <script src="../lib/core/webviewer-core.min.js"></script>
  <script src="../lib/core/pdf/PDFNet.js"></script>
  <script>
    (async function() {
      Core.setWorkerPath('../lib/core');
      // Register the Apryse Custom Security handler with the same custom id used in encryption.
      // Calling this function is a requirement to load files encrypted with PDFTronCustomSecurityHandler.
      await PDFNet.addPDFTronCustomHandler(myCustomId);
      const doc_enc = await PDFNet.PDFDoc.createFromURL(filename);
      if ((await doc_enc.initStdSecurityHandlerUString(myPassword))) {
        // The password is correct! Document can now be used for reading and editing
      }
    })()
  </script>
</html>
```

{% endcode %}

[PDFNet.PDFDoc.createFromURL](https://docs.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.createFromURL__anchor) [PDFNet.PDFDoc.initStdSecurityHandlerUString](https://docs.apryse.com/api/web/Core.PDFNet.PDFDoc.html#initStdSecurityHandlerUString__anchor) [PDFNet.addPDFTronCustomHandler](https://docs.apryse.com/api/web/Core.PDFNet.html#addPDFTronCustomHandler__anchor) [Core.setWorkerPath](https://docs.apryse.com/api/web/Core.html#.setWorkerPath__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
<html>
  <script src="../lib/core/CoreControls.js"></script>
  <script src="../lib/core/pdf/PDFNet.js"></script>
  <script>
    (async function() {
      CoreControls.setWorkerPath('../lib/core');
      // Register the Apryse Custom Security handler with the same custom id used in encryption.
      // Calling this function is a requirement to load files encrypted with PDFTronCustomSecurityHandler.
      await PDFNet.addPDFTronCustomHandler(myCustomId);
      const doc_enc = await PDFNet.PDFDoc.createFromURL(filename);
      if ((await doc_enc.initStdSecurityHandlerUString(myPassword))) {
        // The password is correct! Document can now be used for reading and editing
      }
    })()
  </script>
</html>
```

{% endcode %}

[PDFNet.PDFDoc.createFromURL](https://docs.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.createFromURL__anchor) [PDFNet.PDFDoc.initStdSecurityHandlerUString](https://docs.apryse.com/api/web/Core.PDFNet.PDFDoc.html#initStdSecurityHandlerUString__anchor) [PDFNet.addPDFTronCustomHandler](https://docs.apryse.com/api/web/Core.PDFNet.html#addPDFTronCustomHandler__anchor) [CoreControls.setWorkerPath](https://docs.apryse.com/api/web/Core.html#.setWorkerPath__anchor)
{% endtab %}
{% endtabs %}

[Encrypt and Decrypt PDF Files](/web/get-started/samples#encryption) Full sample code which illustrates some of our encryption support.

## Encrypting an Apryse Custom secured document using the password and application custom id

The first step before encrypting is to choose an application custom id. A typical way to create this is to use a random generator to create a 32-bit number. This number would then be used throughout your document workflow.

Note that when using WebViewer you would generally perform the encryption on the server side, but the code is shown below for reference and will look similar on the server.

Once that is done, the document can be encrypted:

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
<html>
  <script src="../lib/core/webviewer-core.min.js"></script>
  <script src="../lib/core/pdf/PDFNet.js"></script>
  <script>
    (async function() {
      Core.setWorkerPath('../lib/core');
      const PDFNet = Core.PDFNet;
      const doc = await PDFNet.PDFDoc.createFromURL(fileURL);
      // Create Apryse custom security handler with a custom id. Replace this with your own integer
      const customHandler = await PDFNet.PDFTronCustomSecurityHandler.create(myCustomId);
      // Set a new password required to open a document
      customHandler.changeUserPasswordUString(myPassword);
      // Note: document takes the ownership of customHandler.
      doc.setSecurityHandler(customHandler);
      // Save the encrypted document
      const docArrayBuffer = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_remove_unused);
    })()
  </script>
</html>
```

{% endcode %}

[PDFNet.PDFDoc.createFromURL](https://docs.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.createFromURL__anchor) [PDFNet.PDFDoc.setSecurityHandler](https://docs.apryse.com/api/web/Core.PDFNet.PDFDoc.html#setSecurityHandler__anchor) [PDFNet.PDFTronCustomSecurityHandler.create](https://docs.apryse.com/api/web/Core.PDFNet.PDFTronCustomSecurityHandler.html#.create__anchor) [PDFNet.SecurityHandler.changeUserPasswordUString](https://docs.apryse.com/api/web/Core.PDFNet.SecurityHandler.html#changeUserPasswordUString__anchor) [Core.setWorkerPath](https://docs.apryse.com/api/web/Core.html#.setWorkerPath__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
<html>
  <script src="../lib/core/CoreControls.js"></script>
  <script src="../lib/core/pdf/PDFNet.js"></script>
  <script>
    (async function() {
      CoreControls.setWorkerPath('../lib/core');
      const doc = await PDFNet.PDFDoc.createFromURL(fileURL);
      // Create Apryse custom security handler with a custom id. Replace this with your own integer
      const customHandler = await PDFNet.PDFTronCustomSecurityHandler.create(myCustomId);
      // Set a new password required to open a document
      customHandler.changeUserPasswordUString(myPassword);
      // Note: document takes the ownership of customHandler.
      doc.setSecurityHandler(customHandler);
      // Save the encrypted document
      const docArrayBuffer = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_remove_unused);
    })()
  </script>
</html>
```

{% endcode %}

[PDFNet.PDFDoc.createFromURL](https://docs.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.createFromURL__anchor) [PDFNet.PDFDoc.setSecurityHandler](https://docs.apryse.com/api/web/Core.PDFNet.PDFDoc.html#setSecurityHandler__anchor) [PDFNet.PDFTronCustomSecurityHandler.create](https://docs.apryse.com/api/web/Core.PDFNet.PDFTronCustomSecurityHandler.html#.create__anchor) [PDFNet.SecurityHandler.changeUserPasswordUString](https://docs.apryse.com/api/web/Core.PDFNet.SecurityHandler.html#changeUserPasswordUString__anchor) [CoreControls.setWorkerPath](https://docs.apryse.com/api/web/Core.html#.setWorkerPath__anchor)
{% endtab %}
{% endtabs %}

[Encrypt and Decrypt PDF Files](/web/get-started/samples#encryption) Full sample code which illustrates some of our encryption support.


# Extraction

{% content-ref url="/pages/6CktPKfP5BJ2XWq75oJl" %}
[Extracting text from a PDF using JavaScript](/web/get-started/guides/features/extraction/text-extract)
{% endcontent-ref %}


# Extracting text from a PDF using JavaScript

We show how to extract text from a PDF in JavaScript

To extract text from a PDF document.

{% hint style="warning" %}
Text extraction reading ordering is not defined in the ISO PDF standard. In fact, there is no concept of sentence, paragraph, tables, or anything similar in a typical PDF file. This means each PDF vendor is left to their own design/solution and will extract text with some differences. Therefore, reading order is not guaranteed to match the order that a typical user reading the document would follow.

The reading order of a magazine, newspaper article, and an academic article are all quite different due to the lack of semantic information in a PDF and the placement/ordering of text in the document. Where different users may have different expectations of the correct reading order.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
    const doc = await PDFNet.PDFDoc.createFromURL(filename);
    const page = await doc.getPage(1);

    const txt = await PDFNet.TextExtractor.create();
    const rect = await page.getCropBox();
    txt.begin(page, rect); // Read the page.

    // Extract words one by one.
    let line = await txt.getFirstLine();
    for (; (await line.isValid()); line = (await line.getNextLine())) 
    {
        for (word = await line.getFirstWord(); (await word.isValid()); word = (await word.getNextWord())) 
        {
            // await word.getString();
        }
    }
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Read a PDF File sample](/web/get-started/samples#textextract) Full sample code which illustrates the basic text extraction capabilities.

## Extract text under an annotation

To extract text from under an annotation in the document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
    const doc = await PDFNet.PDFDoc.createFromURL(filename);
    const page = await doc.getPage(1);
    const annotation = await page.getAnnot(0);

    const txt = await PDFNet.TextExtractor.create();
    const rect = await page.getCropBox();
    txt.begin(page, rect); // Read the page.
    const textData = await txt.getTextUnderAnnot(annotation);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

## About extracting text

When we use the `ElementReader` class to read elements from a PDF document, we are often faced with data that is partial. For example, let us say that we are attempting to extract a sentence that says "This is a sample sentence." from a PDF document. We could potentially end up with two elements - "T" and "his is a sample sentence.". This is possible because in a PDF document, text objects are ***not*** always cleanly organized into words sentences, or paragraphs. The `ElementReader` class will return `Element` objects exactly as they are defined in the PDF page content stream.

## Text runs

An element of type `e_text` directly corresponds to a `Tj` element in the PDF document. Each `e_text` element represents a **text run**, which represents a sequence of text glyphs that use the same font and graphics attributes. Say, if there is a single word, whose letters are each presented with a different font, then each letter would be a separate text run. You may also encounter text runs that contain multiple words separated by spaces. The PDF format does not guarantee that the text will be presented in reading order.

## TextExtractor class

All this just goes to say that attempting to use an `ElementReader` to extract text data from a PDF document is not guaranteed to return data in the order expected (reading order). The most straightforward approach to extract words and text from text-runs is using the `pdftron.PDF.TextExtractor` class, as shown in the `TextExtract` sample project - [TextExtract Sample](/web/get-started/samples/textextracttest)

TextExtractor will assemble words, lines, and paragraphs, remove duplicate strings, reconstruct text reading order, etc. Using `TextExtractor` you can also obtain bounding boxes for each word, line, or paragraph (along with style information such as font, color, etc). This information can be used to search for corresponding text elements using `ElementReader`.


# Basics

{% content-ref url="/pages/Ns3nWocnnptNS9MFhOEi" %}
[Lock a PDF document using JavaScript](/web/get-started/guides/features/basics/lock)
{% endcontent-ref %}

{% content-ref url="/pages/bCczeZCxafeslrn8J63n" %}
[Open a PDF using JavaScript](/web/get-started/guides/features/basics/open)
{% endcontent-ref %}

{% content-ref url="/pages/cbmroZYufB4y0ub27DEW" %}
[Access PDF content to read, write & edit using JavaScript](/web/get-started/guides/features/basics/page-content)
{% endcontent-ref %}

{% content-ref url="/pages/AblYDjefoySzxJ977RY5" %}
[Access PDF pages to add, copy, delete or rearrange using JavaScript](/web/get-started/guides/features/basics/pages)
{% endcontent-ref %}

{% content-ref url="/pages/Zw9E4bwjGZcOQioNQnUY" %}
[Serialization / Saving PDFs using JavaScript](/web/get-started/guides/features/basics/save)
{% endcontent-ref %}


# Lock a PDF document using JavaScript

Learn how to efficiently manage PDF document instances across multiple threads with Apryse SDK locking mechanisms. Improve performance and enable parallel applications. The Apryse Web SDK streamlines

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

{% hint style="info" %}
If you plan on sharing a single PDF document instance across multiple threads, then you need to lock the document. Not locking a PDF document instance shared across threads can result in undefined behaviour, including crashing.
{% endhint %}

An Apryse SDK lock is based on two principles:

[Reentrant mutex (also known as recursive lock)](https://en.wikipedia.org/wiki/Reentrant_mutex) Recursive mutex may be locked multiple times by the same process/thread without causing a deadlock.

[Readers-writer (RW) lock (also known as shared-exclusive lock)](https://en.wikipedia.org/wiki/Readers-writer_lock) An RW lock allows concurrent access for read-only operations, while write operations require exclusive access.

## Write to a document

To write to a PDF document in a multithreaded environment.

{% hint style="warning" %}
Only one thread can hold a write lock at any given time.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // Lock the document
  doc.lock();

  // ... perform some document processing using write operations

  // Now we release the lock
  doc.unlock();
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Multithreaded PDF reading](/web/get-started/samples#multithreaded) Full source code which illustrates how to use PDFDoc locking mechanisms to access the document concurrently. PDFDoc uses a recursive shared lock model.

## Read and write to a document

To read and write to a PDF document in a multithreaded environment.

{% hint style="warning" %}
A thread cannot acquire a write lock while holding a read lock.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // Lock the document
  doc.lock();

  // Optionally note that locking a second time does not cause a deadlock
  doc.lock();

  // You can acquire a read lock while holding a write lock:
  doc.lockRead();

  // ... perform some document processing using read operations

  doc.unlockRead();

  // ... perform some document processing using write operations

  // Releasing the lock decrements our lock count,
  // but the thread still holds a write lock on the document
  doc.unlock();

  // Now we release the write lock, and
  // other tasks can begin execution
  doc.unlock();
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Multithreaded PDF reading](/web/get-started/samples#multithreaded) Full source code which illustrates how to use PDFDoc locking mechanisms to access the document concurrently. PDFDoc uses a recursive shared lock model.

## About locking a document

As computing devices become more parallel in nature, Apryse is evolving to allow developers to leverage this power in new and exciting ways. Apryse version 6.0 introduces new locking semantics which allow for concurrent access of a `PDFDoc` instance. This was done to improve performance during interactive viewing (simultaneous rendering, text extraction, etc.), as well as to open up the possibility for new use cases (parallel rendering). This article introduces the locking system, and will get you on your way to developing parallel applications with Apryse SDK.

## PDFDoc lock

Apryse uses a recursive read/write locking system. Multiple threads can hold a read lock on the document, **but only one thread can hold a write lock at any given time**. A thread can acquire an equivalent or weaker lock as many times as it likes without causing a deadlock. In other words, the following is valid:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL("foo.pdf");
  d.lock();
  d.lock();
  d.lockRead();
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

However, a **thread cannot acquire a write lock while holding a read lock**. Because one can only acquire a write lock when no read locks are held, this situation would inevitably lead to a deadlock. To avoid this scenario, Apryse will throw a runtime exception whenever the situation occurs.

## Locking in the Apryse API

Some of our API calls internally acquire a write lock on the document. As a result, these calls can also throw a runtime exception if they are invoked while holding a read lock. This is noted in each method's documentation. Additionally, you will find a complete list of those methods at the end of this article.

In general, the parts of our library that manage the UI will maintain document locks. **For the lower level calls which actually modify the document, you are responsible for maintaining document locks.**

## Client side locking

Apryse provides the following APIs for locking the document:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
function PDFDoc.lock()
function PDFDoc.tryLock(milliseconds)
function PDFDoc.unlock()
function PDFDoc.lockRead()
function PDFDoc.tryLockRead(milliseconds)
function PDFDoc.unlockRead()
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Migration of earlier code

The new locking system is backwards compatible, meaning previous calls to `PDFDoc.Lock` now acquire a write lock. If you would like to take advantage of the ability to read a PDF concurrently, it is your responsibility to review document locks and determine whether it is safe to downgrade them to a read lock.

## Opting out

Conversely, if you are happy with the existing *'one document, one thread'* model previously used in Apryse 5.9., you can continue to work with this system. No change is required on your behalf.


# Open a PDF using JavaScript

Learn how to open a PDF document from a memory buffer with full source code examples. Explore the PDFDoc constructor and best practices for handling PDF documents efficiently. The Apryse Web SDK strea

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To open a PDF document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
// open document from the filesystem or URL
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // optionally read a PDF document from a stream
  const file = await PDFNet.Filter.createURLFilter(filename);
  const doc_stream = await PDFNet.PDFDoc.createFromFilter(file);

  // or pass-in a memory buffer
  const file_sz = await file.size();
  const file_reader = await PDFNet.FilterReader.create(file);
  const mem = await file_reader.read(file_sz);
  const doc_mem = await PDFNet.PDFDoc.createFromBuffer(mem);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Read & write a PDF file from/to memory buffer](/web/get-started/samples#pdfdocmemory) Full source code which illustrates how to read/write a PDF document from/to memory buffer. This is useful for applications that work with dynamic PDF documents that don't need to be saved/read from a disk.

## About opening a document

The PDFDoc constructor creates a PDF document from scratch:

{% hint style="warning" %}
**PDFDoc.Close()**

When you are finished with a PDFDoc object, the PDFDoc.Close() method should be called to clean up memory, file handles, and resources.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.create();
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

A newly-created document does not yet contain any pages. See the [accessing pages](/web/get-started/guides/features/basics/pages) section for details on creating new pages and working with existing pages.

Using Apryse SDK, you can open a document from a serialized file, from a memory buffer, or from a Filter stream.

To open an existing PDF document from a file, specify its file path in the PDFDoc constructor:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Here's how to open an existing PDF document from a memory buffer:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const file = await PDFNet.Filter.createURLFilter(filename);
  const file_sz = await file.size();
  const file_reader = await PDFNet.FilterReader.create(file);
  const mem = await file_reader.read(file_sz);
  const doc_mem = await PDFNet.PDFDoc.createFromBuffer(mem);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

It's also easy to open a PDF document from a [MemoryFilter or a custom Filter ](/web/low-level-pdf-api/filters).

After creating a PDFDoc object, it's good practice to call `InitSecurityHandler()` on it. If the document is encrypted, calling the method will decrypt it. If the document is not encrypted, calling the method is harmless.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);
  if (!doc.initSecurityHandler()) {
    console.log("Document authentication error...");
    return;
  }
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Access PDF content to read, write & edit using JavaScript

Learn how to access PDF page content with the ElementReader API. Explore code samples and powerful features of Apryse SDK for efficient content extraction and dynamic PDF generation. The Apryse Web SD

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To access PDF page content.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);
  const page = await doc.getPage(page_num);
  const reader = await PDFNet.ElementReader.create();

  // Use reader to being reading the page
  reader.beginOnPage(page);

  for (let element = await reader.next(); element !== null; element = await reader.next())
  {
    // ... perform some document processing
  }
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Read Elements Across All PDF Pages](/web/get-started/samples#elementreader) Full code sample which illustrates how to traverse page display list using ElementReader.

## About working with page content

Apryse SDK provides a powerful, easy-to-use API that can be used to read, write and edit text, images, and other graphical entities, called the *Element* API. A good match for interactive applications (such as PDF viewers and editors), for content extraction applications (such as PDF conversion and validation), and for dynamic PDF generation because the Element API is very efficient.

Page content, a major component of a PDF document, is made up of the visible marks on a page drawn by PDF marking operators. For details on PDF content streams and thorough operator descriptions please refer to Section 3.7.1, “Content Streams,” in the PDF Reference Manual.

Although the Apryse SDK SDF and Filter APIs provide everything required to decode and parse low-level content streams, using the Element API is easier and more intuitive. The reason why is that the Element API allows you to treat a page's contents as a *list of objects* (i.e. a display list or a sequence of Elements) rather than as sets of cryptic marking operators.

## What is an element?

An Element (such as text, a path, or an image) is constructed from a set of marking operators from the page content stream. A set of Elements represents a display list.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-afafb1dd1f208fa387d7e910fc852bdec63134bd%2F1bbeacf628e7120439d1692a36a1bd0aaa6e5167-469x244.gif?alt=media)

A sequence of page marking operators represents an Element.

Therefore, the Apryse SDK Element interface allows you to treat page contents as a list of objects whose values and attributes can be modified.

Using the Element interface, applications can read, write, edit, and create page contents and resources. These contents and resource may in turn contain fonts, images, shadings, patterns, extended graphics states, and so on.

An application may use Element methods to modify the appearance of a page, or it can create page content from scratch.

Each Element is independent of other Elements. Therefore, every Element encapsulates all the relevant information about itself. A text object, for example, contains all font attributes.

*Element* is the concrete base class for all Elements. Apryse SDK supports all content elements allowed by the PDF format, namely: `path`, `text_begin`, `text`, `text_new_line`, `text_end`, `image`, `inline_image`, `shading`, `form`, `group_begin`, `group_end`, `marked_content_begin`, and `marked_content_end`.

Note that some Elements — such as `path`, `text`, `image`, `inline-image`, and `shading` — represent concrete graphical elements. However, other Elements — such as `text_begin/end`, `text_new_line`, `group_begin/end`, and `marked_content_begin/end` — don't have graphical representation but are used for logical grouping of Element sequences or to provide meta-data associated with Element groups.

The Element class hierarchy implements a composite pattern — that is, the Element class provides the methods of all derived classes.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-24611cee0bf5d34d272aeec25ffcaaa18770db2e%2Fa9e064a8e563c4d0c5c915b3329aaf1b136ca05c-352x1316.gif?alt=media)

Element hierarchy. Only methods listed in the Element group or base class can be invoked for the given type.

To find the type of an Element object, use the `element.GetType()` method. **Be forewarned:** it is not allowed to call methods on an object that are not related to that object's Element type. The behavior when doing so is undefined. For example, it is illegal to call `element.GetImageData()` on an e\_path element.

Note that, in above, `e_group_begin/end` and `e_text_begin/end` don't add any functionality to the common Element interface (i.e. `GetType()/GetGState()/GetCTM()`). The main purpose of these Elements is to mark sequences of Elements into logical groups. The Element `e_group_begin` corresponds to the PDF 'q' operator (saveState), `e_group_end` corresponds to the 'Q' operator, `e_text_begin` corresponds to the 'BT' (begin text) operator, and e\_text\_end corresponds to the 'ET' operator.

`e_text_begin` initializes a text object, initializing the text matrix and the text line matrix to the identity matrix. Because PDF text objects can't be nested, a second `e_text_begin` element cannot appear before `e_text_end`. A text object contains one or more text runs (that is, e\_text elements) and new line markers (that is, e\_text\_new\_line elements). `e_text` and `e_text_new_line` are not allowed outside of the text group (that is, outside element sequence surrounded by `e_text_begin/end`).

## Graphics state

Every element has an associated CTM (current transformation matrix) and graphics state. `Element.GetCTM()` returns the transformation matrix used while processing the current Element. `Element.GetGState()` returns the element's associated graphics state. GState keeps track of a number of style attributes used to visually define graphical Elements.

The methods available through the GState class are listed below:

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-0dedc7fa7f12f11169da67671472285d1a7edff6%2Ffdd3f933e2db8d513b3934a8f38500da7abf7066-178x830.gif?alt=media)

Graphics State.

For a detailed description of graphics state attributes refer to section 4.3 "Graphics State" in the PDF Reference Manual.


# Access PDF pages to add, copy, delete or rearrange using JavaScript

Learn how to efficiently manage PDF pages with code samples for copying, deleting, and rearranging pages. Explore methods like ImportPages() for seamless operations. The Apryse Web SDK streamlines sec

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

To access a PDF page.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // Access a PDF page
  const page = await doc.getPage(page_num);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Merge, copy, delete, and rearrange PDF pages](/web/get-started/samples#pdfpage) Full code sample which illustrates how to copy pages from one document to another, how to delete, and rearrange pages and how to use ImportPages() method for very efficient copy and merge operations.

## About working with pages

A high-level PDF document contains a sequence of Page objects, as illustrated in the following figure:

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-ac189ed4a6c6c0b49e973d38c30f51c543a85a33%2F53146f7976a63f3be37adb38823899c1f02acc1a-394x193.gif?alt=media)

PDFDoc Page sequence.

To find the number of pages in a PDF document, call `PDFDoc.GetPageCount()`.

To retrieve a specific page of a document, use `PDFDoc.GetPage(page_num)`. Page numbers in the document's page sequence are indexed from 1. If the given page number doesn't index a page in the current document, `GetPage(page_num)` returns null. For example:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const page = await doc.getPage(page_num);
  if (page != null)
  {
    console.log("Document does contain page#: %d", page_num);
  }
  else
  {
    console.log("Document does not contain page#: %d", page_num);
  }
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

While `GetPage(i)` is convenient for retrieving an individual page, it's an inefficient way to enumerate every page of a document. It's better to traverse the pages with a PageIterator.

To do so, simply call `PDFDoc.GetPageIterator()`. This returns a PageIterator object, which provides `HasNext()`, `Next()` and `Current()` methods. The following code snippet shows how to print the page size for every page in document page sequence:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const itr = await doc.getPageIterator();
  for (itr; await itr.hasNext(); itr.next())
  {
    const curritr = await itr.current();
    const mediabox = curritr.getMediaBox();
    console.log("Media box: %f, %f, %f, %f", mediabox.x1, mediabox.y1, mediabox.x2, mediabox.y2);
  }
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

(This code finds the page size using the page's media box, which we'll talk more about in the following sections.)

To jump to a specific page with a PageIterator, call `PDFDoc.GetPageIterator(page_num)`. If no such page exists, the index of the page will return 0. For example:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const itr = await doc.getPageIterator(page_num);
  if (itr.next().getIndex() > 0)
  {
    console.log("Document does contain page#: %d", page_num);
  }
  else
  {
    console.log("Document does not contain page#: %d", page_num);
  }
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Serialization / Saving PDFs using JavaScript

Learn how to efficiently serialize PDF documents with Apryse SDK. Benefit from incremental save, linearization, and support for compressed object streams. Serialize to memory, stream, or disk with eas

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api)
{% endhint %}

Serialization also known as saving provides the ability to write content back to a storage medium.

Apryse SDK benefits include:

* Incremental save (for fast save and document persistence)
* Linearization (Fast Web View)
* Supports compressed object streams
* Unused object removal. This option can help you create smaller files
* Serialize a document to memory, stream, or a file on disk

## Save a document

To save a PDF document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const doc = await PDFNet.PDFDoc.createFromURL(filename);

  // save the document to a memory buffer
  const buf = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_linearized);

  //optionally save the blob to a file or upload to a server
  const blob = new Blob([buf], { type: 'application/pdf' });
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[Read & write a PDF file from/to memory buffer](/web/get-started/samples#pdfdocmemory) Full source code which illustrates how to read/write a PDF document from/to memory buffer. This is useful for applications that work with dynamic PDF documents that don't need to be saved/read from a disk.

## About saving a document

PDF document can be serialized (or saved) to a file on a disk, to a memory buffer, or to an arbitrary data stream such as `MemoryFilter` or a `custom filter`.

To save a PDF document to a file on disk, invoke its `Save()` method:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const buf = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_linearized);

  //optionally save the blob to a file or upload to a server
  const blob = new Blob([buf], { type: 'application/pdf' });
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

The second argument is a bitwise disjunction of flags used as options during serialization.

Apryse SDK allows a document to be saved incrementally (see section 2.2.7 "Incremental Update" in the PDF Reference Manual). Because applications may allow users to modify PDF documents, users should not have to wait for the entire file (which can contain hundreds of pages) to be rewritten each time modifications to the document are saved. Apryse SDK allows modifications to be appended to a file, leaving the original data intact. The addendum appended when a file is incrementally updated contains only those objects that were actually added or modified. Incremental update allows an application to save modifications to a PDF document in an amount of time proportional to the size of the modification rather than the size of the file. In addition, because the original contents of the document are still present in the file, it is possible to undo saved changes by deleting one or more file updates.

Changes can be appended to an existing document using `e_incremental` flag:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const buf = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_incremental);

  //optionally save the blob to a file or upload to a server
  const blob = new Blob([buf], { type: 'application/pdf' });
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Note that the file output name matches the input name.

Over time, PDF documents may accumulate unused objects like old updates, modifications, unused fonts, images, and so on. To trim down the file size by removing these unused objects, use the `e_remove_unused` flag:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const buf = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_remove_unused);

  //optionally save the blob to a file or upload to a server
  const blob = new Blob([buf], { type: 'application/pdf' });
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

A PDF document can also be serialized into a memory buffer as follows:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
  const buf = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_linearized);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}


# Redaction

{% content-ref url="/pages/Uwc6jhVEUlVel3aWkyMY" %}
[Redact a PDF document using JavaScript](/web/get-started/guides/features/redaction/redact)
{% endcontent-ref %}


# Redact a PDF document using JavaScript

Learn how to redact content from a PDF document using Apryse SDK API. Remove sensitive information securely with pdftron.PDF.Redactor. Control the style of redaction overlay for added security. The Ap

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="https://apryse.com/capabilities#Redaction" class="button primary">Package: Redaction</a><a href="https://showcase.apryse.com/redaction" class="button primary">Live demo</a>
{% endhint %}

To redact content from a PDF document.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
    const rarr = []; // we will contain a list of redaction objects in this array
    rarr.push(await PDFNet.Redactor.redactionCreate(1, (await PDFNet.Rect.init(100, 100, 550, 600)), false, 'Top Secret'));
    rarr.push(await PDFNet.Redactor.redactionCreate(2, (await PDFNet.Rect.init(30, 30, 450, 450)), true, 'Negative Redaction'));
    rarr.push(await PDFNet.Redactor.redactionCreate(2, (await PDFNet.Rect.init(0, 0, 100, 100)), false, 'Positive'));
    rarr.push(await PDFNet.Redactor.redactionCreate(2, (await PDFNet.Rect.init(100, 100, 200, 200)), false, 'Positive'));
    rarr.push(await PDFNet.Redactor.redactionCreate(2, (await PDFNet.Rect.init(300, 300, 400, 400)), false, ''));
    rarr.push(await PDFNet.Redactor.redactionCreate(2, (await PDFNet.Rect.init(500, 500, 600, 600)), false, ''));
    rarr.push(await PDFNet.Redactor.redactionCreate(3, (await PDFNet.Rect.init(0, 0, 700, 20)), false, ''));

    const app = {};
    app.redaction_overlay = true;
    app.border = false;
    app.show_redacted_content_regions = true;

    const doc = await PDFNet.PDFDoc.createFromURL(filename);
    PDFNet.Redactor.redact(doc, rarr, app, false, false);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[PDF redaction](/web/get-started/samples/pdfredacttest) Full code sample which shows how to use `pdftron.PDF.Redactor` to remove potentially sensitive content within PDF documents.

## About redactor

Apryse Redactor makes sure that if a portion of an image, text, or vector graphics is contained in a redaction region, that portion of the image or path data is destroyed and is not hidden with clipping or image masks. Apryse SDK API can also be used to review and remove metadata and other content that can exist in a PDF document, including XML Forms Architecture (XFA) content and Extensible Metadata Platform (XMP) content.

The redaction process in Apryse SDK consists of two steps:

**1. Content identification** A user applies redact annotations that specify the pieces or regions of content that should be removed. The content for redaction can be identified either interactively (e.g. using ‘pdftron.PDF.PDFViewCtrl’ as shown in PDFView sample) or programmatically (e.g. using ‘pdftron.PDF.TextSearch’ or ‘pdftron.PDF.TextExtractor’). Up until the next step is performed, the user can see, move and redefine these annotations.

**2. Content removal** Using ‘pdftron.PDF.Redactor.Redact()’ the user instructs Apryse SDK to apply the redact regions, after which the content in the area specified by the redact annotations is removed. The redaction function includes number of options to control the style of the redaction overlay (including color, text, font, border, transparency, etc.).


# Bookmark

{% content-ref url="/pages/zZadb8Wd5z7Rng48xKLo" %}
[Code samples to edit PDF bookmarks using JavaScript](/web/get-started/guides/features/bookmark/samples)
{% endcontent-ref %}


# Code samples to edit PDF bookmarks using JavaScript

Discover how to read, add, and edit PDF outlines and bookmarks with this high-level API sample code. Enhance your document management skills today! The Apryse Web SDK streamlines secure, serverless do

[Read, add, edit PDF outlines and bookmarks](/web/get-started/samples#bookmarks) The sample code illustrates how to read and edit existing outline items and create new bookmarks using the high-level API.


# Apryse Web SDK Samples: View, Annotate, Edit, and More

{% content-ref url="/pages/Pr5WoLhhmB0sYPErqsvc" %}
[Advanced JavaScript samples](/web/get-started/guides/samples/signatures)
{% endcontent-ref %}

{% content-ref url="/pages/iCxhyOaLzIzdMmGZok99" %}
[Samples to customize PDF viewer UI with JavaScript](/web/get-started/guides/samples/customization)
{% endcontent-ref %}

{% content-ref url="/pages/f3V241GP1cdZOsDbA3G0" %}
[Samples to view documents using JavaScript](/web/get-started/guides/samples/viewing)
{% endcontent-ref %}

{% content-ref url="/pages/FkSLM5UTn8jrnNBk8y5i" %}
[Full API samples for WebViewer](/web/get-started/guides/samples/full-apis)
{% endcontent-ref %}

{% content-ref url="/pages/kdOtziIMzNTPl4VX5xcv" %}
[Project integration samples](/web/get-started/guides/samples/project-integration)
{% endcontent-ref %}

{% content-ref url="/pages/mKGnu8g5PeecMRvxEb41" %}
[Samples to customize PDF Forms with JavaScript](/web/get-started/guides/samples/forms)
{% endcontent-ref %}

{% content-ref url="/pages/6tJ4pMDNp40gQBanlTth" %}
[PDF manipulation samples in JavaScript](/web/get-started/guides/samples/pdf-manipulation)
{% endcontent-ref %}

{% content-ref url="/pages/imugd625lh7LDOBX52lb" %}
[Annotation code samples in JavaScript](/web/get-started/guides/samples/annotation)
{% endcontent-ref %}


# Advanced JavaScript samples

Discover how Sign App Demonstrates building a signing application where users can request signatures, place fields, sign documents, and review signed documents. Simplify your document signing process

## Sign App

Demonstrates building a signing application where users can request signatures on the documents by placing fields, sign documents, review signed documents.

<a href="https://github.com/ApryseSDK/pdftron-sign-app/" class="button primary">View source code</a>


# Samples to customize PDF viewer UI with JavaScript

Learn how to customize form fields by changing color and opacity. Discover internationalization techniques to switch languages on your website. The Apryse Web SDK streamlines secure, serverless docume

## Customizing form fields

Shows how to change color and opacity of form fields.

<a href="https://sdk.apryse.com/samples/web/samples/forms/form-fields/" class="button primary">Launch demo</a><a href="/web/get-started/samples/form-fields" class="button primary">View source code</a>

## Internationalization

Shows how to switch language.

<a href="https://sdk.apryse.com/samples/web/samples/customization/internationalization/" class="button primary">Launch demo</a><a href="/web/get-started/samples/internationalization" class="button primary">View source code</a>


# Samples to view documents using JavaScript

Learn how to call the WebViewer constructor to load and instantiate documents, whether local or remote. Explore different viewing options with WebViewer Server, Custom Server (XOD), and compare optimi

## Viewing

Shows how to call WebViewer constructor to instantiate and load document. You can load local/remote files of your choice.

<a href="https://sdk.apryse.com/samples/web/samples/viewing/viewing/" class="button primary">Launch demo</a><a href="/web/get-started/samples/viewing" class="button primary">View source code</a>

## Viewing with WebViewer Server

Shows how to call WebViewer constructor to instantiate and load document using WebViewer with WebViewer Server. You can upload local files or load files that are publicly accessible.

<a href="https://sdk.apryse.com/samples/web/samples/viewing/viewing-with-webviewer-server/" class="button primary">Launch demo</a><a href="/web/get-started/samples/viewing-with-webviewer-server" class="button primary">View source code</a>

## Viewing with Custom Server (XOD)

Shows how to load XOD files, which are generated from a custom server.

<a href="https://sdk.apryse.com/samples/web/samples/viewing/viewing-with-custom-server/" class="button primary">Launch demo</a><a href="/web/get-started/samples/viewing-with-custom-server" class="button primary">View source code</a>

## Viewer optimized PDFs

Compares viewing an unoptimized PDF with the same viewer optimized PDF side by side

<a href="https://sdk.apryse.com/samples/web/samples/viewing/fast-viewing/" class="button primary">Launch demo</a><a href="/web/get-started/samples/fast-viewing" class="button primary">View source code</a>


# Full API samples for WebViewer

Learn how to customize document save/download, select elements in a PDF, perform complex editing, preprocess documents, compare PDFs, and more with PDFNetJS samples. The Apryse Web SDK streamlines sec

## Viewer samples

### ViewerCustomSave

This sample demonstrates how to customize document save/download. In the example annotations are flattened before saving.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ViewerCustomSaveTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/viewercustomsavetest" class="button primary">View source code</a>

### ViewerDisplayPoints

This sample demonstrates how to use PDFNetJS to select elements in a PDF document. Click on an element in the PDF and it will show the element's bounding box and an outline if it is a PDF path.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ViewerDisplayPointsTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/viewerdisplaypointstest" class="button primary">View source code</a>

### ViewerEdit

This sample illustrates how PDFNetJS can be used to perform complex editing on PDF documents as they are displayed in the WebViewer. Open a PDF of your choice and press the refresh button at the top right to change all text to blue and all images to red.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ViewerEditTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/vieweredittest" class="button primary">View source code</a>

### ViewerPreprocess

This sample illustrates how PDFNetJS can be used to preprocess a document before displaying it with WebViewer.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ViewerPreprocessTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/viewerpreprocesstest" class="button primary">View source code</a>

### ViewerPDFDiffTest

This sample illustrates how PDFNetJS can be used to compare two PDF documents and show their difference in WebViewer.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ViewerPDFDiffTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/viewerpdfdifftest" class="button primary">View source code</a>

### ViewerSnapToNearestTest

This sample illustrates how to use the snap API for calculating the nearest snap point.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ViewerSnapToNearestTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/viewersnaptonearesttest" class="button primary">View source code</a>

### ViewerDigitalSignatureValidationTest

This sample illustrates how PDFNetJS can be used to validate digital signatures in a document.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ViewerDigitalSignatureValidationTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/viewerdigitalsignaturevalidationtest" class="button primary">View source code</a>

### ReaderModeTest

This sample demonstrates Reader Mode in WebViewer.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ReaderModeTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/readermodetest" class="button primary">View source code</a>

### deck.js

This sample illustrates how PDFNetJS can be used to power deck.js slideshows in the same way WebViewer does using PDFNet rasterizing functions.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/DeckJSTest/" class="button primary">Launch demo</a>

## Console samples

### AddImage

This sample illustrates how to embed various raster image formats (e.g. TIFF, JPEG, JPEG2000, JBIG2, GIF, PNG, BMP, etc.) in a PDF document.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/AddImageTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/addimagetest" class="button primary">View source code</a>

### Annotation

Shows how to add a text annotation to an existing page and how to create link annotations between several pages.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/AnnotationTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/annotationtest" class="button primary">View source code</a>

### Bookmark

The sample code illustrates how to read and edit existing outline items and create new bookmarks using the high-level API.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/BookmarkTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/bookmarktest" class="button primary">View source code</a>

### ContentReplacer

The sample demonstrates replacement of objects such as images, text and strings within a document.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ContentReplacerTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/contentreplacertest" class="button primary">View source code</a>

### DigitalSignature

Demonstrates how to digitally sign a PDF and also how to certify a PDF doc.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/DigitalSignaturesTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/digitalsignaturestest" class="button primary">View source code</a>

### ElementBuilder

Illustrates how to use PDFNet page writing API, how to embed fonts and images and how to copy graphical elements from one page to another.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ElementBuilderTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/elementbuildertest" class="button primary">View source code</a>

### ElementEdit

The sample code shows how to edit the page display list and how to modify graphics state attributes on existing Elements. In particular the sample program strips all images from the page and changes text color to blue.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ElementEditTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/elementedittest" class="button primary">View source code</a>

### ElementReader

Illustrates how to traverse page display list using ElementReader.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/ElementReaderTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/elementreadertest" class="button primary">View source code</a>

### Encryption

Demonstrates how to encrypt and decrypt files by adding or removing passwords.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/EncTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/enctest" class="button primary">View source code</a>

### XFDF Import

PDFNet includes full support for FDF (Forms Data Format) and the capability to merge/extract forms data (FDF) with/from PDF. The sample illustrates basic XFDF merge functionality available in PDFNet.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/FDFTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/fdftest" class="button primary">View source code</a>

### InteractiveForms

Illustrates how to create and edit interactive forms (check boxes and text boxes).

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/InteractiveFormsTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/interactiveformstest" class="button primary">View source code</a>

### LogicalStructure

This sample illustrates how to traverse the structure and content of a tagged PDF document.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/LogicalStructureTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/logicalstructuretest" class="button primary">View source code</a>

### PDFA

This sample demonstrates PDFA verification and conversion.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/PDFATest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/pdfatest" class="button primary">View source code</a>

### PDFDraw

This sample illustrates how to use the built-in rasterizer in order to render PDF images on the fly and how to save resulting images in PNG and JPEG format.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/PDFDrawTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/pdfdrawtest" class="button primary">View source code</a>

### PDFLayers

This sample demonstrates how to create PDF layers (also known as Optional Content Groups - OCGs). The sample also shows how to extract and render PDF layers.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/PDFLayersTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/pdflayerstest" class="button primary">View source code</a>

### PDFPage

The sample illustrates how to copy pages from one document to another, how to delete, and re-arrange pages and how to use ImportPages() method for very efficient copy and merge operations.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/PDFPageTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/pdfpagetest" class="button primary">View source code</a>

### PDFRedaction

The sample illustrates how to create Redaction objects and use PDFNetJS's redact function to remove and overlay PDF document content.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/PDFRedactTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/pdfredacttest" class="button primary">View source code</a>

### PDFRect

The sample illustrates how to translate a PDF image by extracting and manipulating the image's media box.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/RectTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/recttest" class="button primary">View source code</a>

### SDFTest

The sample illustrates how to extract an SDF document from a PDF document and change some of its values.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/SDFTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/sdftest" class="button primary">View source code</a>

### Stamper

The sample code illustrates how to stamp text and images onto various pages in a PDF document.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/StamperTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/stampertest" class="button primary">View source code</a>

### TextExtraction

The sample demonstrates how to extract information from text boxes such as their position, font, size, color and of course the text itself.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/TextExtractTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/textextracttest" class="button primary">View source code</a>

### TextSearch

The sample demonstrates how to search using strings and regular expressions in a PDF document and how to set search mode and parameters.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/TextSearchTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/textsearchtest" class="button primary">View source code</a>

### WebViewerConvert

The sample is using `pdftron.PDF.Convert.ToXod()` to convert/stream PDF, XPS, MS Office, RTF, HTML and other document formats to WebViewer.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/WebViewerConvertTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/webviewerconverttest" class="button primary">View source code</a>

### Optimizer

The sample illustrates how to reduce PDF file size using 'pdftron.PDF.Optimizer'. It also shows how to simplify and optimize PDF documents for viewing on mobile devices and on the Web using 'pdftron.PDF.Flattener'.

<a href="https://sdk.apryse.com/samples/web/samples/full-apis/OptimizerTest/" class="button primary">Launch demo</a><a href="/web/get-started/samples/optimizertest" class="button primary">View source code</a>


# Project integration samples

Explore Github repositories showcasing how to integrate WebViewer into React, Angular, Vue, NuxtJS, Svelte, Electron, and Salesforce Lightning Web Component projects.

## React

Github repo that demonstrates how to integrate WebViewer into a [React](https://reactjs.org/) project.

<a href="https://github.com/ApryseSDK/webviewer-react-sample/" class="button primary">View source code on Github</a>

## Angular

Github repo that demonstrates how to integrate WebViewer into an [Angular](https://angularjs.org/) project.

<a href="https://github.com/ApryseSDK/webviewer-angular-sample/" class="button primary">View source code on Github</a>

## Vue

Github repo that demonstrates how to integrate WebViewer into [Vue](https://vuejs.org/) project.

<a href="https://github.com/ApryseSDK/webviewer-vue-sample/" class="button primary">View source code on Github</a>

## NuxtJS

Github repo that demonstrates how to integrate WebViewer into [NuxtJS](https://nuxtjs.org/) project.

<a href="https://github.com/ApryseSDK/webviewer-nuxtjs-sample/" class="button primary">View source code on Github</a>

## Svelte

Github repo that demonstrates how to integrate WebViewer into [Svelte](https://svelte.dev/) project.

<a href="https://github.com/ApryseSDK/webviewer-svelte-sample/" class="button primary">View source code on Github</a>

## Electron

Github repo that demonstrates how to integrate WebViewer into an [Electron](https://electronjs.org/) project.

<a href="https://github.com/ApryseSDK/webviewer-electron-sample/" class="button primary">View source code on Github</a>

## Salesforce

Github repo that demonstrates how to integrate WebViewer into [Salesforce as a Lightning Web Component](https://trailhead.salesforce.com/en/content/learn/projects/quick-start-lightning-web-components/).

<a href="https://github.com/ApryseSDK/webviewer-salesforce/" class="button primary">View source code on Github</a>


# Samples to customize PDF Forms with JavaScript

Discover how to add form fields to a PDF with this Form Builder Demo. Learn to customize signatures, text fields, and checkboxes. Change color and opacity easily. The Apryse Web SDK streamlines secure

## Form Builder Demo

This sample demonstrates how to add form fields to a PDF. Fields are first added as annotations and then converted to interactive form fields. Add signatures, text fields, checkboxes.

<a href="https://sdk.apryse.com/samples/web/samples/forms/form-builder/" class="button primary">Launch demo</a><a href="/web/get-started/samples/form-builder" class="button primary">View source code</a>

## Customizing form fields

Shows how to change color and opacity of form fields.

<a href="https://sdk.apryse.com/samples/web/samples/forms/form-fields/" class="button primary">Launch demo</a><a href="/web/get-started/samples/form-fields" class="button primary">View source code</a>


# PDF manipulation samples in JavaScript

Learn how to use thumbnail controls, perform page operations, assemble documents from multiple viewers, separate layers, and colors for enhanced document manipulation. Ideal for client-side PDF and Of

## Thumbnail controls

Shows how to use thumbnail controls for page manipulation of documents. Note that this is only enabled for client side PDF and Office documents.

<a href="https://sdk.apryse.com/samples/web/samples/pdf-manipulation/thumbnail-controls/" class="button primary">Launch demo</a><a href="/web/get-started/samples/thumbnail-controls" class="button primary">View source code</a>

## Page operations

Shows how to rotate/crop/add/delete page, change page order, insert blank pages and merge multiple documents.

<a href="https://sdk.apryse.com/samples/web/samples/pdf-manipulation/page-operations/" class="button primary">Launch demo</a><a href="/web/get-started/samples/page-operations" class="button primary">View source code</a>

## Document assembly from multiple viewers

Shows how to implement a dropzone to be able to drag and drop pages from multiple viewer instances.

<a href="https://github.com/ApryseSDK/webviewer-document-merge/" class="button primary">View source code</a>

## Layer separation

Shows how to get each layer's information. You can also show/hide specific layers.

<a href="https://sdk.apryse.com/samples/web/samples/pdf-manipulation/layer-separation/" class="button primary">Launch demo</a><a href="/web/get-started/samples/layer-separation" class="button primary">View source code</a>

## Color separation

Shows how to get each color's information. You can also show/hide specific colors.

<a href="https://sdk.apryse.com/samples/web/samples/pdf-manipulation/color-separation/" class="button primary">Launch demo</a><a href="/web/get-started/samples/color-separation" class="button primary">View source code</a>


# Annotation code samples in JavaScript

Discover how to manage users and permissions, annotate HTML web pages, and enable real-time collaboration using Node.js and Firebase backends. Learn about custom annotations in this comprehensive guid

## Users and permissions

Describes types of permissions and switching between users. You can also show/hide annotations of specific users.

<a href="https://sdk.apryse.com/samples/web/samples/annotation/users-and-permissions/" class="button primary">Launch demo</a><a href="/web/get-started/samples/users-and-permissions" class="button primary">View source code</a>

## Annotate HTML web pages

In this example, you are able to pass any URL and be able to save a copy of that website to annotate.

<a href="https://github.com/ApryseSDK/webviewer-html-annotate/" class="button primary">View source code on Github</a>

## Realtime collaboration - Node.js

An example setup with Node.js backend.

<a href="https://github.com/ApryseSDK/webviewer-realtime-collaboration-sqlite3-sample/" class="button primary">View source code on Github</a>

## Realtime collaboration - Firebase

An example setup with Firebase backend. For more details, refer to [realtime collaboration guide](/web/collaboration/realtime-collaboration).

<a href="https://sdk.apryse.com/samples/web/samples/annotation/realtime-collaboration/" class="button primary">Launch demo</a><a href="/web/get-started/samples/realtime-collaboration" class="button primary">View source code</a>

## Custom annotations

Shows how to create and add custom annotations by extending the existing annotation classes.

<a href="https://sdk.apryse.com/samples/web/samples/annotation/custom-annotations/" class="button primary">Launch demo</a><a href="/web/get-started/samples/custom-annotations" class="button primary">View source code</a>


# Textdiff

{% content-ref url="/pages/716grgK5gx4FWqVrnDsa" %}
[Semantic Compare using JavaScript](/web/get-started/guides/textdiff/textdiff)
{% endcontent-ref %}


# Semantic Compare using JavaScript

Compare PDF documents easily with Apryse's semantic comparison feature. Highlight textual differences between two versions with colored annotations. Learn more about this efficient tool for document c

## Overview

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="https://apryse.com/capabilities#Compare" class="button primary">Package: Compare</a><a href="https://showcase.apryse.com/semantic-text-compare" class="button primary">Live demo</a>
{% endhint %}

Apryse's semantic comparison feature enables the visualization of textual differences between two related PDF documents. The text processing is based on natural reading order, highlighting the differences as colored annotations.

The comparison is always performed between two versions of a document. The older version is called the Before file (document 1), while the new version is the After file (document 2).

A difference is defined as a consecutive block of text that was inserted, deleted or modified. Differences always come in pairs. When some text is deleted from the Before side, a corresponding placeholder is inserted into the After side to indicate the position where the text was deleted from.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-6795a048edd227f85b594d29ad9e836f8d802b0f%2Fbbfdbdff203933c766216d5010e6a1e45cb45de0-728x162.png?alt=media)

Similarly, when some text is inserted into the After side, a corresponding placeholder is generated for the Before side to help identify its location of insertion.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-891de76bed8aad2b9f68da4701f2177e2778b974%2F27636d97775cc1aaca6c4f9cadc7b5ff78227ca3-603x155.png?alt=media)

Finally, when content is modified, the difference comes out as a pair of annotations consisting of a deletion on the Before side and an insertion on the After side.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-516bc91125abe0d848a6719695726600f7ca1d77%2F5dcca28499ef08a088ea3ef6fcfad6da4164e884-908x184.png?alt=media)

These difference annotations are labeled with a unique identifier, so they can be paired up side-by-side at the application level. We'll learn more about this later.

When entire lines are inserted or deleted, the placeholder at the opposite end is pictured as a horizontal line.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-0f98a32927dfcfdb23c917ef74ce50db68a29e17%2F8964cdd576f182a259155748f03ef19661ed8833-801x192.png?alt=media)

## Usage

The `HighlightTextDiff` method takes two PDF documents as input, one being the Before (1) and the other one being the After (2) document. It compares them to find any differences, then overlays the highlight annotations on top of the input documents, which can in turn be saved to files.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
    // Start with a PDFDoc (open source documents to compare)
    const doc1 = await PDFNet.PDFDoc.createFromURL("compare_before.pdf");
    const doc2 = await PDFNet.PDFDoc.createFromURL("compare_after.pdf");

    // Create an options object
    const options = await PDFNet.PDFDoc.createTextDiffOptions();

    // Compare and highlight text differences in doc1 and doc2
    await doc1.highlightTextDiff(doc2, options);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Note that `HighlightTextDiff` is a static method within `PDFDoc`. The method returns the number of differences found, where each contiguous block of change is considered a single difference. If the two documents are identical, the function returns 0 and no annotations are added.

When the input PDFs already contain annotations or widgets, they are first flattened. When the function finishes, all annotations represent actual differences of text.

## Facing Pages

It is easy to see that the more words and paragraphs you keep inserting into the document, the longer the After version will become compared to the Before PDF. You can reach a point where page N in Before is no longer matching up with page N in After. If you were to display Before and After next to each other, you could start displaying unrelated content, and that can be quite confusing.

Yet in certain situations, you can assume that pages generally line up between Before and After, especially with short documents with few changes between them.

We actually offer two separate APIs, `AppendTextDiff` and `HighlightTextDiff`. Depending on the situation, one might be preferable to the other.

Consider that you have two versions of an input document, Before (yellow paper) and After (cyan paper):

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-9eca0b62b0a4e71f3e6cb7a7ff20221256be6006%2F9a055a33fec8f85d0b67586b00088b4f373f7c97-590x596.png?alt=media)

The `AppendTextDiff` function generates a single output document where the Before and After pages are merged in an alternating order (page 1 of each, followed by page 2 of each, and so on). In this case, you are only saving a single output file, and you can use a single WebViewer in Double Page mode (also known as Two Page View in Acrobat), so that the same page numbers from the two versions are next to each other.

This is most suitable when you know that Before and After have approximately the same number of pages and the differences are on the small side. When one document is longer than the other, blank filler pages are automatically inserted, so that the last few pages will have a blank pair.

The advantage is that it's easy to use a single WebViewer and switch it into Double Page view, and a single output file contains both versions in a compact format. However, the Before and After sides can't be scrolled independently, and the left and right pages could get out of sync very soon.

The `HighlightTextDiff` function treats the two inputs independently, and inserts the highlights directly into the Before and After documents. In this case, you are saving two output files that require two WebViewer controls side-by-side. This way the two documents can scroll independently, so that insertions and deletions line up perfectly, even when the page numbers are way out of sync.

We've already seen sample code for `HighlightTextDiff`, here are some samples for `AppendTextDiff`:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
    // Start with a PDFDoc (open source documents to compare)
    const output = await PDFNet.PDFDoc.create();
    const doc1 = await PDFNet.PDFDoc.createFromURL("compare_before.pdf");
    const doc2 = await PDFNet.PDFDoc.createFromURL("compare_after.pdf");

    // Create an options object
    const options = await PDFNet.PDFDoc.createTextDiffOptions();

    // Compare and highlight text differences in doc1 and doc2
    await output.appendTextDiff(doc1, doc2, options);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Highlight Colors

Designers will be happy to learn that the Before and After annotation colors are customizable. Both the RGB value and the opacity can be adjusted. An opacity of 1.0 (100% opaque) can be too dark in combination with certain colors, in which case 0.5 (50% semi-transparent) may work better. An opacity value of 0.0 (full transparency) is completely invisible, so it makes sense to stay above values of 0.15.

The colors can be configured via the TextDiffOptions object. SetColorA and SetOpacityA control the Before document's annotation color. SetColorB and SetOpacityB adjust the After document's annotation color. Finally, the options object is passed to HighlightTextDiff as the third argument.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
    // Create an options object
    const options = await PDFNet.PDFDoc.createTextDiffOptions();

    // Before color is 100% red, 25% opacity
    options.setColorA({R: 1, G: 0, B: 0});
    options.setOpacityA(0.25);

    // After color is 100% blue, 25% opacity
    options.setColorB({R: 0, G: 0, B: 1});
    options.setOpacityB(0.25);

    // Compare and highlight text differences
    await doc1.highlightTextDiff(doc2, options);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Exclusion Zones

Sometimes the need arises to exclude certain areas from text differencing. Most typically, headers and footers can disrupt the flow of the logical content, which often shows up as fake differences. Another example may be an advertisement that should not be a part of the actual content, either.

For those reasons the semantic comparison API offers a way of setting up exclusion zones where text is not considered for comparison, so any differences will be ignored.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
async function main() {
    // Create an options object
    const options = await PDFNet.PDFDoc.createTextDiffOptions();

    // Exclude footer area from page 1
    const exclusion = [{ x1: 0, y1: 0, x2: 612, y2: 72 }];
    options.addIgnoreZonesForPage(exclusion, 1);

    // Compare and highlight text differences
    await doc1.highlightTextDiff(doc2, options);
}
PDFNet.runWithCleanup(main);
```

{% endcode %}
{% endtab %}
{% endtabs %}

Note that rectangles use PDF coordinates (measured in points, 1 point = 1/72 inch; origin is the page's bottom-left corner).

## Annotation Metadata

We mentioned earlier that differences always come in pairs, and each carries a unique numeric identifier starting at the number 1 by increments of 1. Highlight annotations sharing the same identifier in both documents correspond to each other.

In addition, each annotation also carries information about the type of difference it represents, which can be either `insertion`, `deletion` or `edit`.

Usually each difference is represented by a single annotation object, which may consist of one or more rectangles. However, in certain situations an insertion or deletion may wrap across page boundaries. In those cases a single difference can consist of more than one highlight annotation, one per page, all instances sharing the same identifier and difference type.

The identifier and type information are stored as metadata within the annotation object under two custom keys:

* TextDiffID: a unique number shared between the two PDF documents.
* TextDiffType: may be either `insert`, `delete` or `edit`.

The easiest way to retrieve this information is via the `Annot.GetCustomData` method:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
// Get page 1
const page1 = await doc1.getPage(1);
// Get first annotation
const annot1 = await page1.getAnnot(0);
// Get custom data TextDiffID
const id = await annot1.getCustomData("TextDiffID");
// Get custom data TextDiffType
const type = await annot1.getCustomData("TextDiffType");
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
// Get page 1
PTPage *page1 = [doc1 GetPage: 1];
// Get first annotation
PTAnnot* annot1 = [page1 GetAnnot: 0];
// Get custom data TextDiffID
NSString *id = [annot1 GetCustomData: @"TextDiffID"];
// Get custom data TextDiffType
NSString *type = [annot1 GetCustomData: @"TextDiffType"];
```

{% endcode %}
{% endtab %}
{% endtabs %}

Note that the identifier comes out as a string, but it can be interpreted as a number.


# WVS

{% content-ref url="/pages/8zhp0Fupu1pv0bz7LAGJ" %}
[Changing the HTTP data directory](/web/get-started/guides/wvs/changing-the-http-data-directory)
{% endcontent-ref %}


# Changing the HTTP data directory

Learn how to configure WebViewer Server to write cache to a networked drive on Windows. Follow step-by-step instructions for setting up a static\_data directory and ensuring proper permissions for seam

1. Create a `static_data` directory in your new data location.

1a. **Windows only & Docker <=1.5.6** Modify the file at `WEBVIEWER_INSTALL/apache-tomcat/conf/server.xml` and look Context docBase argument for `/data`, replace it with this.

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```sh
<Context docBase="[YOUR NEW DATA PATH]:/static_data" path="/data" />
```

{% endcode %}
{% endtab %}
{% endtabs %}

1. Modify the file at `WEBVIEWER_INSTALL/apache-tomcat/wvs_config.json`, change the next line into the following line:`"TRN_DATA_STORAGE_DIR":"WEBVIEWER_INSTALL/apache-tomcat/static_data",`
2. Start WebViewer Server, it should now write its cache to your networked drive.

### Using a networked drive

To use a networked drive on Windows, follow the same steps as above except specify your network path.

When using WVS with the Windows installer, it is possible to run its cache on a shared network drive. There can be many complications and considerations when using a network drive. The following are some things you should consider.

* Is the networked drive fast enough to keep WebViewer Server performant?
* Does the network drive maintain good cache consistency? A common symptom of this issue is WebViewer Server being unable to find files it just wrote when making a call on one server and accessing the result with another
* Does the network drive have correct permissions?

### My service won't startup after moving WebViewer Server to a networked drive

This is because the System user that WVS is running as does not have access to the network drive map. We must force the drive to be mapped on system startup so that the System user has access to it. The following steps detail that process. It may be possible to change the service user and get the mapped drives working, however, this is the only way we were able to get the networked drive to work with a service.

1. Write a batch file that can be used to mount your networked drive on startup.

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
echo %time% >> c:\mount_nfs_log.txt
net use Z: \\{your ip}\{netdisk folder} /USER:username password >> C:\mount_nfs_log.txt 2>&1
```

{% endcode %}
{% endtab %}
{% endtabs %}

1. Open your Task Scheduler and create a new task. This task should run as the NT SYSTEM user. The trigger should be system startup and the action should call the batch script you created in the previous steps.
2. Restart your system, when WebViewer Server next starts, it will have access to this drive.


# Build a JavaScript PDF viewer with Apryse WebViewer SDK and npm

Get started with the Apryse WebViewer SDK. Install a Java PDF viewer in a vanilla JavaScript app using Node Package Manager (npm) without a bundler, then render your first document in the WebViewer UI

This guide demonstrates how to integrate the Apryse [WebViewer SDK](/web/what-is-webviewer/overview) into a vanilla JavaScript application using Node Package Manager (npm), without a bundler. You’ll install the SDK, serve its assets, and load it directly in the browser using a script tag. The examples use a simple HTML‑based project to focus on the core integration steps. By the end, you’ll initialize WebViewer and render a PDF document in the UI.

You can also download our ready-to-use [GitHub samples](https://github.com/ApryseSDK/webviewer-samples) to get started quickly, or explore the interactive [Showcase demo](https://showcase.apryse.com/) to see WebViewer's full capabilities in action.

## Prerequisites

Before you start:

* Install [Node.js](https://nodejs.org/en/download) and npm. We recommend using the latest active LTS release.
* Install [Visual Studio Code](https://code.visualstudio.com/Download) or another code editor to develop and debug your code.
* Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## 1. Create your project

Set up your project by creating a folder and preparing a workspace for your WebViewer application.

1. In your terminal, go to the directory where you want to create the project.
2. Create a new project folder and move to it so you can start working with it:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
mkdir webviewer-npm-integration
cd webviewer-npm-integration
```

{% endcode %}
{% endtab %}
{% endtabs %}

3. Initialize your project and generate a default `package.json` file:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm init -y
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 2. Install WebViewer

Next, install the Apryse WebViewer SDK using npm. This command adds the [WebViewer package](https://www.npmjs.com/package/@pdftron/webviewer) to your project, allowing you to integrate the PDF viewer and editor into your application.

After navigating to your `webviewer-npm-integration` project directory, run the following command to install WebViewer:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm i @pdftron/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 3. Copy WebViewer assets

WebViewer needs access to its static assets at runtime, including WebAssembly modules, HTML, and CSS files. You must copy these assets into the `public` directory so they can be served correctly. For more, see [Copying WebViewer static assets](/web/get-started/copy-assets).

1. From your project root, create the `public/lib/webviewer` directory if it doesn't already exist:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx mkdir -p public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Copy all WebViewer static assets from `node_modules/@pdftron/webviewer/public` into the new `public/lib/webviewer` directory:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes cpy-cli "node_modules/@pdftron/webviewer/public/**/*" public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

3. Copy the `webviewer.min.js` library file from `node_modules` to the `public` directory:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes cpy-cli node_modules/@pdftron/webviewer/webviewer.min.js public/lib/webviewer --flat 
```

{% endcode %}
{% endtab %}
{% endtabs %}

Your project should now include a similar structure:

{% code lineNumbers="true" %}

```
webviewer-npm-integration/
├── node_modules/
│   └── @pdftron/
│       └── webviewer/
├── public/
│   └── lib/
│       └── webviewer/
│           ├── core/
│           ├── ui/
│           └── webviewer.min.js
├── package.json
└── package-lock.json
```

{% endcode %}

{% hint style="info" %}
**Info**

You can optionally [automate asset copying](/web/get-started/copy-assets). This is recommended for larger projects. It ensures the WebViewer runtime files are updated consistently as part of your build process.
{% endhint %}

## 4. Create PDF viewer

In this section, you'll create an HTML page for your WebViewer application, add the WebViewer script, define a container for the viewer, and initialize WebViewer so it can load and display a document.

1. Open the `webviewer-npm-integration` folder in Visual Studio Code.
2. Create a new `index.html` file in the `webviewer-npm-integration/public` folder.
3. Add the following HTML to the `index.html` file:

{% tabs %}
{% tab title="HTML" %}
{% code title="index.html" lineNumbers="true" %}

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Basic WebViewer</title>
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
    />
    
    
    <!-- Import WebViewer as a script
         The src path must point to the webviewer.min.js file
         inside your project's WebViewer/lib folder -->
    <script src="/lib/webviewer/webviewer.min.js"></script>
  </head>

  <body>
    <!-- Container where WebViewer is rendered -->
    <div id="viewer" style="width: 100%; height: 100vh; margin: 0 auto;"></div>
  </body>
</html>
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

This guide uses a `<script>` tag to load WebViewer. To learn about other approaches, see framework compatibility and [Importing](/web/get-started/libraries-and-frameworks/frameworks#importing) sections.
{% endhint %}

4. Add the following JavaScript to your `index.html` file, placing it after the `<div id="viewer">` element and before the closing `</body>` tag:

{% tabs %}
{% tab title="Web Component" %}

<pre class="language-js" data-line-numbers><code class="lang-js">&#x3C;script>
  WebViewer({
    // Add path to Apryse 'lib' folder on your server
    path: '/lib/webviewer',
    // Replace with your license key, key signup at https://dev.apryse.com
    licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
    // Specify an initial document to load on startup
    initialDoc: 'https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf',
    // You can also use documents on your server
    // initialDoc: '/path/to/my/file.pdf',
  // Provide id of HTML element where WebViewer should be mounted
  }, document.getElementById('viewer'))
  .then(instance => {
    const { documentViewer, annotationManager } = instance.Core;

    // Call methods from instance, documentViewer, and annotationManager as needed

    // You can also access major namespaces from the instance as follows:
    // const Tools = instance.Core.Tools;
    // const Annotations = instance.Core.Annotations;
 
    documentViewer.addEventListener('documentLoaded', () => {
      // Call methods relating to the loaded document
    });
  });
&#x3C;/script>
</code></pre>

[WebViewer()](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor) [WebComponent()](https://sdk.apryse.com/api/web/global.html?#WebComponent__anchor) [WebViewerOptions](https://sdk.apryse.com/api/web/global.html#WebViewerOptions__anchor) [DocumentViewer.documentLoaded](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:documentLoaded)
{% endtab %}

{% tab title="Iframe" %}

<pre class="language-js" data-line-numbers><code class="lang-js">&#x3C;script>
  // Uses iframe-based WebViewer, which was default through version 10

  WebViewer.Iframe({
    // Add path to Apryse 'lib' folder on your server
    path: '/lib/webviewer',
    // Replace with your license key, key signup at https://dev.apryse.com
    licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
// Provide id of HTML element where WebViewer should be mounted
}, document.getElementById('viewer'))
  .then(instance => {
    const { UI, Core } = instance;
    const { documentViewer, annotationManager, Tools, Annotations } = Core;

    // Call methods from UI, Core, instance, documentViewer, and annotationManager as needed

    documentViewer.addEventListener('documentLoaded', () => {
      // Call methods relating to loaded document
    });

    instance.UI.loadDocument('https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf');
  });
&#x3C;/script>
</code></pre>

[WebViewer()](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor) [WebComponent()](https://sdk.apryse.com/api/web/global.html?#WebComponent__anchor) [WebViewerOptions](https://sdk.apryse.com/api/web/global.html#WebViewerOptions__anchor) [DocumentViewer.documentLoaded](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:documentLoaded) [UI.loadDocument()](https://sdk.apryse.com/api/web/UI.html#.loadDocument)
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

* If you're signed in with an Apryse account, your license key is automatically prepopulated in all code snippets.
* Starting in version 11, WebViewer instantiates using a web component instead of an iframe by default; however, both methods are available. See [this guide](/web/ui-customization/web-component-vs-iframe) for more information.
  {% endhint %}

5. Save the `index.html` file.

## 5. Verify your output

Once your project files are in place, serve the webpage so that WebViewer can load its UI and display the PDF you added as the initial document in `index.html`. We use [http-server](https://www.npmjs.com/package/http-server) to preview the page locally in your browser.

1. From your project directory, run the following command to start a local web server:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx http-server -a localhost
```

{% endcode %}
{% endtab %}
{% endtabs %}

If prompted, press `y` to install `http-server`. A successful output looks similar to:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
Starting up http-server, serving ./public

http-server version: 14.1.1

http-server settings: 
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none

Available on:
  http://localhost:8080
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Open the localhost URL from your terminal to view the WebViewer UI and PDF document.

## Get started video

In this 4-minute video, learn how to install WebViewer in a simple HTML-based project using npm.

{% embed url="<https://www.youtube.com/embed/siJASVtD-S0?si=zNmu6tLv9dP6yJyf>" %}
Install the Apryse WebViewer SDK in an HTML-based project using npm.
{% endembed %}

## Next Steps

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# Build a JavaScript PDF viewer with Apryse WebViewer SDK using a manual setup

Get started with the Apryse WebViewer SDK. Download and manually install a PDF viewer and editor in a vanilla JavaScript app without using a package manager, then render your first document in the Web

This guide shows how to manually integrate the Apryse [WebViewer SDK](/web/what-is-webviewer/overview) into a web project without using a package manager or bundler. The approach is useful when you need full control over asset hosting, or when [npm](/web/get-started/npm) or CDN-based setups aren’t an option. The examples use a simple HTML‑based project to focus on the core integration steps. By the end, you’ll successfully initialize WebViewer and render a PDF document in the UI.

You can also download our ready-to-use [GitHub samples](https://github.com/ApryseSDK/webviewer-samples) to get started quickly, or explore the interactive [Showcase demo](https://showcase.apryse.com/) to see WebViewer's full capabilities in action.

## Prerequisites

Before you start:

* Install [Node.js](https://nodejs.org/en/download) and npm. We recommend using the latest active LTS release.
* Install [Visual Studio Code](https://code.visualstudio.com/Download) or another code editor to develop and debug your code.
* Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## 1. Create your project

Set up your project by creating a folder and preparing a workspace for your WebViewer application.

1. In your terminal, go to the directory where you want to create the project.
2. Create a new project folder and move to it so you can start working with it:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
mkdir webviewer-manual-integration
cd webviewer-manual-integration
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 2. Download WebViewer

In this step, you’ll manually download the WebViewer package and add it to your project.

1. Download the [WebViewer package](https://downloads.apryse.com/downloads/WebViewer.zip), which includes the library files, samples, and documentation.
2. Extract the downloaded `WebViewer.zip` file into the `webviewer-manual-integration` project folder. Your project structure should look similar to this:

{% code lineNumbers="true" %}

```
webviewer-manual-integration/
└── WebViewer/
    ├── doc/
    ├── lib/
    ├── licenses/
    ├── samples/
    ├── scripts/
    ├── package.json
    └── server.js
```

{% endcode %}

## 3. Create the PDF viewer

In this section, you'll create an HTML page for your WebViewer application, add the WebViewer script, define a container for the viewer, and initialize WebViewer so it can load and display a document.

1. Open the `webviewer-manual-integration` folder in Visual Studio Code.
2. Create a new `index.html` file in the `webviewer-manual-integration` folder.
3. Add the following HTML to the `index.html` file:

{% tabs %}
{% tab title="HTML" %}
{% code title="index.html" lineNumbers="true" %}

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Basic WebViewer</title>
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
    />
    
    <!-- Import WebViewer as a script
         The src path must point to the webviewer.min.js file
         inside your project's WebViewer/lib folder -->
    <script src="WebViewer/lib/webviewer.min.js"></script>
  </head>

  <body>
    <!-- Container where WebViewer is rendered -->
    <div id="viewer" style="width: 100%; height: 100vh; margin: 0 auto;"></div>
  </body>
</html>
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

This guide uses a `<script>` tag to load WebViewer. If you install the SDK manually, you can also use a hybrid approach and import WebViewer using ES module syntax with a bundler. For more, see [Importing](/web/get-started/libraries-and-frameworks/frameworks#importing).
{% endhint %}

4. Add the following JavaScript to your `index.html` file, placing it after the `<div id="viewer">` element and before the closing `</body>` tag:

{% tabs %}
{% tab title="Web Component" %}

<pre class="language-js" data-line-numbers><code class="lang-js">&#x3C;script>
  WebViewer({
    // Add path to Apryse 'lib' folder on your server
    path: 'WebViewer/lib',
    // Replace with your license key, key signup at https://dev.apryse.com
    licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
    // Specify an initial document to load on startup
    initialDoc: 'https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf',
    // You can also use documents on your server
    // initialDoc: '/path/to/my/file.pdf',
  // Provide id of HTML element where WebViewer should be mounted
  }, document.getElementById('viewer'))
  .then(instance => {
    const { documentViewer, annotationManager } = instance.Core;

    // Call methods from instance, documentViewer, and annotationManager as needed

    // You can also access major namespaces from the instance as follows:
    // const Tools = instance.Core.Tools;
    // const Annotations = instance.Core.Annotations;

    documentViewer.addEventListener('documentLoaded', () => {
      // Call methods relating to the loaded document
    });
  });
&#x3C;/script>
</code></pre>

[WebViewer()](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor) [WebComponent()](https://sdk.apryse.com/api/web/global.html?#WebComponent__anchor) [WebViewerOptions](https://sdk.apryse.com/api/web/global.html#WebViewerOptions__anchor) [DocumentViewer.documentLoaded](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:documentLoaded)
{% endtab %}

{% tab title="Iframe" %}

<pre class="language-js" data-line-numbers><code class="lang-js">&#x3C;script>
  // Uses iframe-based WebViewer, which was default through version 10

  WebViewer.Iframe({
    // Add path to Apryse 'lib' folder on your server
    path: 'WebViewer/lib',
    // Replace with your license key, key signup at https://dev.apryse.com
    licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
  // Provide id of HTML element where WebViewer should be mounted  
  }, document.getElementById('viewer'))
  .then(instance => {
    const { UI, Core } = instance;
    const { documentViewer, annotationManager, Tools, Annotations } = Core;
    
    // Call methods from UI, Core, instance, documentViewer, and annotationManager as needed

    documentViewer.addEventListener('documentLoaded', () => {
      // Call methods relating to loaded document
    });
    
    instance.UI.loadDocument('https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf');
  });
&#x3C;/script>
</code></pre>

[WebViewer()](https://sdk.apryse.com/api/web/global.html#WebViewer__anchor) [WebComponent()](https://sdk.apryse.com/api/web/global.html?#WebComponent__anchor) [WebViewerOptions](https://sdk.apryse.com/api/web/global.html#WebViewerOptions__anchor) [DocumentViewer.documentLoaded](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:documentLoaded) [UI.loadDocument()](https://sdk.apryse.com/api/web/UI.html#.loadDocument__anchor)
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

* If you're signed in with an Apryse account, your license key is automatically prepopulated in all code snippets.
* Starting in version 11, WebViewer instantiates using a web component by default instead of an iframe. Both methods are still supported. See [this guide](/web/ui-customization/web-component-vs-iframe) for more information.
  {% endhint %}

5. Save the `index.html` file.

## 4. Verify your output

Once your project files are in place, serve the webpage so that WebViewer can load its UI and display the PDF you added as the initial document in `index.html`. We use [http-server](https://www.npmjs.com/package/http-server) to preview the page locally in your browser.

1. From your project directory, run the following command to start a local web server:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx http-server -a localhost
```

{% endcode %}
{% endtab %}
{% endtabs %}

If prompted, press `y` to install `http-server`. A successful output looks similar to:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
Starting up http-server, serving ./public

http-server version: 14.1.1

http-server settings: 
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none

Available on:
  http://localhost:8080
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Open the localhost URL from your terminal to view the WebViewer UI and PDF document.

## Get started video

In this 5-minute video, learn how to manually install WebViewer in a simple HTML-based project without using npm or a package manager.

{% embed url="<https://www.youtube.com/embed/QeU_gEupy5A?si=4tsnDO9SlzMjvwMw>" %}
Manual installation showing how to integrate WebViewer without a package manager.
{% endembed %}

## Next steps

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# Build UI-free document processing workflows with Apryse WebViewer SDK

Use the Apryse WebViewer SDK to process, convert, and edit PDFs without the WebViewer UI. Build headless, programmatic workflows for data extraction, page operations, and document automation.

This guide shows how to use the Apryse [WebViewer SDK](https://apryse.com/products/webviewer) for headless, programmatic document processing without rendering the WebViewer UI. This approach is useful when you need to process, convert, or inspect documents directly in your application, or build automated document workflows.

The examples use a simple HTML‑based project to focus on core SDK functionality without UI components. By the end, you’ll be able to:

* Count the number of pages in a PDF.
* Convert a DOCX file to PDF and automatically download it.

## Prerequisites

Before you start:

* Install [Node.js](https://nodejs.org/en/download) and npm. We recommend using the latest active LTS release.
* Install [Visual Studio Code](https://code.visualstudio.com/Download) or another code editor to develop and debug your code.
* Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## 1. Create your project

Set up your project by creating a folder and preparing a workspace for building document‑processing workflows with the Apryse WebViewer SDK.

1. In your terminal, go to the directory where you want to create the project.
2. Create a new project folder and move to it so you can start working with it:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
mkdir webviewer-no-ui
cd webviewer-no-ui
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 2. Download WebViewer

In this step, you’ll manually download the WebViewer package and add it to your project.

1. Download the [WebViewer package](https://downloads.apryse.com/downloads/WebViewer.zip), which includes the library files, samples, and documentation.
2. Extract the downloaded `WebViewer.zip` file into the `webviewer-no-ui` project folder. Your project structure should look similar to this:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
webviewer-no-ui/
└── WebViewer/
    ├── doc/
    ├── lib/
    ├── licenses/
    ├── samples/
    ├── scripts/
    ├── package.json
    └── server.js
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 3. Create the PDF viewer

In this section, you’ll add a minimal HTML structure and the JavaScript required to run document processing tasks with the Apryse WebViewer SDK. In this headless setup, only the core SDK scripts are loaded, and document workflows are handled entirely through code rather than a full viewer UI.

1. Open the `webviewer-no-ui` folder in Visual Studio Code.
2. Create a new `index.html` file in the `webviewer-no-ui` folder.
3. Add the following HTML to the `index.html` file:

{% tabs %}
{% tab title="HTML" %}
{% code title="index.html" lineNumbers="true" %}

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Basic WebViewer</title>
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
    />
    
    <!-- Load WebViewer Core SDK (no UI components) -->
    <script src="./WebViewer/lib/core/webviewer-core.min.js"></script>
    <!-- Use PDFNetLean.js when advanced PDFNet features aren't required -->
    <!-- <script src="./lib/core/pdf/PDFNetLean.js"></script> -->
    <script src="./WebViewer/lib/core/pdf/PDFNet.js"></script>
  </head>

  <body>
    <h1>Working without a viewer</h1>
    <p>Add query params as a verb and the URL of the file to be processed</p>
    <!-- Example query parameters for document processing -->
    <!-- Use to trigger and test each workflow -->
    <ul>
      <li>Count pages: ?countpages=https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf</li>
      <li>Convert to PDF: ?convert=https://pdftron.s3.amazonaws.com/downloads/pl/report.docx</li>
    </ul>
  </body>

</html>
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

WebViewer Core provides the foundation for WebViewer functionality. In a typical UI setup, you would include the `webviewer.min.js` script. In a headless (no‑UI) scenario, you only need to load the core libraries: `webviewer-core.min.js` and `PDFNet.js`.

If you don’t require advanced PDFNet functionality, you can use the lighter `PDFNetLean.js` script instead. It has a smaller footprint, loads faster, and includes the essential PDFNet features without requiring the full API. For this guide, we use the full `PDFNet.js` library for consistency, though it's not required.
{% endhint %}

4. Add the following JavaScript to your `index.html` file, placing it before the closing `</body>` tag:

{% tabs %}
{% tab title="JavaScript" %}

<pre class="language-js" data-title="index.html" data-line-numbers><code class="lang-js">&#x3C;script>
  // Set path to WebViewer Core worker files
  // Allow PDFNet to load Web Worker scripts for document processing
  Core.setWorkerPath('./WebViewer/lib/core');

  // Create entry point for all no viewer SDK operations
  async function main() {
    // Get current page URL and extract query parameters
    const currentPageURL = new URL(window.location.href);
    const params = new URLSearchParams(currentPageURL.search);

    // Check for 'countpages' parameter
    // Load PDF from URL, create PDFDoc instance
    // Retrieve and display page count
    if (params.has('countpages')) {
      const inputURL = params.get('countpages');
      const doc = await Core.PDFNet.PDFDoc.createFromURL(inputURL);
      doc.initSecurityHandler();
      // Lock document to ensure safe operations
      doc.lock();
      const pageCount = await doc.getPageCount();
      alert(`Your file has ${pageCount} pages`);
    }

   // Check for 'convert' parameter
   // Convert DOCX from URL to PDF using office2PDF
   // Return PDFDoc object and download it
    else if (params.has('convert')) {
      const inputURL = params.get('convert');

      Core.PDFNet.Convert.office2PDF(inputURL).then(async (outPdfDoc) => {
        const pageCount = await outPdfDoc.getPageCount();
        console.log(`Converted DOCX to PDF with ${pageCount} pages`);
        const outputPath = 'output.pdf';

        // Export PDF as blob for download
        // Refactor into utility function if preferred
        const pdfData = await outPdfDoc.saveMemoryBuffer(Core.PDFNet.SDFDoc.SaveOptions.e_linearized);
        const blob = new Blob([pdfData], {type: 'application/pdf'});

        // Trigger download
        const a = document.createElement('a');
        a.href = URL.createObjectURL(blob);
        a.download = outputPath;
        a.click();
      });
    }
  }

  // Initialize PDFNet and run main function
  // Replace with your Apryse license key
  Core.PDFNet.runWithCleanup(main, '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>');
&#x3C;/script>
</code></pre>

**Related links**

* [Core.setWorkerPath()](https://sdk.apryse.com/api/web/Core.html#.setWorkerPath)
* [Core.PDFNet.PDFDoc](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html?#main)
* [PDFNet.runWithCleanup()](https://sdk.apryse.com/api/web/Core.PDFNet.html#.runWithCleanup)
* [PDFDoc.createFromURL()](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html?#.createFromURL__anchor)
  {% endtab %}
  {% endtabs %}

5. Save the `index.html` file.

## 4. Verify your output

Once your project files are in place, serve the webpage so that the Apryse WebViewer SDK can load and run document-processing tasks in the browser. This allows you to verify that your setup is working correctly. We use [http-server](https://www.npmjs.com/package/http-server) to preview the page locally in your browser.

1. From your project directory, run the following command to start a local web server:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx http-server -a localhost
```

{% endcode %}
{% endtab %}
{% endtabs %}

If prompted, press `y` to install `http-server`. A successful output looks similar to:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
Starting up http-server, serving ./

http-server version: 14.1.1

http-server settings: 
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none

Available on:
  http://localhost:8080
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Open the localhost URL from your terminal to view the project in your browser.
3. Paste this URL into your browser to see an alert showing the PDF's page count, then load the page:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
http://localhost:8080/?countpages=https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf
```

{% endcode %}
{% endtab %}
{% endtabs %}

4. Paste this URL into your browser to convert the DOCX file to PDF and automatically download the result, then load the page:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
http://localhost:8080/?convert=https://pdftron.s3.amazonaws.com/downloads/pl/report.docx
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Get started video

In this 6-minute video, learn how to integrate the Apryse WebViewer SDK into your application and build document-processing workflows without a viewer interface.

{% embed url="<https://www.youtube.com/embed/s2lKUTNAGdk?si=tAIPzWicfs-4BGlV>" %}
Integrate the WebViewer SDK without the UI to build document-processing workflows.
{% endembed %}

## Next steps

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# Libraries and Frameworks

{% content-ref url="/pages/GBC2ieyY8pX5h9AHt6yE" %}
[React](/web/get-started/libraries-and-frameworks/react)
{% endcontent-ref %}

{% content-ref url="/pages/5MnA0HjmHx6wTtBMzCpl" %}
[Angular](/web/get-started/libraries-and-frameworks/angular)
{% endcontent-ref %}

{% content-ref url="/pages/G7ILRIgU0gydwUG4OZWt" %}
[Vue](/web/get-started/libraries-and-frameworks/vue)
{% endcontent-ref %}

{% content-ref url="/pages/b5M6m7BH68ugf0GJKObK" %}
[Next.js](/web/get-started/libraries-and-frameworks/nextjs)
{% endcontent-ref %}

{% content-ref url="/pages/mnRr1ckOkmChhBd5J7lR" %}
[NuxtJS](/web/get-started/libraries-and-frameworks/nuxt)
{% endcontent-ref %}

{% content-ref url="/pages/eT8TSekfeEb8lPQkXmsN" %}
[Electron](/web/get-started/libraries-and-frameworks/electron)
{% endcontent-ref %}

{% content-ref url="/pages/t28ow0J9yN1qgWUOQ2WR" %}
[Svelte](/web/get-started/libraries-and-frameworks/svelte)
{% endcontent-ref %}

{% content-ref url="/pages/3DEJn4eauer1xtOOTZTe" %}
[Vite](/web/get-started/libraries-and-frameworks/vite)
{% endcontent-ref %}

{% content-ref url="/pages/VbzLTZfKWdmrCxFebCXR" %}
[Blazor](/web/get-started/libraries-and-frameworks/blazor)
{% endcontent-ref %}

{% content-ref url="/pages/5mZE1j1pvxiwgh1OR4h5" %}
[Framework compatibility](/web/get-started/libraries-and-frameworks/frameworks)
{% endcontent-ref %}


# Build a React PDF viewer with Apryse WebViewer SDK and Vite

Create a React (Vite) PDF viewer and editor with Apryse WebViewer. Learn how to integrate the SDK into your React apps and render PDFs in the viewer UI.

Create React App was once the standard for scaffolding React applications, but it has since been deprecated in favor of tools like Vite. This guide shows how to build a React PDF viewer using the Apryse [WebViewer SDK](/web/what-is-webviewer/overview) with [React and Vite](https://react.dev/learn/build-a-react-app-from-scratch#vite). You’ll learn how to integrate the SDK into a React application to render, view, and interact with PDF documents using the WebViewer UI.

You can also download a ready-to-use [GitHub sample](https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-react) to get started quickly, or explore the interactive [Showcase demo](https://showcase.apryse.com/) to see WebViewer's full capabilities in action.

## Prerequisites

This guide assumes basic familiarity with React development. Before you start:

* Install [Node.js](https://nodejs.org/en/download) and npm. See the [Vite documentation](https://vite.dev/guide/#scaffolding-your-first-vite-project) for version details.
* Install [Visual Studio Code](https://code.visualstudio.com/Download) or another code editor to develop and debug your code.
* Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## 1. Create a React project

In this section, you’ll create a new React with Vite application using npm. This project provides the foundation for integrating Apryse WebViewer. If you already have a React app, skip this and continue to [Install WebViewer](#2-install-webviewer).

1. In your terminal, go to the directory where you want to create the project.
2. Create a new `webviewer-react` project using a minimal setup:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx -y create-vite@latest webviewer-react --template react

```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

Flags are used to skip prompts during project configuration. The Vite setup selects React as the framework and uses Javascript. Select different options if preferred.
{% endhint %}

3. If prompted to install with npm and start now, select **No**.
4. Navigate to your new React project directory and install dependencies:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
cd webviewer-react
npm install
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 2. Install WebViewer

Next, install the Apryse WebViewer SDK using npm. This command adds the [WebViewer package](https://www.npmjs.com/package/@pdftron/webviewer) to your project, allowing you to integrate the PDF viewer and editor into your React application.

After navigating to your `webviewer-react` project directory, run the following command to install WebViewer:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm i @pdftron/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 3. Copy WebViewer assets

WebViewer needs access to its static assets at runtime, including WebAssembly modules, HTML, and CSS files. In a React project, you must copy these assets into the `public` directory so they can be served correctly. For more, see [Copying WebViewer static assets](/web/get-started/copy-assets).

1. From your project root, create the `public/lib/webviewer` directory if it doesn't already exist:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx mkdir -p public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Copy all WebViewer static assets from `node_modules/@pdftron/webviewer/public` into the new `public/lib/webviewer` directory:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes cpy-cli "node_modules/@pdftron/webviewer/public/**/*" public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

Your project should now include a similar structure:

{% code lineNumbers="true" %}

```
webviewer-react/
├─ node_modules/
├─ public/
│  ├─ lib/
│  │  └─ webviewer/
│  │     ├─ core/
│  │     └─ ui/
│  ├─ favicon.svg
│  └─ ...
├─ src/
└── ...
```

{% endcode %}

{% hint style="info" %}
**Info**

The `path` option used when initializing WebViewer must point to this directory (for example, `/lib/webviewer`). If the path is incorrect, WebViewer will fail to load.
{% endhint %}

## 4. Create the PDF viewer

Next, instantiate the Apryse WebViewer SDK and mount it in your React project. This involves creating a container for the viewer and initializing WebViewer so it loads correctly at runtime.

1. In Visual Studio Code, replace the contents of the `src/App.jsx` file with the following and save:

<pre class="language-jsx" data-line-numbers><code class="lang-jsx">import { useEffect, useRef } from 'react';
import WebViewer from '@pdftron/webviewer';

function App() {
  // Create reference to DOM element where WebViewer will mount
  const viewerRef = useRef(null);

  useEffect(() => {
    // Initialize WebViewer once when component mounts
    WebViewer(
      {
        // Path to copied WebViewer static assets
        path: '/lib/webviewer',
        // Replace with your Apryse license key
        licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
        // Initial document to load when WebViewer starts (optional)
        initialDoc: 'https://pdftron.s3.amazonaws.com/downloads/pl/demo-annotated.pdf',
      },
      // Attach WebViewer to container element
      viewerRef.current
    ).then((instance) => {
      // Access WebViewer instance here if needed
      // const { UI, Core } = instance;
    });
  // Empty dependency array ensures this runs only once  
  }, []);
  return (
    // Container element for WebViewer
    &#x3C;div
	    ref={viewerRef}
	    style={{ height: '100vh', width: '100%', margin: '0 auto' }}
	  />
  );
}

export default App;
</code></pre>

{% hint style="info" %}
**Info**

If you're signed in with an Apryse account, your license key is automatically prepopulated in all code snippets.
{% endhint %}

2. Replace the contents of the `src/main.jsx` file with the following and save:

{% tabs %}
{% tab title="JSX" %}
{% code title="src/main.jsx" lineNumbers="true" %}

```jsx
import { StrictMode } from 'react'; 
import { createRoot } from 'react-dom/client';

import App from './App.jsx';

createRoot(document.getElementById('root')).render( 
    <App /> 
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

In React 18 and later, `useEffect` may run twice in development when Strict Mode is enabled (which Vite uses by default). This can cause WebViewer to initialize twice and to display a duplicate instance. This behavior is expected and doesn't affect production builds.

For this example, you can avoid duplicate initialization by removing the `StrictMode` wrapper in `src/main.jsx`. However, note that Strict Mode helps identify issues like memory leaks, so removing it isn't recommended for production or more complex applications.
{% endhint %}

## 5. Verify your output

You can now load and display a PDF document in the WebViewer UI. Run your React application to launch WebViewer and see the PDF in your browser.

1. From your project directory, run the following command to start the application:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm run dev
```

{% endcode %}
{% endtab %}
{% endtabs %}

A successful output looks similar to:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
> webviewer-react@0.0.0 dev
> vite


  VITE v8.0.16  ready in 1369 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Open the localhost URL from your terminal to view the WebViewer UI and PDF document.

## Get started video

In this 7-minute video, learn how to install and integrate the Apryse WebViewer SDK into a React project built with Vite.

{% embed url="<https://www.youtube.com/embed/EjCYqOCge0E?si=Q8w1Y13KiYd4RC41>" %}
Integrate the WebViewer SDK in a React with Vite application.
{% endembed %}

## Next steps

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# Build an Angular PDF viewer with Apryse WebViewer SDK

Create an Angular PDF viewer and editor with Apryse WebViewer. Learn how to integrate the SDK into your Svelte apps and render PDFs in the viewer UI.

This guide shows how to build an Angular PDF viewer using the Apryse [WebViewer SDK](/web/what-is-webviewer/overview). You'll learn how to integrate the SDK into an [Angular](https://angular.dev/overview) application to render, view, and interact with PDF documents using the WebViewer UI.

You can also download a ready-to-use [GitHub sample](https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-angular) to get started quickly, or explore the interactive [Showcase demo](https://showcase.apryse.com/) to see WebViewer's full capabilities in action.

## Prerequisites

This guide assumes basic familiarity with Angular development. Before you start:

* Install [Node.js](https://nodejs.org/en/download) and npm. See the [Angular documentation](https://angular.dev/installation#prerequisites) for version details.
* Install [Visual Studio Code](https://code.visualstudio.com/Download) or another code editor to develop and debug your code.
* Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## 1. Create an Angular project

In this section, you’ll create a new Angular application using npm. This project provides the foundation for integrating Apryse WebViewer. If you already have an Angular app, skip this and continue to [Install WebViewer](#2-install-webviewer).

1. In your terminal, install the Angular CLI globally so you can create and manage Angular projects:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm install -g @angular/cli
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Navigate to the directory where you want to create the project.
3. Create a new `webviewer-angular` project using a minimal setup:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
ng new webviewer-angular --style=css --ssr=false --defaults
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

Flags are used to skip prompts during project configuration. The setup sets the CSS stylesheet format, disables server-side rendering (SSR) and prerendering, and accepts all defaults. Select different options if preferred.
{% endhint %}

4. Navigate to your new Angular project directory and install dependencies:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
cd webviewer-angular
npm install
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 2. Install WebViewer

Next, install the Apryse WebViewer SDK using npm. This command adds the [WebViewer package](https://www.npmjs.com/package/@pdftron/webviewer) to your project, allowing you to integrate the PDF viewer and editor into your Angular application.

After navigating to your `webviewer-angular` project directory, run the following command to install WebViewer:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm i @pdftron/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 3. Copy WebViewer assets

WebViewer requires access to its static assets at runtime, including WebAssembly modules, HTML, and CSS files. These assets are located in `node_modules/@pdftron/webviewer/public`. To ensure they’re served correctly, make them publicly accessible in your Angular application by configuring the `assets` array in `angular.json`. In this section, you’ll copy the required WebViewer assets to a location that Angular serves automatically. For more, see [Copying WebViewer static assets](/web/get-started/copy-assets).

1. Open your project in Visual Studio Code and go to the `angular.json` file.
2. In the `angular.json` file, update the `assets` array to include the following and save:

{% tabs %}
{% tab title="JSON" %}
{% code title="angular.json" lineNumbers="true" %}

```json
{
  // ...
  "assets": [
    // ...other assets,
    { 
      "glob": "**/*",
      "input": "./node_modules/@pdftron/webviewer/public",
      "output": "/lib/webviewer"
    }
  ],
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

The `output` value creates a new set of folders and moves the static assets to the `lib/webviewer` folder.

{% hint style="info" %}
**Info**

You can add multiple entries to the `assets` array, each with its own `input` path. Don't modify or remove the default entries in `angular.json`. For example, keep entries such as `{"glob": "**/*", "input": "public"}`.
{% endhint %}

## 4. Create the PDF viewer

In this section, you'll add WebViewer to your Angular app by creating a component and initializing the viewer. This mounts the WebViewer UI and loads a document in your application.

1. Create a `webviewer` folder in your project's `src` directory:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes mkdirp src/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Create `webviewer.ts` and `webviewer.html` files in the `webviewer` folder:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx touch src/webviewer/webviewer.ts
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx touch src/webviewer/webviewer.html
```

{% endcode %}
{% endtab %}
{% endtabs %}

3. In Visual Studio Code, add this code to the `webviewer.ts` file and save:

<pre class="language-ts" data-line-numbers><code class="lang-ts">import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import WebViewer from '@pdftron/webviewer';

@Component({
  selector: 'webviewer',
  templateUrl: './webviewer.html',
  standalone: true,
}) 

export class WebViewerComponent implements AfterViewInit {
  // Reference to DOM element where WebViewer will be mounted
  @ViewChild('viewer') viewer!: ElementRef;
  
  ngAfterViewInit(): void {
    WebViewer(
      {
        // Path to copied WebViewer static assets
        path: '../../lib/webviewer',
        // Replace with your Apryse license key
        licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
        // Document to load when WebViewer initializes
        initialDoc: 'https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf',
      },
      // Element where WebViewer is mounted
      this.viewer.nativeElement
    ).then(instance => {
      // WebViewer APIs are available via the instance
    });
  }
}
</code></pre>

{% hint style="info" %}
**Info**

If you're signed in with an Apryse account, your license key is automatically prepopulated in all code snippets.
{% endhint %}

4. Add this code to the `webviewer.html` file and save:

{% tabs %}
{% tab title="HTML" %}
{% code title="src/webviewer/webviewer.html" lineNumbers="true" %}

```html
<div #viewer class="viewer" style="height: 100vh; width: 100%; margin: 0 auto;"></div>
```

{% endcode %}
{% endtab %}
{% endtabs %}

5. Replace the `src/app/app.ts` file with the following and save:

{% tabs %}
{% tab title="TypeScript" %}
{% code title="src/app/app.ts" lineNumbers="true" %}

```ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
// Import WebViewer component created earlier
import { WebViewerComponent } from '../webviewer/webviewer';

@Component({
  selector: 'app-root',
  standalone: true,
  // Make WebViewer available in the root component
  imports: [RouterOutlet, WebViewerComponent],
  templateUrl: './app.html',
  styleUrl: './app.css'
})

export class App { 
  // Replace with your Angular project name 
  title = 'webviewer-angular';
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

6. Replace the `src/app/app.html` file with the following and save:

{% tabs %}
{% tab title="HTML" %}
{% code title="src/app/app.html" lineNumbers="true" %}

```html
<main class="main">
  <webviewer />
</main>
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 5. Verify your output

You can now load and display a PDF document in the WebViewer UI. Run your Angular application to launch WebViewer and see the PDF in your browser.

1. From your project directory, run the following command to start the application:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
ng serve
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

You may get a warning message in the command line, as the project runs, that you're not using `RouterOutlet` correctly. You can ignore the warning.
{% endhint %}

A successful output looks similar to:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
Application bundle generation complete. [0.643 seconds]

Watch mode enabled. Watching for file changes...
NOTE: Raw file sizes do not reflect development server per-request transformations.
  ➜  Local:   http://localhost:4200/
  ➜  press h + enter to show help
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Open the localhost URL from your terminal to view the WebViewer UI and PDF document.

## Get started video

In this 5-minute video, learn how to install and integrate the Apryse WebViewer SDK into an Angular project.

{% embed url="<https://www.youtube.com/embed/mOuIYRM4Nps?si=dxYysZ9qyrEuOHEQ>" %}
Integrate the WebViewer SDK in an Angular (v20+) application.
{% endembed %}

## Next steps

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# Build a Vue PDF viewer with Apryse WebViewer SDK

Create a Vue PDF viewer and editor with Apryse WebViewer. Learn how to integrate the SDK into your Vue.js apps and render PDFs in the viewer UI.

This guide shows how to build a Vue PDF viewer using the Apryse [WebViewer SDK](/web/what-is-webviewer/overview). You'll learn how to integrate the SDK into a [Vue.js](https://vuejs.org/) application to render, view, and interact with PDF documents using the WebViewer UI.

You can also download a ready-to-use [GitHub sample](https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-vue) to get started quickly, or explore the interactive [Showcase demo](https://showcase.apryse.com/) to see WebViewer's full capabilities in action.

## Prerequisites

This guide assumes basic familiarity with Vue.js development. Before you start:

* Install [Node.js](https://nodejs.org/en/download) and npm. See the [Vue.js documentation](https://vuejs.org/guide/quick-start.html#creating-a-vue-application) for version details.
* Install [Visual Studio Code](https://code.visualstudio.com/Download) or another code editor to develop and debug your code.
* Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## 1. Create a Vue project

In this section, you’ll create a new Vue.js application using npm. This project provides the foundation for integrating Apryse WebViewer. If you already have a Vue app, skip this and continue to [Install WebViewer](#id-2.-install-webviewer).

1. In your terminal, go to the directory where you want to create the project.
2. Create a new `webviewer-vue` Vue 3 project using the official Vue tooling and a minimal setup:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes create-vue@latest webviewer-vue --bare
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

Flags are used to skip prompts during project configuration. The setup skips all example code and starts with a blank Vue project. Select different options if preferred.
{% endhint %}

3. Complete the setup prompts with the following settings. Select different options if preferred:

* Don't use TypeScript.
* Don't select any additional Vue or experimental features.

4. Navigate to your new Vue project directory and install Vue dependencies:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
cd webviewer-vue
npm install
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 2. Install WebViewer

Next, install the Apryse WebViewer SDK using npm. This command adds the [WebViewer package](https://www.npmjs.com/package/@pdftron/webviewer) to your project, allowing you to integrate the PDF viewer and editor into your Vue application.

After navigating to your `webviewer-vue` project directory, run the following command to install WebViewer:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm i @pdftron/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 3. Copy WebViewer assets

WebViewer needs access to its static assets at runtime, including WebAssembly modules, HTML, and CSS files. In a Vue project, you must copy these assets into the `public` directory so they can be served correctly. For more, see [Copying WebViewer static assets](/web/get-started/copy-assets).

1. From your project root, create the `public/lib/webviewer` directory if it doesn't already exist:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx mkdir -p public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Copy all WebViewer static assets from `node_modules/@pdftron/webviewer/public` into the new `public/lib/webviewer` directory:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes cpy-cli "node_modules/@pdftron/webviewer/public/**/*" public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

Your project should now include a similar structure:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
webviewer-vue/
├── .vscode/
├── node_modules/
├── public/
│   ├── lib/
│   │   └── webviewer/
│   │       ├── core/
│   │       ├── ui/
│   │       └── ...
│   └── favicon.ico
├── src/
└── .gitignore
└── ...
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

The `path` option used when initializing WebViewer must point to this directory (for example, `/lib/webviewer`). If the path is incorrect, WebViewer will fail to load.
{% endhint %}

## 4. Create the PDF viewer

In this section, you'll add WebViewer to your Vue app by creating a component and initializing the viewer. This mounts the WebViewer UI and loads a document in your application.

1. Create a `components` folder in your project's `src` directory:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes mkdirp src/components
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Create a `WebViewer.vue` file in the `components` folder:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx touch src/components/WebViewer.vue
```

{% endcode %}
{% endtab %}
{% endtabs %}

3. In Visual Studio Code, add this code to the `WebViewer.vue` file and save:

{% tabs %}
{% tab title="Vue" %}

<pre class="language-vue" data-title="src/components/WebViewer.vue" data-line-numbers><code class="lang-vue">&#x3C;template>
  &#x3C;!-- Container element where WebViewer will be mounted -->
  &#x3C;div ref="viewer" style="height: 100vh; width: 100%; margin: 0 auto;">&#x3C;/div>
&#x3C;/template>

&#x3C;script setup>
import { ref, onMounted } from 'vue';
import WebViewer from '@pdftron/webviewer';

// Define component props
const props = defineProps({
  initialDoc: String,
});

// Reference to DOM element used as the WebViewer mount point
const viewer = ref(null);

/**
 * Initialize WebViewer once the component is mounted
 * and the DOM element is available
 */
onMounted(() => {
  WebViewer(
    { 
      // Public path where the WebViewer lib assets are served
      path: '/lib/webviewer',
      // Document to load initially (passed in from App.vue)
      initialDoc: props.initialDoc,
      // Replace with your Apryse license key
      licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
    },
    viewer.value,
  );
});
&#x3C;/script>
</code></pre>

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

If you're signed in with an Apryse account, your license key is automatically prepopulated in all code snippets.
{% endhint %}

4. Replace the contents of the `src/App.vue` file with the following and save:

{% tabs %}
{% tab title="Vue" %}
{% code title="src/App.vue" lineNumbers="true" %}

```vue
<script setup> 
import WebViewer from './components/WebViewer.vue' 
</script> 

<template> 
  <WebViewer 
    initialDoc="https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf" 
  />
</template> 

```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

If WebViewer doesn't appear in your application, verify that the container element has proper CSS dimensions. WebViewer automatically fills the full width and height of the element it's mounted to.
{% endhint %}

## 5. Verify your output

You can now load and display a PDF document in the WebViewer UI. Run your Vue application to launch WebViewer and see the PDF in your browser.

1. From your project directory, run the following command to start the application:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm run dev
```

{% endcode %}
{% endtab %}
{% endtabs %}

A successful output looks similar to:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
> webviewer-vue@0.0.0 dev
> vite


  VITE v8.0.16  ready in 2896 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  Vue DevTools: Open http://localhost:5173/__devtools__/ as a separate window
  ➜  Vue DevTools: Press Option(⌥)+Shift(⇧)+D in App to toggle the Vue DevTools
  ➜  press h + enter to show help
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Open the localhost URL from your terminal to view the WebViewer UI and PDF document.

## Get started video

In this 4-minute video, learn the fundamentals of Apryse WebViewer, including key concepts for installation and initialization in any web application.

{% embed url="<https://www.youtube.com/embed/UIIWdERmBtU?si=ZIOH-IIi9PqhqEDv>" %}
Overview of Apryse WebViewer fundamentals, including installation and initialization.
{% endembed %}

## Next steps

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# Build a Next.js PDF viewer with Apryse WebViewer SDK

Create a Next.js PDF viewer and editor with Apryse WebViewer. Learn how to integrate the SDK into your Next.js app and render PDFs in the viewer UI.

This guide shows how to build a Next.js PDF viewer using the Apryse [WebViewer SDK](/web/what-is-webviewer/overview). You'll learn how to integrate the SDK into a [Next.js](https://nextjs.org/docs) application to render, view, and interact with PDF documents using the WebViewer UI. This guide uses JavaScript examples. TypeScript projects will require minor adjustments.

You can also download a ready-to-use [GitHub sample](https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-nextjs) to get started quickly, or explore the interactive [Showcase demo](https://showcase.apryse.com/) to see WebViewer's full capabilities in action.

## Prerequisites

This guide assumes basic familiarity with Next.js development. Before you start:

* Install [Node.js](https://nodejs.org/en/download) and npm. See the [Next.js documentation](https://nextjs.org/docs/app/getting-started/installation#system-requirements) for version details.
* Install [Visual Studio Code](https://code.visualstudio.com/Download) or another code editor to develop and debug your code.
* Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## 1. Create a Next.js project

In this section, you’ll create a new Next.js application using npm. This project provides the foundation for integrating Apryse WebViewer. If you already have a Next.js app, skip this and continue to [Install WebViewer](#2-install-webviewer).

1. In your terminal, go to the directory where you want to create the project.
2. Create a new `webviewer-nextjs` project using a minimal setup:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes create-next-app@latest webviewer-nextjs --js --no-eslint --no-react-compiler --no-tailwind --no-src-dir --app --yes
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

Flags are used to skip prompts during project configuration. The setup uses JavaScript, skips ESLint configuration, disables React Compiler, excludes Tailwind CSS, omits the `src/` directory, enables App Router, and accepts other defaults. Select different options if preferred.
{% endhint %}

3. Navigate to your new Next.js project directory and install dependencies:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
cd webviewer-nextjs
npm install
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 2. Install WebViewer

Next, install the Apryse WebViewer SDK using npm. This command adds the [WebViewer package](https://www.npmjs.com/package/@pdftron/webviewer) to your project, allowing you to integrate the PDF viewer and editor into your Next.js application.

After navigating to your `webviewer-nextjs` project directory, run the following command to install WebViewer:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm i @pdftron/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 3. Copy WebViewer assets

WebViewer needs access to its static assets at runtime, including WebAssembly modules, HTML, and CSS files. In a Next.js project, you must copy these assets into the `public` directory so they can be served correctly. For more, see [Copying WebViewer static assets](/web/get-started/copy-assets).

1. From your project root, create the `public/lib/webviewer` directory if it doesn't already exist:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx mkdir -p public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Copy all WebViewer static assets from `node_modules/@pdftron/webviewer/public` into the new `public/lib/webviewer` directory:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes cpy-cli "node_modules/@pdftron/webviewer/public/**/*" public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

Your project should now include a similar structure:

{% code lineNumbers="true" %}

```
webviewer-nextjs/
├── .next/
├── app/
├── node_modules/
├── public/
│   ├── file.svg
│   └── lib/
│       └── webviewer/
│           ├── core/
│           └── ui/
└── ...
```

{% endcode %}

{% hint style="info" %}
**Info**

The `path` option used when initializing WebViewer must point to this directory (for example, `/lib/webviewer`). If the path is incorrect, WebViewer will fail to load.
{% endhint %}

## 4. Create the PDF viewer

In this section, you'll add WebViewer to your Next.js app by creating a component and initializing the viewer. This mounts the WebViewer UI and loads a document in your application.

1\. Create a `components` folder in your project's root:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes mkdirp components
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Create a `webviewer.js` file in the `components` folder:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx touch components/webviewer.js
```

{% endcode %}
{% endtab %}
{% endtabs %}

3. In Visual Studio Code, add this code to the `webviewer.js` file and save:

<pre class="language-js" data-line-numbers><code class="lang-js">// Runs only on the client
'use client';

// Import React hooks
import { useEffect, useRef } from 'react';

export default function WebViewer() {
  // Create a ref to hold div where WebViewer is mounted
  const viewer = useRef(null);

  // Run once after the component is mounted
  useEffect(() => {
    // Dynamically import WebViewer to avoid SSR issues
    import('@pdftron/webviewer').then((module) => {
      // Access the default export from the module
      const WebViewer = module.default;

      // Initialize WebViewer
      WebViewer(
        { 
          // Path to the WebViewer lib assets
          path: '/lib/webviewer',
          // Replace with your Apryse license key
          licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
          // Initial document to load into the viewer
          initialDoc: 'https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf',
        },
        // DOM element where WebViewer will be rendered
        viewer.current,
      ).then((instance) => {
        // Destructure the documentViewer from the Core API
        const { documentViewer } = instance.Core;

      // WebViewer APIs can now be used here
      // Example: documentViewer.addEventListener(...)
      });
    });

  // Empty dependency array ensures this runs once
  }, []);

  // Render the container that WebViewer mounts into
  return (
    &#x3C;div
      className='webviewer'
      ref={viewer}
      style={{ width: '100%', height: '100vh', margin: '0 auto' }}
    />
  );
}
</code></pre>

{% hint style="info" %}
**Info**

If you're signed in with an Apryse account, your license key is automatically prepopulated in all code snippets.
{% endhint %}

4. Replace the contents of the `app/page.js` file with the following and save:

{% tabs %}
{% tab title="JavaScript" %}
{% code title="app/page.js" lineNumbers="true" %}

```js
import WebViewer from '../components/webviewer';

export default function Page() {
  return (
    <div>
      <WebViewer />
    </div>
  );
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 5. Verify your output

You can now load and display a PDF document in the WebViewer UI. Run your Next.js application to launch WebViewer and see the PDF in your browser.

1. From your project directory, run the following command to start the application:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm run dev
```

{% endcode %}
{% endtab %}
{% endtabs %}

A successful output looks similar to:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
> webviewer-nextjs@0.1.0 dev
> next dev

▲ Next.js 16.2.7 (Turbopack)
- Local:         http://localhost:3000
✓ Ready in 413ms
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Open the localhost URL from your terminal to view the WebViewer UI and PDF document.

{% hint style="info" %}
**Info**

You may see a React hydration warning in the console when running the app locally. This is often caused by browser extensions modifying the page before React loads, not by your code. This warning is safe to ignore and doesn't affect WebViewer or PDF rendering. It will not appear in production.

You can suppress hydration warnings during development by overriding `console.error` — but we don’t recommend this unless you're absolutely sure hydration warnings are harmless.
{% endhint %}

## Get started video

In this 4-minute video, learn how to install and integrate the Apryse WebViewer SDK into a Next.js project using TypeScript. You’ll set up the PDF viewer, enable content editing, and explore the DOCX and Spreadsheet Editors.

{% embed url="<https://www.youtube.com/embed/0VoCnV0vyFE?si=EmZHj3gggFUjpXg6>" %}
Integrate WebViewer in a Next.js project with PDF editing, DOCX Editor, and Spreadsheet Editor.
{% endembed %}

## Troubleshooting

This section helps you resolve specific build errors when integrating WebViewer into a Next.js application.

#### The viewer renders as an empty box

If the page loads but the viewer area stays empty with no visible error, WebViewer can't load its static assets. Check the browser console for an error such as:

{% code lineNumbers="true" %}

```
Failed to load WebViewer Core script from <URL>
```

{% endcode %}

The `path` option doesn't point to your local copy of the WebViewer assets. Set it to the directory you created in the [Copy WebViewer assets](#3-copy-webviewer-assets) section (for this guide, it is the `/lib/webviewer`) directory). Don't point it at an external CDN or the npm package path.

#### Build fails with a Server Component error

If the build fails with an error such as:

{% code lineNumbers="true" %}

```
You're importing a module that depends on `useRef` into a React Server Component module.
This API is only available in Client Components.
To fix, mark the file (or its parent) with the `"use client"` directive.
```

{% endcode %}

WebViewer must run in a Client Component because it relies on browser APIs. Add the `'use client'` directive as the first line of the file that initializes WebViewer, as shown in the `webviewer.js` file example in the [Create the PDF viewer](#4-create-the-pdf-viewer) section.

#### Build fails with an ssr: false error

If the build fails with an error such as:

{% code lineNumbers="true" %}

```
`ssr: false` is not allowed with `next/dynamic` in Server Components. Please move it into a Client Component.
```

{% endcode %}

The WebViewer import was moved into `app/page.js` using `next/dynamic`. Instead, load WebViewer with the native `import('@pdftron/webviewer')` call inside the `useEffect` hook in `webviewer.js`, as shown in the [Create the PDF viewer](#4-create-the-pdf-viewer) section. The `useEffect` hook runs only in the browser, so no `ssr: false` workaround is needed.

## Next steps

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# Build a Nuxt PDF viewer with Apryse WebViewer SDK

Create a Nuxt PDF viewer and editor with Apryse WebViewer. Learn how to integrate the SDK into your NuxtJS apps and render PDFs in the viewer UI.

This guide shows how to build a Nuxt PDF viewer using the Apryse [WebViewer SDK](/web/what-is-webviewer/overview). You'll learn how to integrate the SDK into a [Nuxt](https://nuxt.com/docs/4.x/getting-started/introduction) application to render, view, and interact with PDF documents using the WebViewer UI.

You can also download a ready-to-use [GitHub sample](https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-nuxtjs) to get started quickly, or explore the interactive [Showcase demo](https://showcase.apryse.com/) to see WebViewer's full capabilities in action.

## Prerequisites

This guide assumes basic familiarity with Nuxt development. Before you start:

* Install [Node.js](https://nodejs.org/en/download) and npm. See the [Nuxt documentation](https://nuxt.com/docs/4.x/getting-started/installation#new-project) for version details.
* Install [Visual Studio Code](https://code.visualstudio.com/Download) or another code editor to develop and debug your code.
* Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## 1. Create a Nuxt project

In this section, you’ll create a new Nuxt application using npm. This project provides the foundation for integrating Apryse WebViewer. If you already have a Nuxt app, skip this and continue to [Install WebViewer](#2-install-webviewer).

1. In your terminal, go to the directory where you want to create the project.
2. Create a new `webviewer-nuxt` project using a minimal setup:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm create nuxt@latest webviewer-nuxt -y -- --template minimal --packageManager npm --gitInit

```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

Flags are used to skip prompts during project configuration. The setup uses a minimal template, defaults to npm as the package manager, initializes git, and installs dependencies. Select different options if preferred.
{% endhint %}

3. If prompted to install modules, select **No**.
4. Navigate to your new Nuxt project directory and install dependencies:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
cd webviewer-nuxt
npm install
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 2. Install WebViewer

Next, install the Apryse WebViewer SDK using npm. This command adds the [WebViewer package](https://www.npmjs.com/package/@pdftron/webviewer) to your project, allowing you to integrate the PDF viewer and editor into your Nuxt application.

After navigating to your `webviewer-nuxt` project directory, run the following command to install WebViewer:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm i @pdftron/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 3. Copy WebViewer assets

WebViewer needs access to its static assets at runtime, including WebAssembly modules, HTML, and CSS files. In a Nuxt project, you must copy these assets into the `public` directory so they can be served correctly. For more, see [Copying WebViewer static assets](/web/get-started/copy-assets).

1. From your project root, create the `public/lib/webviewer` directory if it doesn't already exist:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx mkdir -p public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Copy all WebViewer static assets from `node_modules/@pdftron/webviewer/public` into the new `public/lib/webviewer` directory:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes cpy-cli "node_modules/@pdftron/webviewer/public/**/*" public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

Your project should now include a similar structure:

{% code lineNumbers="true" %}

```
webviewer-nuxt/
├── .nuxt/
├── app/
├── node_modules/
├── public/
│   └── lib/
│       └── webviewer/
│           ├── core/
│           └── ui/
└── favicon.ico
└── ...
```

{% endcode %}

{% hint style="info" %}
**Info**

The `path` option used when initializing WebViewer must point to this directory (for example, `/lib/webviewer`). If the path is incorrect, WebViewer will fail to load.
{% endhint %}

## 4. Create the PDF viewer

In this section, you'll add WebViewer to your Nuxt app by creating a component and initializing the viewer. This mounts the WebViewer UI and loads a document in your application.

1. Create a `components` folder in your project's root:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes mkdirp components
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Create a `WebViewer.vue` file in the `components` folder:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx touch components/WebViewer.vue
```

{% endcode %}
{% endtab %}
{% endtabs %}

3. In Visual Studio Code, add this code to the `WebViewer.vue` file and save:

<pre class="language-vue" data-line-numbers><code class="lang-vue">&#x3C;template>
  &#x3C;!-- Container element where WebViewer will be mounted -->
  &#x3C;div id="webviewer" ref="viewer">&#x3C;/div>
&#x3C;/template>

&#x3C;script>
// Import APIs and libraries
import { ref, onMounted } from 'vue';
import WebViewer from '@pdftron/webviewer';

export default {
  // Component name for debugging and dev tools
  name: 'WebViewer',
  // Props accepted by the component
  props: {
    initialDoc: { type: String },
  },

  setup(props) {
    // Reference to the DOM element that hosts the viewer
    const viewer = ref(null);

    // Lifecycle hook that runs after the component is mounted to the DOM
    onMounted(() => {
      // Initialize the WebViewer inside the referenced DOM element
      WebViewer(
        {
          // Path to the WebViewer library assets
          path: '/lib/webviewer',
          // Initial document to load into the viewer
          initialDoc: props.initialDoc,
          // License key for Apryse WebViewer
          // Replace with your Apryse license key
          licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
        },
        // DOM element where WebViewer is rendered
        viewer.value,
        ).then((instance) => {
          // The WebViewer instance is available here
          // This is where you can customize the UI, register events, etc.
        });
    });

    // Expose refs to the template
    return {
        viewer,
    };
  },
};
&#x3C;/script>

&#x3C;style>
/* Size the WebViewer container */
#webviewer {
  height: 100vh;
  width: 100%;
  margin: 0 auto;
}
&#x3C;/style>
</code></pre>

{% hint style="info" %}
**Info**

If you're signed in with an Apryse account, your license key is automatically prepopulated in all code snippets.
{% endhint %}

4. Replace the contents of the `app/app.vue` file with the following and save:

{% tabs %}
{% tab title="Vue" %}
{% code title="app/app.vue" lineNumbers="true" %}

```vue
<script setup>
import WebViewer from '../components/WebViewer.vue';
</script>

<template>
  <ClientOnly>
    <WebViewer initial-doc="https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf" />
  </ClientOnly>
</template>
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 5. Verify your output

You can now load and display a PDF document in the WebViewer UI. Run your Nuxt application to launch WebViewer and see the PDF in your browser.

1. From your project directory, run the following command to start the application:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm run dev
```

{% endcode %}
{% endtab %}
{% endtabs %}

A successful output looks similar to:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
> dev
> nuxt dev

●  Nuxt 4.4.7 (with Nitro 2.13.4, Vite 7.3.5 and Vue 3.5.35)

  ➜ Local:    http://localhost:3000/
  ➜ Network:  use --host to expose

  ➜ DevTools: press Shift + Option + D in the browser (v3.2.4)

✔ Vite client built in 32ms
✔ Vite server built in 9ms
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Open the localhost URL from your terminal to view the WebViewer UI and PDF document.

## Get started video

In this 5-minute video, learn how to install and integrate the Apryse WebViewer SDK into a Nuxt project. You’ll set up the PDF viewer, verify it’s working in the UI, and then enable the Apryse Spreadsheet Editor.

{% embed url="<https://www.youtube.com/embed/UOKDUNYw_gk?si=Ad-nN-L6olaWQrZ5>" %}
Integrate WebViewer into a Nuxt project and enable the Spreadsheet Editor.
{% endembed %}

## Next steps

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# Build an Electron PDF viewer with Apryse WebViewer SDK

Create an Electron PDF viewer and editor with Apryse WebViewer. Learn how to integrate the SDK into your Electron app and render PDFs in the viewer UI.

This guide shows how to build an Electron PDF viewer using the Apryse [WebViewer SDK](/web/what-is-webviewer/overview). You'll learn how to integrate the SDK into an [Electron](https://www.electronjs.org/docs/latest/) application to render, view, and interact with PDF documents using the WebViewer UI.

You can also download a ready-to-use [GitHub sample](https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-electron) to get started quickly, or explore the interactive [Showcase demo](https://showcase.apryse.com/) to see WebViewer's full capabilities in action.

## Prerequisites

This guide assumes basic familiarity with Electron development. Before you start:

* Install [Node.js](https://nodejs.org/en/download) and npm. See the [Electron documentation](https://www.electronjs.org/docs/latest/tutorial/tutorial-prerequisites#nodejs-and-npm) for version details.
* Install [Visual Studio Code](https://code.visualstudio.com/Download) or another code editor to develop and debug your code.
* Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## 1. Create an Electron project

In this section, you’ll create a new Electron application using npm. This project provides the foundation for integrating Apryse WebViewer. If you already have an Electron app, skip this and continue to [Install WebViewer](#id-2.-install-webviewer).

1. In your terminal, go to the directory where you want to create the project.
2. Create a new project directory, initialize your project, and generate a `package.json` file:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
mkdir webviewer-electron
cd webviewer-electron
npm init -y
```

{% endcode %}
{% endtab %}
{% endtabs %}

3. Update the main entry point in the `package.json` file to `main.js` :

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm pkg set main='main.js'
```

{% endcode %}
{% endtab %}
{% endtabs %}

4. Update the `scripts` section and the start command in the `package.json`:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm pkg set scripts.start="electron ."
```

{% endcode %}
{% endtab %}
{% endtabs %}

The final `package.json` should look like this:

{% tabs %}
{% tab title="JSON" %}
{% code title="package.json" lineNumbers="true" %}

```json
{
  "name": "webviewer-electron",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "electron ."
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "commonjs"
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

5. In your project directory, install Electron:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm install --save-dev electron
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 2. Install WebViewer

Next, install the Apryse WebViewer SDK using npm. This command adds the [WebViewer package](https://www.npmjs.com/package/@pdftron/webviewer) to your project, allowing you to integrate the PDF viewer and editor into your Electron application.

After navigating to your `webviewer-electron` project directory, run the following command to install WebViewer:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm i @pdftron/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 3. Copy WebViewer assets

WebViewer needs access to its static assets at runtime, including WebAssembly modules, HTML, and CSS files. In an Electron project, you must copy these assets into the `public` directory so they can be served correctly. For more, see [Copying WebViewer static assets](/web/get-started/copy-assets).

1. From your project root, create the `public/lib/webviewer` directory if it doesn't already exist:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx mkdir -p public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Copy all WebViewer static assets from `node_modules/@pdftron/webviewer/public` into the new `public/lib/webviewer` directory:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes cpy-cli "node_modules/@pdftron/webviewer/public/**/*" public/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

3. Copy the `webviewer.min.js` library file from `node_modules` to the `public` directory:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes cpy-cli node_modules/@pdftron/webviewer/webviewer.min.js public/lib/webviewer --flat 
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

Unlike frameworks such as Vite, Nuxt, or Next.js, Electron doesn't use a module bundler by default to resolve dependencies and prepare them for the browser. As a result, you must manually include the `webviewer.min.js` file in your HTML before using it.
{% endhint %}

Your project should now include a similar structure:

{% code lineNumbers="true" %}

```
webviewer-electron/
├─ node_modules/
├─ public/
│  └─ lib/
│     └─ webviewer/
│        ├─ core/
│        ├─ ui/
│        └─ webviewer.min.js
├─ package-lock.json
└─ package.json
└─ ...
```

{% endcode %}

{% hint style="info" %}
**Info**

The `path` option used when initializing WebViewer must point to this directory (for example, `/lib/webviewer`). If the path is incorrect, WebViewer will fail to load.
{% endhint %}

## 4. Create the PDF viewer

In this section, you’ll add WebViewer to your Electron app by creating an HTML interface and initializing the viewer. This mounts the WebViewer UI and loads a document in your application.

1. Open Visual Studio Code.
2. Create an `index.html` file in your project root and add the following to define your app UI:

{% tabs %}
{% tab title="HTML" %}
{% code title="index.html" lineNumbers="true" %}

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Electron WebViewer</title>
    <!-- Load WebViewer library before render.js runs -->
    <script src="./public/lib/webviewer/webviewer.min.js"></script>
  </head>

  <body style="margin: 0; padding: 0; height: 100vh;">
    <!-- WebViewer mounts into this container from render.js -->
    <div id="viewer" style="width: 100%; height: 100vh; margin: 0 auto;"></div>
    <!-- Initialize WebViewer after DOM load -->
    <script defer src="render.js">render.js</script>
  </body>
</html>
```

{% endcode %}
{% endtab %}
{% endtabs %}

3. Create a `main.js` file in your project root, then add the following code to open an Electron window and load your `index.html` file:

{% tabs %}
{% tab title="JavaScript" %}
{% code title="main.js" lineNumbers="true" %}

```js
const { app, BrowserWindow } = require('electron');

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
  });

  // Main entry point
  win.loadFile('index.html');
};

app.whenReady().then(() => {
  createWindow();

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

4. Create a `render.js` file in your project root, then add the following code to initialize WebViewer inside the HTML UI:

<pre class="language-js" data-line-numbers><code class="lang-js">// Get the HTML element where WebViewer will be mounted
const viewerElement = document.getElementById('viewer');

WebViewer(
  {
    // Path to the copied WebViewer static assets
    path: './public/lib/webviewer',
    // Initial document to load when WebViewer starts
    initialDoc: 'https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf',
    // Replace with your Apryse license key
    licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
  },
  // Attach WebViewer to the container element
  viewerElement
).then((instance) => {
  // WebViewer is initialized and ready to use
});
</code></pre>

{% hint style="info" %}
**Info**

If you're signed in with an Apryse account, your license key is automatically prepopulated in all code snippets.
{% endhint %}

5. Save all files modified in this section.

## 5. Verify your output

You can now load and display a PDF document in the WebViewer UI. Run your Electron application to launch WebViewer and view the PDF inside the app window.

1. From your project directory, run the following command to start the application:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm start
```

{% endcode %}
{% endtab %}
{% endtabs %}

A successful output looks similar to:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
> webviewer-electron@1.0.0 start
> electron .

Downloading Electron binary...
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. The Electron app opens and displays the WebViewer UI with the PDF loaded.

## Get started video

In this 5-minute video, learn how to install and integrate the Apryse WebViewer SDK into an Electron project.

{% embed url="<https://www.youtube.com/embed/Kp41vkQYxzI?si=m0VN0pi5M5DT-i9->" %}
Integrate the WebViewer SDK in an Electron application.
{% endembed %}

## Next steps

Check out the resources below.

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>


# Build a Svelte PDF viewer with Apryse WebViewer SDK

Create a Svelte PDF viewer and editor with Apryse WebViewer. Learn how to integrate the SDK into your Svelte apps and render PDFs in the viewer UI.

This guide shows how to build a Svelte PDF viewer using the Apryse [WebViewer SDK](/web/what-is-webviewer/overview). You'll learn how to integrate the SDK into a [Svelte](https://svelte.dev/docs/kit/introduction) application to render, view, and interact with PDF documents using the WebViewer UI.

You can also download a ready-to-use [GitHub sample](https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-svelte) to get started quickly, or explore the interactive [Showcase demo](https://showcase.apryse.com/) to see WebViewer's full capabilities in action.

## Prerequisites

This guide assumes basic familiarity with Svelte development. Before you start:

* Install [Node.js](https://nodejs.org/en/download) and npm. See the [Svelte documentation](https://svelte.dev/docs/kit/migrating-to-sveltekit-2#Updated-dependency-requirements) for version details.
* Install [Visual Studio Code](https://code.visualstudio.com/Download) or another code editor to develop and debug your code.
* Get your Apryse trial key.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="full" %}

## 1. Create a Svelte project

In this section, you’ll create a new Svelte application using npm. This project provides the foundation for integrating Apryse WebViewer. If you already have a Svelte app, skip this and continue to [Install WebViewer](#2-install-webviewer).

1. In your terminal, go to the directory where you want to create the project.
2. Create a new `webviewer-svelte` project using a minimal setup:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes sv create webviewer-svelte --template minimal --no-types --no-add-ons --install npm
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Info**

Flags are used to skip prompts during project configuration. The setup uses a SvelteKit minimal template, excludes type checking, omits project add-ons, and defaults to npm as the package manager. Select different options if preferred.
{% endhint %}

3. Navigate to your new Svelte project directory and install dependencies:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
cd webviewer-svelte
npm install
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 2. Install WebViewer

Next, install the Apryse WebViewer SDK using npm. This command adds the [WebViewer package](https://www.npmjs.com/package/@pdftron/webviewer) to your project, allowing you to integrate the PDF viewer and editor into your Svelte application.

After navigating to your `webviewer-svelte` project directory, run the following command to install WebViewer:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm i @pdftron/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 3. Copy WebViewer assets

WebViewer needs access to its static assets at runtime, including WebAssembly modules, HTML, and CSS files. In a Svelte project, you must copy these assets into the `static` directory so they can be served correctly. For more, see [Copying WebViewer static assets](/web/get-started/copy-assets).

1. From your project root, create the `static/lib/webviewer` directory if it doesn't already exist:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx mkdir -p static/lib/webviewer 
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Copy all WebViewer static assets from `node_modules/@pdftron/webviewer/public` into the new `static/lib/webviewer` directory:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes cpy-cli "node_modules/@pdftron/webviewer/public/**/*" static/lib/webviewer
```

{% endcode %}
{% endtab %}
{% endtabs %}

Your project should now include a similar structure:

{% code lineNumbers="true" %}

```
webviewer-svelte/
├── .svelte-kit/
├── .vscode/
├── node_modules/
├── src/
├── static/
│   ├── lib/
│   │   └── webviewer/
│   │       ├── core/
│   │       └── ui/
│   └── robots.txt
└── .gitignore
└── ...
```

{% endcode %}

{% hint style="info" %}
**Info**

The `path` option used when initializing WebViewer must point to this directory (for example, `/lib/webviewer`). If the path is incorrect, WebViewer will fail to load.
{% endhint %}

## 4. Create the PDF viewer

In this section, you'll add WebViewer to your Svelte app by creating a component and initializing the viewer. This mounts the WebViewer UI and loads a document in your application.

1. Create a `WebViewer.svelte` file in the `src` folder of your project:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npx --yes shx touch src/WebViewer.svelte
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. In Visual Studio Code, add this code to the `WebViewer.svelte` file and save:

<pre class="language-svelte" data-line-numbers><code class="lang-svelte">&#x3C;script>
  import { onMount } from 'svelte';
  
  // Reference DOM element that will host WebViewer
  let viewer;

  // Run WebViewer setup once component is mounted in DOM
  onMount(() => {
    // Load WebViewer dynamically to avoid build/SSR issues
	  import("@pdftron/webviewer").then(({default: WebViewer}) => {

      // Initialize WebViewer instance
      WebViewer({
        // Path to WebViewer library assets
        path: '/lib/webviewer',
        // Replace with your Apryse license key
        licenseKey: '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>',
        // Initial document to load into viewer
        initialDoc: 'https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf',
        }, viewer,
      ).then((instance) => {
	        // WebViewer APIs are available via the instance
	    });
	  });
  });
&#x3C;/script>

&#x3C;style>
#viewer {
  /* Fill viewport with the viewer */
  width: 100%;
  height: 100vh;
  margin: 0 auto;
}
&#x3C;/style>

&#x3C;!-- Container element bound to viewer variable -->
&#x3C;div id="viewer" bind:this={viewer}>&#x3C;/div>
</code></pre>

{% hint style="info" %}
**Info**

* If you're signed in with an Apryse account, your license key is automatically prepopulated in all code snippets.
* SvelteKit manages its Vite version internally. Recent versions use Vite 8, which requires a small configuration change when using WebViewer. For more, see [Using WebViewer with Vite 8](https://apryse.com/blog/webviewer-vite-8-fix).
* In this example, WebViewer is imported dynamically to avoid running it during server‑side rendering. If your app doesn't use server‑side rendering, you can import WebViewer statically at the top of the file instead.
  {% endhint %}

3. Replace the contents of the `src/routes/+page.svelte` file with the following and save:

{% tabs %}
{% tab title="Svelte" %}
{% code title="src/routes/+page.svelte" lineNumbers="true" %}

```svelte
<script>
	import WebViewer from '../WebViewer.svelte';
</script>

<WebViewer />
```

{% endcode %}
{% endtab %}
{% endtabs %}

## 5. Verify your output

You can now load and display a PDF document in the WebViewer UI. Run your Svelte application to launch WebViewer and see the PDF in your browser.

1. From your project directory, run the following command to start the application:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
npm run dev
```

{% endcode %}
{% endtab %}
{% endtabs %}

A successful output looks similar to:

{% tabs %}
{% tab title="Shell" %}
{% code lineNumbers="true" %}

```shell
> webviewer-svelte@0.0.1 dev
> vite dev

11:31:01 AM [vite] (client) Forced re-optimization of dependencies

  VITE v8.0.16  ready in 740 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Open the localhost URL from your terminal to view the WebViewer UI and PDF document.

## Get started video

In this 4-minute video, learn how to install and integrate the Apryse WebViewer SDK into a Svelte project using TypeScript. You’ll set up the PDF viewer and explore the [DOCX Editor](/web/docx-editor/docx-editor).

{% embed url="<https://www.youtube.com/embed/s0heq5M4pOY?si=50vX-_cht8y6W-Aw>" %}
Integrate WebViewer in a Svelte project with PDF editing and DOCX Editor.
{% endembed %}

## Next steps

<a href="/web/what-is-webviewer/usage" class="button primary">Usage</a><a href="/web/get-started/guides" class="button primary">Guides</a><a href="/web/get-started/samples" class="button primary">Samples</a><a href="https://sdk.apryse.com/api/web/index.html" class="button primary">API docs</a>




---

[Next Page](https://docs.apryse.com/llms-full.txt/1)

