PdfTron Salesforce Integration

WebViewer Version:
8.6.0
Do you have an issue with a specific file(s)?Pdf, Docx
Can you reproduce using one of our samples or online demos?No
Are you using the WebViewer server?No
Does the issue only happen on certain browsers?No
Is your issue related to a front-end framework?No
Is your issue related to annotations?No

Please give a brief summary of your issue:
I integrated Webviewer for Salesforce following the steps mentioned in the video but after deploying the static resources and lwc to the scratch org, I’m unable to use Webviewer . I can’t view pdf files . It throws an error in portal : Worker encountered an error and in console I can see that it says Error: Failed to find PDF worker files. This project is not set up to work with PDF files.

Please describe your issue and provide steps to reproduce it:
The issue is that I can’t use any of PdfTron’s feature because the file doesn’t load. It works when I upload a jpg or png file but they can’t be redacted or downloaded. I followed the steps in the Salesforce integration guide video.

Attaching link of the video showing the issue : pdftron.mp4

Hello, I’m Ron, an automated tech support bot :robot:

While you wait for one of our customer support representatives to get back to you, please check out some of these documentation pages:

Guides:APIs:Forums:

Hi reet.roy, welcome to the PDFTron community.

The errors you are seeing in your video are related to WebViewer not being able to locate PDF worker files which are required for rendering/redaction of your document.

To debug this, please check the Network tab in your browser console (you might need to refresh to see all requests). You should see some failed requests (status code 404) - please click on them and see what the request URL is. Make sure that your static resources includes the requested URL. If the resources are not uploaded, make sure to add them to static resources.

Also, make sure you check the request URL for /lean/ or /full/ which indicates full or lean API. You can set fullAPI to true or false in the WebViewer constructor of your LWC component, and need to make sure the corresponding resources are available (ie when settin fullAPI: true, make sure you generate the workers in the NPM script and answer y to fullAPI).

Hope that helps, let us know if you have any other questions.

Thanks,
Thomas

1 Like

Hi @twinter yeah the path set was incorrect. It’s now working as expected. Just a thing , can we view Salesforce attachments using PDFTron webviewer. If yes , can you please guide me through the steps

Awesome, good to hear that you were able to resolve the issue.

For viewing attachments, you can review the github repo here: GitHub - PDFTron/salesforce-webviewer-attachments

The README.md file also contains a link to a guide that will walk you through the deployment of the solution. Hope that helps!

Thanks,
Thomas

1 Like

I am able to search for related attachments in an Object Record. This will probably be my last query. Can we apply redactions programmatically such that when a document is opened in Webviewer , it is actually redacted automatically.

Yes it’s possible - there are two common ways of achieving this:

  1. Draw your redaction marks in the UI (PDFTron Systems Inc. | Documentation) and export them as annotations using exportAnnotations() API (PDFTron WebViewer Class: AnnotationManager)

Use the exported XML String and save it in a custom object / custom metadata. Then when loading a document, look for any associated annotations and automatically load them the same way it is described here: PDFTron Systems Inc. | Documentation (check the getAnnotations() method in Apex)

  1. Use text search to mark items for redaction or use page coordinates to draw your redaction marks: PDFTron Systems Inc. | Documentation

Let me know if that works for you, thanks!

Hi @twinter , I am looking for redaction automation . I want to redact certain regex matching text from document without storing anything in Custom Objects( have a restriction on creation of Custom Object). Is there a way I can actually set a regex which would redact the document automatically when it loads

Sure, the text search has support for regex. In the previous answer (Option 2) you can see how to do a text search. That API supports regex (see here, you just need to pass { regex: true } to the search call: PDFTron WebViewer Namespace: UI )

So for your use case, I would add a callback function in your config.js file for documentLoaded event, perform a call to seachTextFull('your regex here', { regex: true}), use a searchListener to mark any matches for redaction (this will draw redaction rectangles over all search results). If you wish to apply the redactions right away make a call to applyRedactions() on AnnotationManager: PDFTron WebViewer Class: AnnotationManager

Thanks,
Thomas

Sorry @twinter . I am a beginner in terms of JS. My config.js looks like this, to view tha change please go to the bottom of the snippet.

let resourceURL = ‘/resource/’
/**

  • @note If you are using WebViewer <= 7.x, please uncomment the line below, and
  • comment out the line below that
    */
    // const Core = window.CoreControls; // WebViewer <= 7.x
    const Core = window.Core; // WebViewer >= 8.x
    Core.forceBackendType(‘ems’);

