Some test text!

Saving Annotationskeyboard_arrow_down

Saving Annotations Stored in Salesforce

linkSaving Annotations in Salesforce

linkAdding a new ContentVersion record from WebViewer LWC

First, you need to generate a base64 string that is sent to the Apex backend. Use the following function in your config_apex.js file to achieve this:

async function saveDocument() {
  const doc = docViewer.getDocument();
  if (!doc) {

  const fileType = doc.getType();
  const filename = doc.getFilename();
  const xfdfString = await docViewer.getAnnotationManager().exportAnnotations();
  const data = await doc.getFileData({
    // Saves the document with annotations in it

  let binary = '';
  const bytes = new Uint8Array(data);
  for (let i = 0; i < bytes.byteLength; i++) {
    binary += String.fromCharCode(bytes[i]);

  const base64Data = window.btoa(binary);

  const payload = {
    title: filename.replace(/\.[^/.]+$/, ""),
    contentDocumentId: doc.__contentDocumentId
  // Post message to LWC
  parent.postMessage({ type: 'SAVE_DOCUMENT', payload }, '*');

linkPassing blob data to Apex

Once you have your document's data converted, you can pass it to Apex. This snippet uses

//snipped for brevity
//see full github sample to see how to set up event flow
handleReceiveMessage(event) {
  const me = this;
  if (event.isTrusted && typeof === 'object') {
    switch ( {
      case 'SAVE_DOCUMENT':
        saveDocument({ json: JSON.stringify(, recordId: this.recordId }).then((response) => {
          me.iframeWindow.postMessage({ type: 'DOCUMENT_SAVED', response }, '*')
        }).catch(error => {

linkCreating a new ContentVersion record from blob data

Finally, you can use the below Apex code snippet to create a new ContentVersion record. This sample also links the newly created document to the sObject record you started from.

// force-app\main\default\classes\ContentVersionController.cls
public with sharing class ContentVersionController {
// snipped for brevity
  public static void saveDocument(String json, String recordId) {
    try {
      PDFTron_ContentVersionPayload pl = PDFTron_ContentVersionPayload.parse(json);
      ContentVersion cv = new ContentVersion();
      cv.ContentLocation = 'S'; //File originated in Salesforce
      if(pl.contentDocumentId != null) {
        cv.ContentDocumentId = pl.contentDocumentId;
        cv.ReasonForChange = 'Saved from WebViewer';//only for file updates
      } else {
        for(ContentDocumentLink cdl : 
          [   SELECT ContentDocumentId, ContentDocument.Title
              FROM ContentDocumentLink 
              WHERE LinkedEntityId = :recordId
              AND ContentDocument.Title = :pl.title ]) {
                if(cdl.ContentDocumentId != null) {
                  cv.ContentDocumentId = cdl.ContentDocumentId;
      cv.VersionData = EncodingUtil.base64Decode(pl.base64Data);
      cv.Title = pl.title;
      cv.PathOnClient = pl.filename;
      insert cv; 
    } catch (Exception e) {
      throw new AuraHandledException(e.getMessage());

You can also find the full source code for an end to end flow in this Github repository.

Get the answers you need: Support


Free Trial

Get unlimited trial usage of PDFTron SDK to bring accurate, reliable, and fast document processing capabilities to any application or workflow.

Select a platform to get started with your free trial.

Unlimited usage. No email address required.