EmbedPDF

Coordinates

Every position in EmbedPDF is one of two kinds. You can tell them apart at a glance, and the compiler won’t let you mix them.

The file’s coordinates — PdfRect#

What the PDF document itself stores. Y grows upward (PDF is a math graph), measured in PDF points, corners named explicitly:

annotation.rect;
// { left: 100, bottom: 700, right: 120, top: 720 }

You’ll meet this shape anywhere you read or write the document: annotation DTOs, creation drafts, patches. It round-trips byte-for-byte with what Acrobat and every other PDF tool sees.

The viewer’s coordinates — Rect#

What you use to put things on screen. Y grows downward (like HTML), same points scale, origin at the page’s top-left:

thread.contentRect;
// { x: 100, y: 80, width: 20, height: 20 }

Every viewer API speaks this shape: stage.reveal(...), selection and search rects, render boxes, overlay anchors.

The rule: stored in the file → PdfRect. Shown on screen → Rect. Wrong one → compile error, because the shapes don’t match.

You usually don’t convert#

APIs hand you the right kind for the job. A comment thread carries both — same sticky note, two jobs:

const threads = useCommentThreads();
const stage = useStage();
 
threads.map((t) => (
  <button key={refKey(t.root.ref)} onClick={() => stage.reveal(t.pageIndex, { rect: t.contentRect })}>
    {t.root.contents} — page {t.pageLabel}
  </button>
));

No math. t.root.rect stays the document’s truth (export it, save it, diff it); t.contentRect is the same box, ready for the viewer.

When you do convert#

Holding a raw annotation and pointing the viewer at it is one call:

const anno = useAnnotation();
stage.reveal(pageIndex, { rect: anno.toContentBox(dto.pageObjectNumber, dto.rect) });

That’s the only file ↔ viewer bridge you’ll ever need.

Pixels are never stored#

Pixels change with every zoom, rotation, and scroll — so no EmbedPDF API stores them. When you draw your own overlay, convert at the last moment:

function Pin({ item, page }: AnnotationRendererProps) {
  // viewer coordinates → this page's pixels (zoom + rotation handled)
  const px = page.transform.toPixels({ x: item.box.x, y: item.box.y });
  return <div style={{ position: 'absolute', left: px.x, top: px.y }}>📌</div>;
}

Going the other way (a pointer event → viewer coordinates) is page.transform.fromPixels(px), and for raw client coordinates the page context has page.toContentPoint(clientX, clientY).

Cheat sheet#

You’re doing…UseShape
Reading / writing annotations, drafts, patchesfile coordinates{ left, bottom, right, top }
reveal, selection & search rects, overlaysviewer coordinates{ x, y, width, height }
Drawing DOM/canvas right nowpage.transformpixels — convert, never store

Bringing your own coordinate system? Viewer coordinates are a plain y-down frame in points — one affine transform maps them into your world, and nothing else in the API will surprise you.

Was this page helpful?

Your feedback goes directly to the documentation team.