# Asset viewers

The asset viewers show an asset's content in a box of your page: an image fitted to it, a website's screenshot with a link to the page, text, a PDF or Office document a page at a time, or a spreadsheet's sheets. Each one fills the element it renders, so it suits a detail page, a review screen or a full-window frame. Video and audio play in the [asset component](/docs/widgets/asset-component). [Widgets](/docs/widgets) covers installing, styling and translating.

Asset viewers (preview)

## Choose a viewer

| Asset                 | React, Vue and Svelte       | Custom element                  | What it shows                                                                                           |
| --------------------- | --------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `image` rendered file | `CalmLensImageViewer`       | `<calmlens-image-viewer>`       | The image, fitted inside the element, over its blurred placeholder until it loads                       |
| `website`             | `CalmLensWebsiteViewer`     | `<calmlens-website-viewer>`     | A link to the original page, then the page's screenshot at the element's width, scrolling down the page |
| `plaintext-document`  | `CalmLensTextViewer`        | `<calmlens-text-viewer>`        | The text, wrapped and scrolling                                                                         |
| `pdf` rendered file   | `CalmLensDocumentViewer`    | `<calmlens-document-viewer>`    | A PDF, or a document or presentation rendered as one, a page at a time                                  |
| Spreadsheet           | `CalmLensSpreadsheetViewer` | `<calmlens-spreadsheet-viewer>` | A tab per sheet and a table of its cells, a page of rows at a time                                      |

`getAssetViewerKind(asset)` from `calmlens/ui` names the viewer for an asset's display info or an asset from the API: `image`, `website`, `text`, `document`, `spreadsheet`, `media` for video and audio, or `none` while there is nothing to show.

The viewers show the asset's content as it is; they show no age notice or unavailable notice of their own. Use them where your app has decided the viewer may see the asset, or use the [asset component](/docs/widgets/asset-component), which picks the viewer and shows the age and unavailable notices for the viewer.

## Show an asset

