> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/redaction/search-and-redact.md).

# Search and Redact

Learn how to set up a programatic search and redact workflow in WebViewer with this comprehensive guide. Enable redaction, search for text, and create redactions step-by-step. Optimize your document w

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="https://apryse.com/capabilities#Redaction" class="button primary">Package: Redaction</a><a href="https://showcase.apryse.com/redaction" class="button primary">Live demo</a>
{% endhint %}

A common workflow in WebViewer is a programatic search and redact - search a document for text and permanently removing it from the document.

There are four steps involved in setting up this workflow. This guide will walk you through each step and explain the core concepts in setting up this workflow.

## Step 1 - Enable redaction

As mentioned in the [setup redaction guide](/web/redaction/redaction-setup.md), there are a few parameters you need to pass to the WebViewer constructor to get redaction working. The two properties are `fullAPI` and `enableRedaction`.

In your WebViewer constructor call, ensure you are setting both those properties to `true`.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer(
    {
        path: '/webviewer/lib',
        fullAPI: true,
        enableRedaction: true
    },
    document.getElementById('viewer'),
)
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Step 2 - Search for text

Once redactions are enabled, we can begin implementing our workflow by searching for the text we want to redact. The easiest way to do this is using the [`textSearchInit`](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#textSearchInit__anchor) API.

This API accepts a string or a regex to search for, as well as some additional options and callbacks.

Using the `textSearchInit` function looks like this:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
const { documentViewer, Search } = instance;

// The text to search for
const searchText = "redact me"

// Set the mode for the search. See the documentation for more options
const modes = [Search.Mode.PAGE_STOP, Search.Mode.HIGHLIGHT];

const searchOptions = {
  // search the entire document
  fullSearch: true,
  onDocumentEnd: () => {
    // called when search is complete
  },
  onResult: (result) => {
    // called when a search result is found
    console.log(result);
  },
}
documentViewer.textSearchInit(searchText, modes, searchOptions);
```

{% endcode %}
{% endtab %}
{% endtabs %}

[SearchModes](https://sdk.apryse.com/api/web/Core.Search.html#.Mode) [textSearchInit](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#textSearchInit__anchor)

There are a few main parts to look at here.

The `mode` parameter sets the search mode. [There are many different options here](https://sdk.apryse.com/api/web/Core.Search.html#.Mode), and you can set one or many search modes by passing them as an array. For example, if you wanted to use a wildcard ("\*") in your search pattern, you would have to append the `WILD_CARD` mode like so:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
const modes = [Search.Mode.PAGE_STOP, Search.Mode.HIGHLIGHT, Search.Mode.WILD_CARD]
```

{% endcode %}
{% endtab %}
{% endtabs %}

The searchOptions parameter lets you provide a set of callbacks and additional options for your search. A full list list of all the options can be seen [here](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#textSearchInit__anchor). In this workflow, we care about `fullSearch`, `onResult`, and `onDocumentEnd`.

### Regex search

You can also search for text using a regular expression by setting the `REGEX` search mode and passing in a regex expression as your search query. This would look something like this:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
const { documentViewer, Search } = instance;

// The regex to search for
const searchText = "text1|text2"

// Set the mode for the search. See the documentation for more options
const modes = [Search.Mode.PAGE_STOP, Search.Mode.HIGHLIGHT, Search.Mode.REGEX];
const searchOptions = {
  ...
}

documentViewer.textSearchInit(searchText, modes, searchOptions);
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Step 3 - Create redactions

Now that we have our search code implemented, we can use the results of that search to programmatically create redaction annotations. This process involves using the `onResult` callback and using the `result` object to get the coordinates of the text we want to redact. Using these coordinates, we can place a redaction annotation on the document.

The implementation will look something like this:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
const { documentViewer, Search, annotationManager, Annotations } = instance;

// The text to search for
const searchText = "Redact me"

// Set the mode for the search. See the documentation for more options
const modes = [Search.Mode.PAGE_STOP, Search.Mode.HIGHLIGHT];

const searchOptions = {
  // search the entire document
  fullSearch: true,
  onDocumentEnd: () => {
    // called when search is complete
  },
  onResult: (result) => {
    if (result.resultCode === Search.ResultCode.FOUND) {
  
      // Get the page number and the quads for the search result
      const { pageNum, quads } = result;
      
      // Create a new redaction annotation using the quads and page number
      const redactAnnot = new Annotations.RedactionAnnotation({
        PageNumber: pageNum,
        Quads: quads.map((quad) => quad.getPoints())
      });
      
      // Apply and redraw the redaction annotation
      annotationManager.addAnnotations([redactAnnot]);
      annotationManager.drawAnnotationsFromList([redactAnnot]);
    }
  },
}
documentViewer.textSearchInit(searchText, modes, searchOptions);

```

{% endcode %}
{% endtab %}
{% endtabs %}

At this point, you should be able to search for text and place a redaction annotation on top of all the results. The last step is to apply those redactions to permanently remove the text from the document.

## Step 4 - Apply redactions

At this point we have only placed redaction annotations on the document and have not actually removed the content from the underlying document. To do the actual content removal, we need to "apply" the redactions.

To do this, we can call the [`annotationManager.applyRedactions`](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#applyRedactions) API, passing in all the redactions we created in step 3.

We want to do this after the search is complete, so we can add the code into the `onDocumentEnd` callback.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
const { documentViewer, Search, annotationManager, Annotations } = instance;

// The text to search for
const searchText = "Redact me"

// Set the mode for the search. See the documentation for more options
const modes = [Search.Mode.PAGE_STOP, Search.Mode.HIGHLIGHT];

const searchOptions = {
  // search the entire document
  fullSearch: true,
  onDocumentEnd: () => {
    // get all the redaction annotations on the document and apply them
    const redactionList = annotationManager.getAnnotationsList().filter(annot => annot instanceof Annotations.RedactionAnnotation);
    annotationManager.applyRedactions(redactionList)
  },
  onResult: (result) => {
    // onResult code from previous steps omitted for brevity
  },
}

documentViewer.textSearchInit(searchText, modes, searchOptions);
```

{% endcode %}
{% endtab %}
{% endtabs %}

After calling the above code, the redactions should be applied to the underlying document and the text you searched for should be permanently removed!

## Next steps

* [Download the redacted document](/web/open-save-document/save.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/redaction/search-and-redact.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
