Lazy pages and placeholders

A document with many pages should not draw them all at once, and a page should not jump into place when its PDF arrives. This article shows how <pdf-page> waits: lazy pages draw only when they come near, and placeholders keep each page's box in the layout before the PDF is there.

Lazy pages

With loading=lazy, like on an <img>, a page gets its size as soon as the file has loaded, and draws when it comes within one viewport of the visible area, or when the browser finds text in it, focuses it or prints it. In an <iframe>, the viewport is the frame's own, as long as the frame itself is near the visible area:

Lazy pages

Scroll the frame: each page draws as it comes near.

html
<pdf-file id=f src=//pdf.ist/web.pdf></pdf-file>
<pdf-page of=f no=1 loading=lazy scale=0.4></pdf-page>
<pdf-page of=f no=2 loading=lazy scale=0.4></pdf-page>
<pdf-page of=f no=3 loading=lazy scale=0.4></pdf-page>
<pdf-page of=f no=4 loading=lazy scale=0.4></pdf-page>

A lazy page fires load once it has its size and render once it has drawn, and matches :state(pending) until then. A lazy PDFium page also waits to load PDFium, and a file whose pages are all lazy waits to verify its signatures.

Pages have content-visibility: auto built in, too: off-screen pages skip layout and paint, and keep their size.

Placeholders

Before its PDF arrives, a page already has a box, so the layout does not move when the page draws. The box is the first of these that applies:

  1. The page's own size, remembered from an earlier visit.
  2. The --width and --height custom properties you set on the page.
  3. placeholder:aspect-ratio: a paper name or a ratio.
  4. The paper of the visitor's region: US Letter where it is the norm, A4 everywhere else.

Remembered sizes

Every page remembers its size in localStorage, per file and page number. On the next visit, the box is right before the PDF has loaded.

Sizes from CSS

On a first visit, you can give the box yourself, in plain lengths (px, in, cm, mm, pt, pc or q), used as given and not multiplied by scale:

css
pdf-page {
  --width: 210mm;
  --height: 297mm;
}

Paper names and ratios

placeholder:aspect-ratio takes a paper name, such as A4, letter, A5 or legal, in any case, from the names of paper.rt.ht; or a ratio, such as 16/9 or 1.5, which keeps the area of the visitor's paper, so 297/210 is A4 in landscape. It is multi-homed, so set it on the script for every page and on a file for its own:

html
<script src=//new.webpdf.pro/.js type=module placeholder:aspect-ratio=A4></script>
<pdf-file id=slides src=slides.pdf placeholder:aspect-ratio=16/9></pdf-file>

- reserves no box: the page has no size until its PDF arrives.

The visitor's paper

Without any of these, the box is the paper of the visitor's region, from the browser's language: US Letter in the United States, Canada, Mexico, the Philippines and the other countries where it is the norm, A4 everywhere else.

Turning it off

Token on the scriptTurns off
break=content-visibilityThe built-in content-visibility: auto of pages.
break=lazyloading=lazy: every page draws at once.
break=hintRemembered page sizes and the regional paper that reserve boxes before a PDF loads (an explicit placeholder:aspect-ratio still does).

You can also opt one page out of content-visibility with CSS: pdf-page { content-visibility: visible; }.

Printing every page

A lazy page draws when it is printed, but a print starts at once. To print a long document completely, make its lazy pages eager first:

js
await Promise.all([...document.querySelectorAll("pdf-page[loading=lazy]")].map((page) => {
  page.loading = "eager";
  return page.update();
}));
print();

See also