Pass the [asset URL](/docs/guides/delivering-assets#asset-urls) as `src`, public or signed, and the asset's display info (`getAssetInfo`) or the asset from the API as `asset`. Give the element a height, or let an image take its own shape:

```tsx
import type { AssetInfo } from "calmlens/app";
import { CalmLensDocumentViewer } from "calmlens/react";

export function Attachment({ src, info }: { src: string; info: AssetInfo }) {
  return (
    <div style={{ height: 600 }}>
      <CalmLensDocumentViewer src={src} asset={info} label="Attachment" />
    </div>
  );
}
```

Vue and Svelte take the same props. In HTML, import `calmlens/elements` once, link `calmlens/styles.css`, set `src` as an attribute and `asset` as a property:

```html
<calmlens-document-viewer label="Attachment" style="display: block; height: 600px"></calmlens-document-viewer>
<script type="module">
  import "calmlens/elements";

  const { src, info } = await fetch("/api/attachments/42").then((response) => response.json());
  const viewer = document.querySelector("calmlens-document-viewer");
  viewer.setAttribute("src", src);
  viewer.asset = info;
</script>
```

| Prop              | Viewers           | What it sets                                                                                            |
| ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------- |
| `src`             | All               | Required. The asset URL, public or signed, which the viewer loads resources beneath.                    |
| `asset`           | All               | Required. The asset's display info, or the asset from the API.                                          |
| `onExpired`       | All               | Returns a fresh signed asset URL. See [Private assets](#private-assets).                                |
| `onUnavailable`   | All               | Called when the CDN refuses the asset to this viewer.                                                   |
| `label`           | All               | The viewer's accessible name, such as the asset's title. The image viewer uses it as the image's `alt`. |
| `viewerClaims`    | All               | `region` and `ageConfirmed`, added to every resource URL.                                               |
| `messages`        | All               | Replaces some or all of the text the viewer shows.                                                      |
| `sizes`           | Image             | The `sizes` of the image's responsive widths. Defaults to `100vw`.                                      |
| `text`, `getText` | Text, spreadsheet | The text to show. See [Text](#text).                                                                    |
| `pageSize`        | Spreadsheet       | Rows shown at a time. Defaults to 100.                                                                  |
| `initialPage`     | Document          | The page shown first, from 1.                                                                           |
| `pdf`             | Document          | Where pdf.js loads its worker, fonts and decoders from. See [Documents](#documents).                    |

The custom elements take `src`, `label`, `viewer-claims` (JSON), `sizes`, `page-size` and `initial-page` as attributes, and the other props as properties.

A viewer draws on its own surface, in the browser's page colours by default. Set `--calmlens-viewer-background` and `--calmlens-viewer-text` on it to match your page.

## Private assets

For a private asset, pass a signed asset URL as `src`: your server checks the viewer and signs it with `client.cdn.signAssetUrl({ assetId })`. The viewers load `rendered` (images, screenshots and documents) or `original` (a text asset's file) beneath it, so sign a text asset's URL with `original: true`.

When the URL stops working, as a signed one does once it expires, the viewer calls `onExpired` for a fresh asset URL and carries on: the image loads again, the text is read again, and the document opens again at the page it was on. If the fresh URL fails too, or there is no `onExpired`, the viewer shows its failure notice; the document viewer's notice links to the PDF.

## Images and websites

The image viewer shows the asset's rendered image inside its element, keeping its shape, over the blurred placeholder from `content.preview` until it loads. Without a height of its own, the element takes the image's shape from `content.rendered`.

The website viewer links to the page's `externalUrl` (only `http` and `https` addresses), opening it in a new tab, and shows the screenshot at the element's width. Tall screenshots scroll inside the element, and the scrolling region can be focused to scroll with the keyboard.

## Text

The text viewer always shows text as text: markup in it is displayed, never run. It reads, in order:

1. `text`, when your app already has it.
2. `getText()`, a function that returns it.
3. The asset's `content.extractedText`, when CalmLens returned it inline.
4. A `plaintext-document` asset's `original` file.

## Documents

The document viewer shows a PDF a page at a time with [pdf.js](https://mozilla.github.io/pdf.js/), which the SDK depends on at a fixed version and loads only when a document viewer is shown. Office documents and presentations show the PDF CalmLens rendered from them.

| Control                    | Keys in the pages region        |
| -------------------------- | ------------------------------- |
| Previous and next page     | `←` `→`, `Page Up` `Page Down`  |
| First and last page        | `Home` `End`                    |
| Page number field          | Type a number and press `Enter` |
| Zoom out, zoom in          | `-` `+`                         |
| Fit to width (the default) | `0`                             |
| Open PDF                   | A link to the PDF itself        |

Each page's text is in the page for screen readers, and the page number is announced when it changes. A document pdf.js can't open shows a notice with a link to the PDF.

Without `pdf`, pdf.js reads documents in the page itself. To read them in a worker, serve pdf.js's worker file from your app and pass its URL; the other folders come from the same `pdfjs-dist` package:

| `pdf` option          | What to serve                                                            |
| --------------------- | ------------------------------------------------------------------------ |
| `workerSrc`           | `pdfjs-dist/build/pdf.worker.min.mjs`                                    |
| `standardFontDataUrl` | `pdfjs-dist/standard_fonts/`, for documents that don't embed their fonts |
| `cMapUrl`             | `pdfjs-dist/cmaps/`, for documents in CJK and other non-Latin encodings  |
| `wasmUrl`             | `pdfjs-dist/wasm/`, for JPEG 2000 and JBIG2 images                       |

Serve the files of the `pdfjs-dist` version the SDK installs, so the worker matches the library.

## Spreadsheets

The spreadsheet viewer shows the sheets CalmLens reads from a spreadsheet: a tab for each sheet (hidden sheets are marked), and a table with the sheet's column letters and row numbers, where hidden rows and columns are marked. Large sheets show a page of rows at a time with buttons for the previous and next rows. Comments and chart titles are listed below the table. Cell text is always shown as text. Move between tabs with the arrow keys, `Home` and `End`.

A spreadsheet's sheets are in its text child: the `plaintext-document` child asset that holds its extracted text. Pass that child's asset URL as `src` and the child as `asset`, and the viewer reads it as the text viewer does. Or pass the text yourself as `text` or `getText`.

## Messages

`messages` replaces any of the viewers' text, keyed by name, such as `loading`, `openDocument`, `nextPage`, `pageStatus` (`Page {page} of {count}`) and `rowRange` (`Rows {from}–{to} of {count}`). `DEFAULT_VIEWER_MESSAGES` from `calmlens/ui` lists every key with its default.
