For the complete documentation index, see llms.txt. This page is also available as Markdown.
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 as

    • Zoom 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.

App.js
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;