Redaction Showcase Demo Code Sample

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: Add the ES6 JavaScript sample code provided in this guide.

Once you generate your license key, it will automatically be included in your sample code below.

License Key

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