---
title: "Coordinate space conversion in iOS PDF viewer | Nutrient SDK"
canonical_url: "https://www.nutrient.io/guides/ios/faq/coordinate-spaces/"
md_url: "https://www.nutrient.io/guides/ios/faq/coordinate-spaces.md"
last_updated: "2026-06-08T17:11:05.557Z"
description: "By specification, PDF documents have their own coordinate space, which is different from the coordinate space used by UIKit."
---

# Coordinate space conversion

By specification, PDF documents have their own coordinate space, which is different from the coordinate space used by UIKit. While UIKit has its origin in the top-left corner, with y coordinates increasing in a _downward_ direction, the y coordinates in PDF documents increase _upward_, starting in the bottom-left corner.![PDF page coordinates](@/assets/guides/shared/faq/document-coordinate-space.png)

Additionally, the PDF coordinate space can be offset from the visible bottom-left corner of a page due to a crop box, and it can also be rotated due to the page being rotated. This is efficient because it means cropping or rotating a page is just setting a value: You don’t need to change the content stream or any of the 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 bottom-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.

Nutrient provides an easy-to-use API for transforming between the PDF coordinate space and other coordinate spaces. For example, the following code uses the Nutrient conversion API to retrieve the onscreen coordinates of an annotation:

### SWIFT

```swift

let pdfController: PSPDFViewController =...
let annotation: PSPDFAnnotation =...
if let pageView = pdfController.pageViewForPage(at: annotation.pageIndex) {
    // Get the (normalized) PDF coordinates of the annotation.
    let annotationPDFRect = annotation.boundingBox
    // Convert the annotation's PDF coordinates to view coordinates.
    let annotationViewRect = pageView.convert(rect: annotationPDFRect, from: pageView.pdfCoordinateSpace)
}

```

### OBJECTIVE-C

```objc

PSPDFViewController *pdfController =...;
PSPDFAnnotation *annotation =...;
PSPDFPageView *pageView = [pdfController pageViewForPageAtIndex:annotation.pageIndex];
if (pageView) {
    // Get the (normalized) PDF coordinates of the annotation.
    CGRect annotationPDFRect = annotation.boundingBox;
    // Convert the annotation's PDF coordinates to view coordinates.
    CGRect annotationViewRect = [pageView convertRect:annotationPDFRect fromCoordinateSpace:pageView.pdfCoordinateSpace];
}

```

## 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 XFDF of a rectangle annotation like so:

```xml

<!-- 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, but if we want to calculate the distance between the top side of the rectangle and the top of the page (Nutrient bounding boxes use width, height, left, and top values), we can 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:

```json

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

```

## Conversion with page views

[`PSPDFPageView`](https://www.nutrient.io/api/ios/documentation/pspdfkitui/pdfpageview) has a `pdfCoordinateSpace` property, which is an object conforming to the [`UICoordinateSpace`](https://developer.apple.com/documentation/uikit/uicoordinatespace) protocol. This represents the coordinate space of that page view’s PDF page. Each `UIView` also conforms to [`UICoordinateSpace`](https://developer.apple.com/documentation/uikit/uicoordinatespace). Therefore, to convert between PDF page and view coordinates, you can call the following methods from [`UICoordinateSpace`](https://developer.apple.com/documentation/uikit/uicoordinatespace) on either a PDF coordinate space or a view:

### SWIFT

```swift

public func convert(_ point: CGPoint, to coordinateSpace: UICoordinateSpace) -> CGPoint
public func convert(_ point: CGPoint, from coordinateSpace: UICoordinateSpace) -> CGPoint
public func convert(_ rect: CGRect, to coordinateSpace: UICoordinateSpace) -> CGRect
public func convert(_ rect: CGRect, from coordinateSpace: UICoordinateSpace) -> CGRect

```

### OBJECTIVE-C

```objc

- (CGPoint)convertPoint:(CGPoint)point toCoordinateSpace:(id <UICoordinateSpace>)coordinateSpace;

- (CGPoint)convertPoint:(CGPoint)point fromCoordinateSpace:(id <UICoordinateSpace>)coordinateSpace;

- (CGRect)convertRect:(CGRect)rect toCoordinateSpace:(id <UICoordinateSpace>)coordinateSpace;

- (CGRect)convertRect:(CGRect)rect fromCoordinateSpace:(id <UICoordinateSpace>)coordinateSpace;

```

## Page info

[`PSPDFPageInfo`](https://www.nutrient.io/api/ios/documentation/pspdfkit/pdfpageinfo) can be used to get information for a specific page in a document. You can get this object by retrieving the [`PSPDFPageInfo`](https://www.nutrient.io/api/ios/documentation/pspdfkit/pdfpageinfo) object for a specific page on a [`PSPDFDocument`](https://www.nutrient.io/api/ios/documentation/pspdfkit/document) using [`pageInfoForPageAtIndex:`](https://www.nutrient.io/api/ios/documentation/pspdfkit/document/pageinfoforpage(at:)).

[`PSPDFPageInfo`](https://www.nutrient.io/api/ios/documentation/pspdfkit/pdfpageinfo) includes information like the size of a page, transforms, and page rotation.

## Copying annotations across rotated pages

Note that some PDF files can also have rotation set on pages. You can get this value by checking the [`savedRotation`](https://www.nutrient.io/api/ios/documentation/pspdfkit/pdfpageinfo/savedrotation) property. This can be a value between 0 and 270 in 90-degree steps.

When a PDF page is rotated in the context of a PDF editor, it gets a rotation property assigned. However, its dimensions remain the same. See the attached screenshot showing the page information for the same page with different rotations applied.![page info](@/assets/guides/shared/faq/pageInfo.png)

Annotations in these rotated pages are positioned relative to the page dimensions. So if you, for example, copy an annotation on one page and then paste it onto the same page with an applied rotation, it’ll end up at the same position on the rotated page.![page info](@/assets/guides/shared/faq/rotation.gif)

If the page is generated so it appears rotated without the rotation property applied (e.g. by generating a fresh PDF from a rotated image source), the copied annotation will no longer match the position and orientation in the original document. Instead of having a dimension of width × height with, say, 90 degrees of rotation, you now have a page of dimension height × width with 0-degree rotation. The annotations thus end up at the wrong positions in this new coordinate space.

The simplest solution is to ensure the coordinate spaces match. That means that rather than rotating the image, you create a PDF first and then rotate the page properly with a PDF editor or PDF library like Nutrient to preserve the rotation information. Then, copying annotations will just work.

If that’s not an option, you’ll need to update the annotation bounding boxes (and potentially even the annotation contents) to compensate for the switch in coordinates. Nutrient doesn’t provide a helper that would do that for you, because we don’t know that it’s the same page, just with the source content rotated. The bounding box rects need to be transformed and then applied to annotations. Using `setBoundingBox:transform:includeOptional:` might be useful to update the contents of the annotation if that’s also needed (passing `transform: YES`).

## Conversion with other views

There are also conversion functions that can be used with any view. There is rarely a case where you need to use these functions, as the above property on [`PSPDFPageView`](https://www.nutrient.io/api/ios/documentation/pspdfkitui/pdfpageview) is better suited for most use cases. Use these functions only when the page is shown in a view other than a [`PSPDFPageView`](https://www.nutrient.io/api/ios/documentation/pspdfkitui/pdfpageview). For these to work correctly, the page must fill the view’s bounds:

### SWIFT

```swift

func PSPDFConvertViewPointToPDFPoint(_ viewPoint: CGPoint, _ pageInfo: PSPDFPageInfo, _ viewBounds: CGRect) -> CGPoint
func PSPDFConvertPDFPointToViewPoint(_ pdfPoint: CGPoint, _ pageInfo: PSPDFPageInfo, _ viewBounds: CGRect) -> CGPoint
func PSPDFConvertPDFRectToViewRect(_ pdfRect: CGRect, _ pageInfo: PSPDFPageInfo, _ viewBounds: CGRect) -> CGRect
func PSPDFConvertViewRectToPDFRect(_ viewRect: CGRect, _ pageInfo: PSPDFPageInfo, _ viewBounds: CGRect) -> CGRect

```

### OBJECTIVE-C

```objc

CGPoint PSPDFConvertViewPointToPDFPoint(CGPoint viewPoint, PSPDFPageInfo *pageInfo, CGRect viewBounds);
CGPoint PSPDFConvertPDFPointToViewPoint(CGPoint pdfPoint, PSPDFPageInfo *pageInfo, CGRect viewBounds);
CGRect PSPDFConvertPDFRectToViewRect(CGRect pdfRect, PSPDFPageInfo *pageInfo, CGRect viewBounds);
CGRect PSPDFConvertViewRectToPDFRect(CGRect viewRect, PSPDFPageInfo *pageInfo, CGRect viewBounds);

```

To learn how to convert points, lines, and rectangles from view coordinates to PDF coordinates, refer to the next sections.

### Converting points

### SWIFT

```swift

let annotation: Annotation =...
let pageIndex =...
let pageInfo = document.pageInfoForPage(at: pageIndex)!
let view: UIView =... // Some `UIView` that displays a PDF page filling the view.
let viewBounds = view.bounds

let viewPoint = CGPoint(x: 100, y: 200)

// Convert view points into PDF points.
let pdfPoint = PSPDFConvertViewPointToPDFPoint(viewPoint, pageInfo, viewBounds)
annotation.points = [pdfPoint]

```

### OBJECTIVE-C

```objc

PSPDFAnnotation *annotation =...
PSPDFPageIndex pageIndex =...
PSPDFPageInfo *pageInfo = [document pageInfoForPageAtIndex:pageIndex];
UIView *view =... // Some `UIView` that displays a PDF page filling the view.
CGRect viewBounds = view.bounds;

CGPoint viewPoint = CGPointMake(100, 200);

// Convert view points into PDF points.
CGPoint pdfPoint = PSPDFConvertViewPointToPDFPoint(viewPoint, pageInfo, viewBounds);
annotation.points = @[@(pdfPoint)];

```

### Converting lines

### SWIFT

```swift

let inkAnnotation: InkAnnotation =...
let pageIndex =...
let pageInfo = document.pageInfoForPage(at: pageIndex)!
let view: UIView =... // Some `UIView` that displays a PDF page filling the view.
let viewBounds = view.bounds

let viewLines = [
	[(CGPoint(x: 100, y: 100)), (CGPoint(x: 100, y: 200)), (CGPoint(x: 150, y: 300))],
	[(CGPoint(x: 200, y: 100)), (CGPoint(x: 200, y: 200)), (CGPoint(x: 250, y: 300))]
]

// Convert view line points into PDF line points.
let pdfLines = ConvertToPDFLines(viewLines: viewLines, pageInfo: pageInfo, viewBounds: viewBounds)
inkAnnotation.lines = pdfLines

```

### OBJECTIVE-C

```objc

PSPDFInkAnnotation *inkAnnotation =...
PSPDFPageIndex pageIndex =...
PSPDFPageInfo *pageInfo = [document pageInfoForPageAtIndex:pageIndex];
UIView *view =... // Some `UIView` that displays a PDF page filling the view.
CGRect viewBounds = view.bounds;

NSArray *viewLines = @[
    @[@(CGPointMake(100, 100)), @(CGPointMake(100, 200)), @(CGPointMake(150, 300))],
    @[@(CGPointMake(200, 100)), @(CGPointMake(200, 200)), @(CGPointMake(250, 300))]
];

// Convert view line points into PDF line points.
NSArray *pdfLines = PSPDFConvertViewLinesToPDFLines(viewLines, pageInfo, viewBounds);
inkAnnotation.lines = pdfLines;

```

### Converting rectangles

### SWIFT

```swift

let annotation: Annotation =...
let pageIndex =...
let pageInfo = document.pageInfoForPage(at: pageIndex)!
let view: UIView =... // Some `UIView` that displays a PDF page filling the view.
let viewBounds = view.bounds

let viewRect = CGRect(x: 100, y: 100, width: 300, height: 300)

// Convert view rects into PDF rects.
let pdfRect = PSPDFConvertViewRectToPDFRect(viewRect, pageInfo, viewBounds)
annotation.rects = [pdfRect]

```

### OBJECTIVE-C

```objc

