This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/web/pspdfkit-for-web/coordinate-spaces.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. Find PDF coordinates with JavaScript | Nutrient SDK

By specification, PDF documents have their own coordinate space, which differs from the one Nutrient Web SDK uses. Web APIs have their origin in the top-left corner, with y coordinates increasing downward. In contrast, y coordinates in PDF documents increase upward, starting in the bottom-left corner.

PDF page coordinates

A crop box can offset the PDF coordinate space from the visible bottom-left corner of a page. Rotating a page also rotates the coordinate space. This approach is efficient because cropping or rotating a page only sets a value. You don’t need to change the content stream or any annotations on the page. However, this can also be confusing to work with. Therefore, Nutrient exposes a normalized page coordinate space, which always puts the origin in the top-left corner of the visible area of the page.

Our Instant JSON format uses a coordinate space where the origin is the top-left corner of the page, with the y-axis increasing downward.

Obtaining page dimensions

Use the getPageInfoForIndex() function to fetch the dimensions of a page. Some PDF files also set a rotation on pages. You can get this value from the rotation property of a page. The rotation is a value between 0 and 270 in 90-degree increments.

Convert a click to page coordinates

For clicks on a page, listen for the page.press event. The event reports pageIndex and a point that’s already in page coordinates, ready for an annotation’s boundingBox:

instance.addEventListener("page.press", (event) => {
console.log(event.pageIndex, event.point.x, event.point.y);
});

page.press doesn’t fire when the click selects an annotation, such as a note or a rectangle’s outline. It also doesn’t fire when the click opens a signature field’s signing dialog or follows a link. Some other clicks still fire it — for example, on a read-only highlight; inside an unfilled rectangle; or on a text field, checkbox, or custom overlay. To stop it for an overlay, see handle clicks on PDF custom overlays.

Right and middle clicks also fire page.press, and event.nativeEvent.button identifies them. Releasing the mouse after selecting text fires it too. To ignore a press that ends away from where it started, see the note placement example.

For other document object model (DOM) events from inside the viewer, such as drop, add the listener to instance.contentDocument. Convert the event’s clientX and clientY with instance.transformClientToPageSpace(). With the deprecated useIframe option, the viewer runs in its own frame and its event coordinates are relative to that frame, so use instance.transformContentClientToPageSpace() instead.

transformClientToPageSpace() takes a page index. To find the page under the pointer, read data-page-index from the .PSPDFKit-Page element in event.composedPath().

For complete examples, see the note placement example and the image drop example.

Understanding XFDF/PDF rects

In addition to having their own coordinate spaces, PDFs also represent the bounding box of an annotation differently than Nutrient. For example, consider the case where we export the XML Forms Data Format (XFDF) of a rectangle annotation like so:

<!-- Other attributes omitted for clarity -->
<square rect="50.000000, 100.000000, 80.000000, 120.000000" />

The rect attribute contains the following information in this order:

  • The left side of the rectangle is 50 units from the left of the page.
  • The bottom side of the rectangle is 100 units from the bottom of the page.
  • The right side of the rectangle is 80 units from the left of the page.
  • The top side of the rectangle is 120 units from the bottom of the page.

The width of the rectangle annotation is 30 units (80-50) and the height is 20 units (120-100).

We already have the left value. Nutrient bounding boxes use width, height, left, and top values, so we need the distance between the rectangle’s top and the page’s top. To calculate it, subtract the XFDF top value from the height of the page. With a page height of 800, the adjusted top value would be 680 (800-120).

Thus, the equivalent Nutrient bounding box would be:

{
"top": 680,
"left": 50,
"width": 30,
"height": 20
}

How to convert between raster image pixels and points

Resolution doesn’t apply to PDF documents unless you convert them into raster images, i.e. images whose dimensions are in pixels. The default unit Nutrient returns for page sizes is the point. Because 1 inch equals 72 points, you can convert points into inches. Inch separation results from dividing the size in points of a particular page by 72. Resolution, expressed in DPI (dots per inch), is thus the result of dividing the page size in points by the inch separation. To summarize, use these relations when you convert between points/inches and pixels in PDF:

1 inch = 72 points
Inch separation = points / 72
DPI (resolution) = pixels / inch separation

Since PDF 1.6, the UserUnit entry of the page dictionary can specify a relationship between inches and points greater than 1⁄72. See table 30 on page 79 of the PDF 1.7 specification(opens in a new tab) for more information.