> 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/extraction/text-position.md).

# Get text position in PDF documents

Learn how to get text positions in WebViewer using Quad objects. Use the getTextPosition function to locate individual characters on a page for creating annotations. Find examples and related info her

Text positions in WebViewer are represented by [`Quad`](https://sdk.apryse.com/api/web/Core.Math.Quad.html) objects. `Quads` are made up of 4 pairs of XY coordinates that represent the 4 corners of a rectangle.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-1c53c65e07402b413e3fe4790fbda7f1d25e2115%2F8f176a9cdc6b36016fa1d4093206f5dfa46a0770-501x241.png?alt=media)

Text that is split up across multiple lines or columns would be represented by an array of `Quad` objects, one `Quad` for each section.

## Getting text position

The string received by the `loadPageText` callback function can be used by the [`getTextPosition`](https://sdk.apryse.com/api/web/Core.Document.html#getTextPosition__anchor) function to get the position of individual character on a page. This can be useful for creating annotations on top of text. An example of `getTextPosition` being used with `loadPageText` can be found below

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { documentViewer } = instance.Core;

    documentViewer.addEventListener('documentLoaded', async () => {
      const doc = documentViewer.getDocument();
      const searchTerm = 'TEXT TO SEARCH';
      const pageNumber = 1; // page to parse

      const pageText = await doc.loadPageText(pageNumber);
      let startIndex = 0;
      let endIndex = 0;

      while (startIndex > -1) {
        startIndex = pageText.indexOf(searchTerm, endIndex);
        endIndex = startIndex + searchTerm.length;

        // Get text position for each letter  in the 'searchTerm' found
        // 'quads' will contain an array for each character between the start and end indexes
        const quads = await doc.getTextPosition(pageNumber, startIndex, endIndex);
      }
    })
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [DocumentViewer#documentLoaded](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:documentLoaded__anchor) [DocumentViewer.getDocument](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#getDocument__anchor) [Document.loadPageText](https://sdk.apryse.com/api/web/Core.Document.html#loadPageText__anchor) [Document.getTextPosition](https://sdk.apryse.com/api/web/Core.Document.html#getTextPosition__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v7.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { docViewer } = instance;

    docViewer.on('documentLoaded', async () => {
      const doc = docViewer.getDocument();
      const searchTerm = 'TEXT TO SEARCH';
      const pageNumber = 1; // page to parse

      const pageText = await doc.loadPageText(pageNumber);
      let startIndex = 0;
      let endIndex = 0;

      while (startIndex > -1) {
        startIndex = pageText.indexOf(searchTerm, endIndex);
        endIndex = startIndex + searchTerm.length;

        // Get text position for each letter  in the 'searchTerm' found
        // 'quads' will contain an array for each character between the start and end indexes
        const quads = await doc.getTextPosition(pageNumber, startIndex, endIndex);
      }
    })
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [DocumentViewer#documentLoaded](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:documentLoaded__anchor) [DocumentViewer.getDocument](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#getDocument__anchor) [Document.loadPageText](https://sdk.apryse.com/api/web/Core.Document.html#loadPageText__anchor) [Document.getTextPosition](https://sdk.apryse.com/api/web/Core.Document.html#getTextPosition__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { docViewer } = instance;

    docViewer.on('documentLoaded', () => {
      const doc = docViewer.getDocument();
      const searchTerm = 'TEXT TO SEARCH';
      const pageIndex = 0; // page to parse

      doc.loadPageText(pageIndex, pageText => {
        const getTextPositionCallback = quads => {
          // 'quads' will contain an array for each character between the start and end indexes
        }
        let startIndex = 0;
        let endIndex = 0;

        while (startIndex > -1) {
          startIndex = pageText.indexOf(searchTerm, endIndex);
          endIndex = startIndex + searchTerm.length;

          // Get text position for each letter  in the 'searchTerm' found
          doc.getTextPosition(pageIndex, startIndex, endIndex, getTextPositionCallback);
        }
      });
    })
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [DocumentViewer#documentLoaded](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#event:documentLoaded__anchor) [DocumentViewer.getDocument](https://sdk.apryse.com/api/web/Core.DocumentViewer.html#getDocument__anchor) [Document.loadPageText](https://sdk.apryse.com/api/web/Core.Document.html#loadPageText__anchor) [Document.getTextPosition](https://sdk.apryse.com/api/web/Core.Document.html#getTextPosition__anchor)
{% endtab %}
{% endtabs %}

The `getTextPosition` callback function will receive an array of [`Quad`](https://sdk.apryse.com/api/web/Core.Math.Quad.html) objects for each letter between the start and end indexes. This can be used to create annotations on top of text by setting an annotation 'Quads' to be the array received by the callback function. An example of this can be found below

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { Annotations, annotationManager } = instance.Core;

    const getTextPositionCallback = quads => {
      // quads will be an array of objects with x1,y1,x2,y2,x3,y3,x4, and y4 values. Each of these object will be for a letter in the search text
      const annotation = new Annotations.TextHighlightAnnotation();
      annotation.PageNumber = pageIndex + 1;

      annotation.Quads = quads;
      annotation.StrokeColor = new Annotations.Color(136, 39, 31);
      annotationManager.addAnnotation(annotation);
      annotationManager.redrawAnnotation(annotation);
    }
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [AnnotationManager.addAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#addAnnotation__anchor) [AnnotationManager.redrawAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#redrawAnnotation__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v7.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { Annotations, annotManager } = instance;

    const getTextPositionCallback = quads => {
      // quads will be an array of objects with x1,y1,x2,y2,x3,y3,x4, and y4 values. Each of these object will be for a letter in the search text
      const annotation = new Annotations.TextHighlightAnnotation();
      annotation.PageNumber = pageIndex + 1;

      annotation.Quads = quads;
      annotation.StrokeColor = new Annotations.Color(136, 39, 31);
      annotationManager.addAnnotation(annotation);
      annotationManager.redrawAnnotation(annotation);
    }
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [AnnotationManager.addAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#addAnnotation__anchor) [AnnotationManager.redrawAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#redrawAnnotation__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  .then(instance => {
    const { Annotations, annotManager } = instance;

    const getTextPositionCallback = quads => {
      // quads will be an array of objects with x1,y1,x2,y2,x3,y3,x4, and y4 values. Each of these object will be for a letter in the search text
      const annotation = new Annotations.TextHighlightAnnotation();
      annotation.PageNumber = pageIndex + 1;

      annotation.Quads = quads;
      annotation.StrokeColor = new Annotations.Color(136, 39, 31);
      annotationManager.addAnnotation(annotation);
      annotationManager.redrawAnnotation(annotation);
    }
  })
```

{% endcode %}

[WebViewerInstance](https://sdk.apryse.com/api/web/WebViewerInstance.html) [AnnotationManager.addAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#addAnnotation__anchor) [AnnotationManager.redrawAnnotation](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#redrawAnnotation__anchor)
{% endtab %}
{% endtabs %}

## Related

* [Selected text](/web/extraction/selected-text.md)
* [Text search](/web/search/text-search.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/extraction/text-position.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.