const urlSearch = new URLSearchParams(location.hash)
const custom = JSON.parse(urlSearch.get(‘custom’));
resourceURL = resourceURL + custom.namespacePrefix;

/**

  • The following Core.set* functions point WebViewer to the
  • optimized source code specific for the Salesforce platform, to ensure the
  • uploaded files stay under the 5mb limit
    */
    // office workers
    Core.setOfficeWorkerPath(resourceURL + ‘office’)
    Core.setOfficeAsmPath(resourceURL + ‘office_asm’);
    Core.setOfficeResourcePath(resourceURL + ‘office_resource’);

// pdf workers
Core.setPDFResourcePath(resourceURL + ‘resource’)
if (custom.fullAPI) {
Core.setPDFWorkerPath(resourceURL+ ‘pdf_full’)
Core.setPDFAsmPath(resourceURL +‘asm_full’);
} else {
Core.setPDFWorkerPath(resourceURL+ ‘pdf_lean’)
Core.setPDFAsmPath(resourceURL +‘asm_lean’);
}

// external 3rd party libraries
Core.setExternalPath(resourceURL + ‘external’)

window.addEventListener(“message”, receiveMessage, false);

function receiveMessage(event) {
/**

  • @note If you are using WebViewer version <= 7.x, please uncomment the line

  • below
    */
    // const instance = readerControl;
    if (event.isTrusted && typeof event.data === ‘object’) {
    switch (event.data.type) {
    case ‘OPEN_DOCUMENT’:

    instance.loadDocument(event.data.file, {
    officeOptions: {
    disableBrowserFontSubstitution: true,
    }
    })
    break;
    case ‘OPEN_DOCUMENT_BLOB’:
    const { blob, extension, filename, documentId } = event.data.payload;
    instance.loadDocument(blob, { extension, filename, documentId,
    officeOptions: {
    disableBrowserFontSubstitution: true,
    }
    })
    break;
    case ‘CLOSE_DOCUMENT’:
    instance.closeDocument()
    break;
    default:
    break;
    }
    }
    const docViewer = instance.Core.documentViewer;

// you must have a document loaded when calling this api
docViewer.addEventListener(‘documentLoaded’, function() {

const searchListener = (searchTerm, options, results) => {
  // add redaction annotation for each search result
  const newAnnotations = results.map(result => {
      const annotation = new Annotations.RedactionAnnotation();
      annotation.PageNumber = result.pageNum;
      annotation.Quads = result.quads.map(quad => quad.getPoints());
      annotation.StrokeColor = new Annotations.Color(136, 39, 31);
      return annotation;
  });

  annotManager.addAnnotations(newAnnotations);
  annotManager.drawAnnotationsFromList(newAnnotations);
  
};
instance.UI.addSearchListener(searchListener);
instance.UI.searchTextFull('[0-9]', {
  regex: true
});
instance.Core.documentViewer.getAnnotationManager().applyRedactions();

});
}

What am I missing here?

Hi,

You’re very close, I made some slight additions to it / added comments to clarify:

window.addEventListener('documentLoaded', async function() {
    // get annotationManager
    const annotManager = await instance.Core.documentViewer.getAnnotationManager();

    // create search listener callback to draw redaction rectangles
    const searchListener = (searchTerm, options, results) => {
    // add redaction annotation for each search result
    const newAnnotations = results.map(result => {
        const annotation = new Annotations.RedactionAnnotation();
        annotation.PageNumber = result.pageNum;
        annotation.Quads = result.quads.map(quad => quad.getPoints());
        annotation.StrokeColor = new Annotations.Color(136, 39, 31);
        return annotation;
    });

    annotManager.addAnnotations(newAnnotations);
    annotManager.drawAnnotationsFromList(newAnnotations);
};

    //use searchlistener as callback for searches
    instance.addSearchListener(searchListener);

    //search the full document text for regex
    await instance.UI.searchTextFull('[0-9]', {
      regex: true
    })

    // wait half second, then apply redactions + remove yellow search highlight
    setTimeout(async() => { 
        await instance.Core.documentViewer.getAnnotationManager().applyRedactions(); 
        instance.Core.documentViewer.clearSearchResults();  
    }, 500)
  //optionally close search side panel
   instance.UI.closeElements([ 'searchPanel' ]);
})