Redaction Showcase Demo Sample Code

Requirements
View Demo

Quickly search and redact sensitive text within documents — all handled securely on the client side. Redaction is performed entirely within your private network, ensuring sensitive data never leaves your environment.

This demo allows you to:

  • Support 30+ document types including PDF, MS Office (doc, docx, xlsx, pptx) and Images (jpg, png), all converted to PDF for processing
  • Load form local source or URL
  • Search using free-text or predefined patterns:
    • Phone Numbers
    • Credit Card Numbers
    • Emails
  • Customize search types:
    • Match Case
    • Match Whole Words
    • Wildcard '*'
    • Regular Expressions
    • Search Direction Up
    • Ambient String (returns surrounding strings to matches)
  • Download document as PDF with saved redactions

Implementation steps
To add Redaction capability with WebViewer:

Step 1: Choose your preferred web stack
Step 2: Download required modules listed in the Demo Dependencies section below
Step 3: Add the ES6 JavaScript sample code provided in this guide

Demo Dependencies
This sample uses the following:

Want to see a live version of this demo?

Try the Redaction demo

1// ES6 Compliant Syntax
2// GitHub Copilot v1, Claude Sonnet 4 (Preview), October 5, 2025
3// File: showcase-demos/redaction/index.js
4
5import WebViewer from '@pdftron/webviewer';
6
7// Global variables to track state
8let redactionDemoFile = "https://apryse.s3.amazonaws.com/public/files/samples/sales-invoice-with-credit-cards.pdf";
9const searchResults = []; // Store search results globally for access in other functions
10
11// Function to initialize and load the Redaction Tool
12function initializeWebViewer() {
13
14 const element = document.getElementById('viewer');
15 if (!element) {
16 console.error('Viewer div not found.');
17 return;
18 }
19
20 WebViewer({
21 path: '/lib',
22 initialDoc: redactionDemoFile,
23 licenseKey: 'YOUR_LICENSE_KEY',
24 enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
25 enableRedaction: true, // Enable redaction feature
26 backendType: WebViewer.BackendTypes.WASM, //required for redaction https://community.apryse.com/t/pdfworkererror-related-to-exclusive-lock-in-recursivesharedmutex-cpp-on-emscripten-platform/10059
27 fullAPI: true, // Required to use the PDFNet API
28 loadAsPDF: true,
29 disableElements: ['searchPanel', 'searchButton'], // Disable built-in search to prevent focus errors
30 }, element).then(instance => {
31
32 const { documentViewer } = instance.Core;
33
34 documentViewer.addEventListener('documentLoaded', () => {
35 instance.UI.openElements(['redactionPanel']);
36 instance.UI.disableElements(disabledElements);
37 instance.UI.addSearchListener(searchListener); //Handle search events to capture results for redaction
38 });
39
40 // UI Section
41 createUIElements();
42 });
43}
44
45// Function to apply redactions based on search results
46async function applyRedactions() {
47 const { documentViewer } = window.WebViewer.getInstance().Core;
48 const annotationManager = documentViewer.getAnnotationManager();
49 const annotations = await formatAnnotations(searchResults);
50 console.log('Global results', searchResults);
51
52 //Accessing the annotation manager to add and draw annotations
53 annotationManager.addAnnotations(annotations);
54 annotationManager.drawAnnotationsFromList(annotations);
55
56 // Apply redactions
57 annotationManager.applyRedactions();
58
59 // Clear search results and the searchResults array after applying redactions
60 documentViewer.clearSearchResults();
61 searchResults.length = 0;
62}
63
64// Search Listener, captures search results and adds redaction annotations
65// Only add it once to avoid multiple triggers
66const searchListener = (searchPattern, options, results) => {
67 const { UI } = window.WebViewer.getInstance();
68 addAnnotationsUsingSearchResult(results);
69 if (results.length > 0) {
70 UI.openElements(['redactionPanel']);
71 }
72 else
73 UI.closeElements(['redactionPanel']);
74
75 console.log('Search complete: ', searchPattern, options, results);
76};
77
78// Function to perform search and add redaction annotations
79function search(searchtext, searchOptions) {
80
81 const { documentViewer } = window.WebViewer.getInstance().Core;
82 const { UI } = window.WebViewer.getInstance();
83
84 const annotationManagerObj = documentViewer.getAnnotationManager();
85 const annotationList = annotationManagerObj.getAnnotationsList();
86 annotationManagerObj.deleteAnnotations(annotationList);
87 UI.searchTextFull(searchtext, searchOptions); // Perform the search with given options
88
89}
90
91// Function to format search results into redaction annotations
92async function formatAnnotations(results) {
93 const { documentViewer, Annotations } = window.WebViewer.getInstance().Core;
94 const annotationManager = documentViewer.getAnnotationManager();
95 const redactionList = annotationManager
96 .getAnnotationsList()
97 .filter((annot) => annot instanceof Annotations.RedactionAnnotation);
98
99 return await results.flatMap((r) => {
100 const annotation = new Annotations.RedactionAnnotation();
101 annotation.PageNumber = r.page_num;
102 annotation.Quads = r.quads.map((quad) => quad.getPoints());
103 annotation.StrokeColor = new Annotations.Color(0, 255, 0);
104 annotation.setContents(r.result_str);
105 annotation.Author = 'Guest';
106 annotation.setCustomData(
107 'trn-annot-preview',
108 documentViewer.getSelectedText(annotation.PageNumber)
109 );
110 if (redactionList.some((r) => r.getContents() === annotation.getContents())) {
111 return [];
112 }
113 return [annotation];
114 });
115}
116
117// Function to add annotations using search results
118// This function is called from the search listener
119async function addAnnotationsUsingSearchResult(results) {
120 const { documentViewer } = window.WebViewer.getInstance().Core;
121 const annotationManager = documentViewer.getAnnotationManager();
122
123 //Keep results in global variable to access later if needed
124 searchResults.push(...results);
125 console.log('results', results);
126 const annotations = await formatAnnotations(results);
127 annotationManager.addAnnotations(annotations);
128 annotationManager.drawAnnotationsFromList(annotations);
129};
130
131// Search options for redaction
132// You can modify these options or add more as needed
133const searchOptions = {
134 caseSensitive: true, // match case
135 wholeWord: true, // match whole words only
136 wildcard: false, // allow using '*' as a wildcard value
137 regex: false, // string is treated as a regular expression
138 searchUp: false, // search from the end of the document upwards
139 ambientString: true, // return ambient string as part of the result
140};
141
142// Sample redaction search patterns using regex
143// You can modify or add more patterns as needed
144// WebViewer implements its own pattern similar to these below, here we define our own for the redaction demo
145const redactionSearchSamples = [
146 {
147 label: 'Phone Numbers',
148 value: '\\b(?:\\+?1[-\\s]?)?(?:\\(?[0-9]{3}\\)?[-\\s]?)[0-9]{3}[-\\s]?[0-9]{4}\\b',
149 },
150 {
151 label: 'Emails',
152 value: '\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}\\b',
153 },
154 {
155 label: 'Credit Card Numbers',
156 value: '\\b(?:\\d[ -]*?){13,16}\\b',
157 },
158];
159
160//UI Elements to disable
161const disabledElements = [
162 'toolbarGroup-Shapes',
163 'toolbarGroup-View',
164 'toolbarGroup-Insert',
165 'toolbarGroup-Annotate',
166 'toolbarGroup-FillAndSign',
167 'toolbarGroup-Forms',
168 'toolbarGroup-Edit',
169 'toolbarGroup-Measure',
170];
171
172// UI Elements
173// ui-elements.js
174// Function to create and initialize UI elements
175function createUIElements() {
176 // Create a container for all controls (label, dropdown, and buttons)
177 // Dynamically load ui-elements.js if not already loaded
178 if (!window.SidePanel) {
179 const script = document.createElement('script');
180 script.src = '/showcase-demos/redaction/ui-elements.js';
181 script.onload = () => {
182 UIElements.init('viewer', searchResults);
183 UIElements.handleException(); //Add handling of Reacts focus error on this JavaScript sample.
184 };
185 document.head.appendChild(script);
186 }
187}
188
189//Make functions accessible globally
190window.redactionSearchSamples = redactionSearchSamples;
191window.searchOptions = searchOptions;
192window.applyRedactions = applyRedactions;
193window.search = search;
194
195// Initialize the WebViewer
196initializeWebViewer();
197

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales