ReactUIRedactionEditingAnnotations
Custom UI Enhancements to WebViewer with React
This sample demonstrates several features to customize WebViewer into a React app. The UI customization includes:
How to leverage Apryse's document renderer without an
<iFrame>How to define custom
<button>elements and implement functionality from the Apryse SDK such asZoom In/Out
Drawing Rectangles
Select Tool
Creating and Applying Redactions
How to implement searching using DocViewer Search APIs
WebViewer provides a slick out-of-the-box responsive UI that enables you to view, annotate and manipulate PDFs and other document types inside any web project. Check out getting started with WebViewer for React.
Click the button below to view the full project in GitHub.
import React, { useRef, useEffect, useState } from 'react';
import SearchContainer from './components/SearchContainer';
import { ReactComponent as ZoomIn } from './assets/icons/ic_zoom_in_black_24px.svg';
import { ReactComponent as ZoomOut } from './assets/icons/ic_zoom_out_black_24px.svg';
import { ReactComponent as AnnotationRectangle } from './assets/icons/ic_annotation_square_black_24px.svg';
import { ReactComponent as AnnotationRedact } from './assets/icons/ic_annotation_add_redact_black_24px.svg';
import { ReactComponent as AnnotationApplyRedact} from './assets/icons/ic_annotation_apply_redact_black_24px.svg';
import { ReactComponent as Search } from './assets/icons/ic_search_black_24px.svg';
import { ReactComponent as Select } from './assets/icons/ic_select_black_24px.svg';
import { ReactComponent as EditContent } from './assets/icons/ic_edit_page_24px.svg';
import { ReactComponent as AddParagraph } from './assets/icons/ic_paragraph_24px.svg';
import { ReactComponent as AddImageContent } from './assets/icons/ic_add_image_24px.svg';
import './App.css';
/* global globalThis */
const root = globalThis;
const App = () => {
const viewer = useRef(null);
const scrollView = useRef(null);
const searchTerm = useRef(null);
const searchContainerRef = useRef(null);
const [documentViewer, setDocumentViewer] = useState(null);
const [annotationManager, setAnnotationManager] = useState(null);
const [searchContainerOpen, setSearchContainerOpen] = useState(false);
const [isInContentEditMode, setIsInContentEditMode] = useState(false);
const Annotations = root.Core?.Annotations;
// if using a class, equivalent of componentDidMount
useEffect(() => {
let pollTimer;
const initViewer = () => {
const Core = root.Core;
if (!Core) {
pollTimer = setTimeout(initViewer, 50);
return;
}
Core.setWorkerPath('/webviewer');
Core.enableFullPDF();
const documentViewer = new Core.DocumentViewer();
documentViewer.setScrollViewElement(scrollView.current);
documentViewer.setViewerElement(viewer.current);
documentViewer.enableAnnotations();
documentViewer.loadDocument('/files/demo.pdf');
setDocumentViewer(documentViewer);
documentViewer.addEventListener('documentLoaded', () => {
console.log('document loaded');
documentViewer.setToolMode(documentViewer.getTool(Core.Tools.ToolNames.EDIT));
setAnnotationManager(documentViewer.getAnnotationManager());
});
};
initViewer();
return () => {
if (pollTimer) {
clearTimeout(pollTimer);
}
};
}, []);
const zoomOut = () => {
documentViewer.zoomTo(documentViewer.getZoomLevel() - 0.25);
};
const zoomIn = () => {
documentViewer.zoomTo(documentViewer.getZoomLevel() + 0.25);
};
const startEditingContent = () => {
const contentEditManager = documentViewer.getContentEditManager();
contentEditManager.startContentEditMode();
setIsInContentEditMode(true);
}
const endEditingContent = () => {
setIsInContentEditMode(false);
documentViewer.setToolMode(documentViewer.getTool(root.Core.Tools.ToolNames.EDIT));
const contentEditManager = documentViewer.getContentEditManager();
contentEditManager.endContentEditMode();
}
const addParagraph = () => {
if (isInContentEditMode) {
const addParagraphTool = documentViewer.getTool(root.Core.Tools.ToolNames.ADD_PARAGRAPH);
documentViewer.setToolMode(addParagraphTool);
} else {
alert('Content Edit mode is not enabled.')
}
};
const addImageContent = () => {
if (isInContentEditMode) {
const addImageContentTool = documentViewer.getTool(root.Core.Tools.ToolNames.ADD_IMAGE_CONTENT);
documentViewer.setToolMode(addImageContentTool);
} else {
alert('Content Edit mode is not enabled.')
}
};
const createRectangle = () => {
documentViewer.setToolMode(documentViewer.getTool(root.Core.Tools.ToolNames.RECTANGLE));
};
const selectTool = () => {
documentViewer.setToolMode(documentViewer.getTool(root.Core.Tools.ToolNames.EDIT));
};
const createRedaction = () => {
documentViewer.setToolMode(documentViewer.getTool(root.Core.Tools.ToolNames.REDACTION));
};
const applyRedactions = async () => {
const annotationManager = documentViewer.getAnnotationManager();
annotationManager.enableRedaction();
await annotationManager.applyRedactions();
};
return (
<div className="App">
<div id="main-column">
<div className="center" id="tools">
<button onClick={zoomOut}>
<ZoomOut />
</button>
<button onClick={zoomIn}>
<ZoomIn />
</button>
<button onClick={startEditingContent} title="Switch to edit mode">
<EditContent />
</button>
<button onClick={addParagraph} title="Add new paragraph">
<AddParagraph />
</button>
<button onClick={addImageContent} title="Add new content image">
<AddImageContent />
</button>
<button onClick={endEditingContent} title="End edit mode">
Finish Editing
</button>
<button onClick={createRectangle}>
<AnnotationRectangle />
</button>
<button onClick={createRedaction} title="Create Redaction">
<AnnotationRedact />
</button>
<button onClick={applyRedactions} title="Apply Redactions">
<AnnotationApplyRedact />
</button>
<button onClick={selectTool}>
<Select />
</button>
<button
onClick={() => {
// Flip the boolean
setSearchContainerOpen(prevState => !prevState);
}}
>
<Search />
</button>
</div>
<div className="flexbox-container" id="scroll-view" ref={scrollView}>
<div id="viewer" ref={viewer}></div>
</div>
</div>
<div className="flexbox-container">
<SearchContainer
Annotations={Annotations}
annotationManager={annotationManager}
documentViewer={documentViewer}
searchTermRef={searchTerm}
searchContainerRef={searchContainerRef}
open={searchContainerOpen}
/>
</div>
</div>
);
};
export default App;