> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/salesforce/get-started/without-viewer.md).

# Run without Viewer in Salesforce

Discover how Apryse's SDK and PDFNet SDK can enhance your Salesforce app by enabling document operations without a UI. Follow this guide to integrate WebViewer into your Salesforce org seamlessly. Sal

Apryse's SDK provides full viewing of documentation formats and other capabilities but also provides access to Apryse's PDFNet SDK that allows document operations excluding a UI if necessary.

This is useful for Salesforce that needs additional processing without viewing a document. Although there are performance drawbacks as this is done on the client-side, it can be critical as part of your app's flow.

Here is a sample no-viewer Salesforce to follow with this guide:

<a href="https://github.com/ApryseSDK/salesforce-webviewer-attachments-no-viewer" class="button primary">Sample Project</a>

## Prerequisites

This guide assumes you will have downloaded WebViewer SDK and have Webviewer running on your Salesforce org.

If you have not done so, please check out the following to get started:

* [Integration](/salesforce/get-started/integrate.md)
* [**Salesforce CLI**](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_install_cli.htm#sfdx_setup_install_cli)
* [**Node and NPM**](https://nodejs.org/en/)
* (Optional) [**Visual Studio Code with Salesforce Extensions**](https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-vscode)

{% @apryse-download-button/apryse-download-button id="WebViewer\_web\_web" title="Download WebViewer" showVersion="true" %}

## 1. Create a hidden LWC

Webviewer still needs to be present on the page of your Salesforce org. You will need to create a lightning web component that has a hidden div to hold the iframe that would run no-viewer Webviewer.

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

```html
<template>
  <div id="core-controls-iframe" class="slds-hidden" lwc:dom="manual"></div>
</template>
```

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

WebViewer Core is the core for WebViewer. The usual script that would be required with the UI is `webviewer.min.js`. Using this script would automatically import and use the core.

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

```js
renderedCallback() {
    Promise.all([loadScript(this, libUrl + '/webviewer.min.js')])
        .then(() => {
          this.initUI();
        })
        .catch(console.error);
  }
```

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

`webviewerConstructor` is similar to `custom` tag in a Webviewer constructor, pass information through the iframe src and hash the information from the url in the config file. Set the iframe to take up no space in the lwc if you want a true no-viewer page. Create an iframe to host an html file that runs script tags pathed to `webviewer.core.min.js` and `PDFNet.js`

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

```js
var webviewerConstructor = {
  cs_pdftron_core: libUrl + "/core/webviewer-core.min.js",
  pdfnet: myfilesUrl + "/PDFNet.js",
  full_api: true
};

initUI() {
    const viewerElement = this.template.querySelector("div");
    var queryParameter = `#param=${JSON.stringify( webviewerConstructor )}`;

    var rcFrame = document.createElement("iframe");
    rcFrame.style = "position: absolute; width:0; height:0; border:0;";

    rcFrame.src = `${myfilesUrl}/noviewer.html${queryParameter}`;
    viewerElement.appendChild(rcFrame);
    this.iframeWindow = rcFrame.contentWindow;
}
```

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

## 2. Setting the worker path

In a viewer-less scenario, we would only import the core script (`webviewer-core.min.js`; `CoreControls.js` prior to WebViewer 8.0) in the HTML, if needing the FULL api you need to reference a different script(`PDFNet.js`) also.

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

```js
var script = document.createElement('script');
  script.onload = function () {
    init();
    var pdfnet = document.createElement('script');
    pdfnet.src = clientSidePdfGenerationConfig['pdfnet'];
    document.head.appendChild(pdfnet);
  };
  script.src = clientSidePdfGenerationConfig['cs_pdftron_core'];
  document.head.appendChild(script);
```

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

WebViewer requires web workers to function and work with documents. This is necessary regardless of the setup. Normally, this is done automatically via the `path` option in the WebViewer constructor. In a viewer-less scenario, we can load the workers with a simple JavaScript call:

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

```js
window.Core.forceBackendType('ems');

window.Core.setWorkerPath(resourceURL + 'lib/core');

window.Core.setOfficeAsmPath(resourceURL + 'office_asm');
window.Core.setOfficeWorkerPath(resourceURL + 'office');
window.Core.setOfficeResourcePath(resourceURL + 'office_resource');

// pdf workers
window.Core.setPDFResourcePath(resourceURL + 'resource');
if (clientSidePdfGenerationConfig['full_api']) {
  window.Core.setPDFWorkerPath(resourceURL + 'pdf_full');
  window.Core.setPDFAsmPath(resourceURL + 'asm_full');
} else {
  window.Core.setPDFWorkerPath(resourceURL + 'pdf_lean');
  window.Core.setPDFAsmPath(resourceURL + 'asm_lean');
}

// external 3rd party libraries
window.Core.setExternalPath(resourceURL + 'external');
window.Core.disableEmbeddedJavaScript(true)

window.Core.disableOptimizedWorkers();
```

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

## 3. Usage

Once the workers are ready, everything is in place to use the APIs and classes in the [`Core`](/web/what-is-webviewer/usage.md#Core-Namespace)[ namespace](/web/what-is-webviewer/usage.md#Core-Namespace).

### Communicate with Salesforce

Often in Salesforce, sending information to our workers is common so having a communication method to recieve and send out information is required.

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

```js
// Recieve
window.addEventListener("message", receiveMessage, false);

function receiveMessage(event) {
  if (event.isTrusted && typeof event.data === 'object') {
    switch (event.data.type) {
      case 'DOWNLOAD_DOCUMENT':
        transportDocument(event.data.payload, false)
        break;
      default:
        break;
    }
  }
}

// Send
parent.postMessage({ type: 'DOWNLOAD_DOCUMENT', file }, '*')
```

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

### Building a custom UI

One special use case would be to create your own UI by leveraging the [DocumentViewer](https://sdk.apryse.com/api/web/Core.DocumentViewer.html) class and object.

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

```js
const documentViewer = new Core.DocumentViewer();

// Hook up the DocumentViewer object to your own elements
documentViewer.setScrollViewElement(document.getElementById('scroll-view'));
documentViewer.setViewerElement(document.getElementById('viewer'));

// Load your document
documentViewer.loadDocument('path/to/document.pdf', { l: licenseKey });
```

{% endcode %}

[DocumentViewer](https://sdk.apryse.com/api/web/Core.DocumentViewer.html) [DocumentViewer.setScrollViewElement](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#setScrollViewElement) [DocumentViewer.setViewerElement](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#setViewerElement) [DocumentViewer.loadDocument](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#loadDocument)
{% endtab %}

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

```js
const documentViewer = new CoreControls.DocumentViewer();

// Hook up the DocumentViewer object to your own elements
documentViewer.setScrollViewElement(document.getElementById('scroll-view'));
documentViewer.setViewerElement(document.getElementById('viewer'));

// Load your document
documentViewer.loadDocument('path/to/document.pdf', { l: licenseKey });
```

{% endcode %}

[DocumentViewer](https://sdk.apryse.com/api/web/Core.DocumentViewer.html) [DocumentViewer.setScrollViewElement](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#setScrollViewElement) [DocumentViewer.setViewerElement](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#setViewerElement) [DocumentViewer.loadDocument](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#loadDocument)
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**You can check out the full guide on how to build your own UI .**
{% endhint %}

### Processing documents (Full API - PDFNet)

Another use case is to use the APIs to process documents in the client (browser). We already establish Full API usage so now we just have to test them.

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

```js
async function main() {
  // creates an empty pdf document (PDFDoc)
  const doc = await PDFNet.PDFDoc.create();
  doc.initSecurityHandler();
  // Locks all operations on the document
  doc.lock();

  // insert user code after this point
  const pgnum = await doc.getPageCount();
  alert(`Test Complete! Your file has ${pgnum} pages`);
};

PDFNet.runWithCleanup(main, /* License key goes here after purchase */)
```

{% endcode %}

[PDFNet.runWithCleanup](https://sdk.apryse.com/api/web/Core.PDFNet.html#.runWithCleanup) [PDFDoc](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#main) [PDFDoc.create](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.create__anchor)
{% endtab %}

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

```js
async function main() {
  // creates an empty pdf document (PDFDoc)
  const doc = await PDFNet.PDFDoc.create();
  doc.initSecurityHandler();
  // Locks all operations on the document
  doc.lock();

  // insert user code after this point
  const pgnum = await doc.getPageCount();
  alert(`Test Complete! Your file has ${pgnum} pages`);
};

PDFNet.runWithCleanup(main, /* License key goes here after purchase */)
```

{% endcode %}

[PDFNet.runWithCleanup](https://sdk.apryse.com/api/web/Core.PDFNet.html#.runWithCleanup) [PDFDoc](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#main) [PDFDoc.create](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.create__anchor)
{% endtab %}
{% endtabs %}


---

# 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
If you need additional information that is not directly available in this page, you can query the documentation 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/salesforce/get-started/without-viewer.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.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