PSPDFAnnotation *annotation =...
PSPDFPageIndex pageIndex =...
PSPDFPageInfo *pageInfo = [document pageInfoForPageAtIndex:pageIndex];
UIView *view =... // Some `UIView` that displays a PDF page filling the view.
CGRect viewBounds = view.bounds;

CGRect viewRect = CGRectMake(100, 100, 300, 300);

// Convert view rects into PDF rects.
CGRect pdfRect = PSPDFConvertViewRectToPDFRect(viewRect, pageInfo, viewBounds);
annotation.rects = @[@(pdfRect)];

```

## Converting between the normalized PDF coordinate space and the raw PDF coordinate space

The Nutrient API uses a normalized page coordinate space, which always puts the origin in the bottom-left corner of the visible area of the page.

You can convert from the raw PDF coordinate space to Nutrient’s normalized space using the [`transform`](https://www.nutrient.io/api/ios/documentation/pspdfkit/pdfpageinfo/transform) property of [`PSPDFPageInfo`](https://www.nutrient.io/api/ios/documentation/pspdfkit/pdfpageinfo). For example, to place a 100-point by 100-point square annotation at the page origin stored in the PDF, use code like the following:

### SWIFT

```swift

let document: PSPDFDocument =...
let pageInfo = document.pageInfoForPage(at: 13)
let rawPDFRect = CGRect(x: 0, y: 0, width: 100, height: 100)
let normalizedPDFRect = rawPDFRect.applying(pageInfo.transform)

let annotation: PSPDFSquareAnnotation =...
annotation.boundingBox = normalizedPDFRect

```

### OBJECTIVE-C

```objc

PSPDFDocument *document =...
PSPDFPageInfo *pageInfo = [document pageInfoForPageAtIndex:13];
CGRect rawPDFRect = CGRectMake(0, 0, 100, 100);
CGRect normalizedPDFRect = CGRectApplyAffineTransform(rawPDFRect, pageInfo.transform);

PSPDFSquareAnnotation *annotation =...
annotation.boundingBox = normalizedPDFRect;

```

The annotation might not end up visible if there is a crop box offset.

To convert from the normalized PDF coordinate space used by Nutrient to the raw PDF coordinate space, use the inverse of the page info’s [`transform`](https://www.nutrient.io/api/ios/documentation/pspdfkit/pdfpageinfo/transform). For example, to read an annotation’s bounding box as it would be serialized in the PDF, use the following:

### SWIFT

```swift

let document: PSPDFDocument =...
let pageInfo = document.pageInfoForPage(at: 13)
let annotation: PSPDFAnnotation =...

let normalizedPDFRect = annotation.boundingBox
let rawPDFRect = normalizedPDFRect.applying(pageInfo.transform.inverted())

```

### OBJECTIVE-C

```objc

PSPDFDocument *document =...
PSPDFPageInfo *pageInfo = [document pageInfoForPageAtIndex:13];
PSPDFAnnotation *annotation =...

CGRect normalizedPDFRect = annotation.boundingBox;
CGRect rawPDFRect = CGRectApplyAffineTransform(normalizedPDFRect, CGAffineTransformInvert(pageInfo.transform));

```

## How to convert between raster image pixels and points

The concept of resolution does not apply to PDF documents unless they have been converted into raster images, i.e. images whose dimensions are expressed in pixels. The default unit Nutrient returns for page sizes is the point, which is easily converted into inches by considering the fact that 1 inch is equal to 72 points. 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, these are the relations you have to consider when you need to convert between points/inches and pixels in PDF:

1 inch = 72 points

Inch separation = points / 72

DPI (resolution) = pixels / inch separation

Note that, since PDF 1.6, the relationship between inches and points may be specified as greater than 1⁄72 by means of the `UserUnit` entry of the page dictionary. See table 30 on page 79 of the [PDF 1.7 specification](https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf) for more information.
---

## Related pages

- [Rendering annotations in our iOS PDF viewer](/guides/ios/getting-started/rendering-annotations.md)
- [Customize PDF fonts on iOS](/guides/ios/features/custom-fonts.md)
- [PDF rendering library for iOS](/guides/ios/getting-started/rendering-pdf-pages.md)
- [Rendering PDF forms in our iOS viewer](/guides/ios/viewer/rendering/pdf-forms.md)

