Render TIFF and PDF in the browser.
No browser displays a multi-page TIFF. The Abscode Browser JS SDK decodes every page of a TIFF, PDF or other image to a JPEG on the user's own device, no upload, no server-side rendering step, no round-trip per page.
The formats your users have aren't the formats browsers show
Scanners, fax gateways and document archives emit multi-page TIFF. Browsers don't decode it. Most teams solve this by shipping every page to a server and pulling rendered images back.
TIFF doesn't render
No mainstream browser decodes TIFF in an img tag, and a multi-page TIFF has no concept a browser could show even if it did. The file is opaque until something decodes it.
PDF display isn't yours to control
The built-in PDF viewer is the browser's, not your application's. You can embed it; you cannot lay it out, tint it, thumbnail it, or hand its pixels to the rest of your UI.
Server rendering costs you twice
Upload the document, render each page, cache the output, serve it back. That is bandwidth, latency, storage of customer documents, and a viewer that stops working the moment the network does.
The SDK does the decoding where the file already is
The Abscode Browser JS SDK is WebAssembly. It loads once, then reads the document's bytes as a Uint8Array, one line off any File, and returns JPEG bytes per page. The document never leaves the tab, nothing is uploaded, and once the page is loaded the viewer keeps working with no network at all.
Outline first, pixels second
Read the document's structure before rendering anything. You get page count and per-page dimensions instantly, so the scroll container can be laid out correctly while the pages are still being produced.
A working viewer, end to end
This is the whole pattern: inspect, lay out, stream. Every method resolves to {STATUS, DESCRIPTION, DATA} and never throws, so error handling is a branch rather than a try block.
- ✓ Works the same for a 40-page TIFF and a 40-page PDF
- ✓ Page metadata before any rendering starts
- ✓ Each page painted as it lands, with progress
- ✓ Worker pool keeps the scroll thread free
// Worker-pool build, rendering stays off the scroll thread. const helper = new ImageWizHelper(); await helper.ready; await helper.initialize(LICENSE_BLOB); const toU8 = async (file) => new Uint8Array(await file.arrayBuffer()); async function openDocument(file) { // 1. Outline: pages, size, colour type, DPI. const info = await helper.readImageInformation(await toU8(file)); if (!info.STATUS) return showError(info.DESCRIPTION); // 2. Lay the viewer out before any pixels exist. info.DATA.INFORMATION.forEach(p => addPlaceholder(p.PAGE_NO, p.WIDTH, p.HEIGHT, p.COLOR_TYPE, p.DPI)); // 3. Render resolution for on-screen viewing. await helper.SetDPI(150); // 4. Stream, each page arrives as it is produced. // Re-read the file: the worker took the first buffer. const res = await helper.compressToJPGStream([await toU8(file)], (page) => { // page = { index, total, name, fileData, progress } const blob = new Blob([page.fileData], { type: 'image/jpeg' }); paintPage(page.index, URL.createObjectURL(blob)); setProgress(page.progress); }); if (!res.STATUS) showError(res.DESCRIPTION); // res.DATA, every page's JPEG bytes, also collected for you. }
Everything a viewer shell needs, before rendering
One call to readImageInformation with the document's bytes returns the file size in KB, the total page count, and a per-page record.
TOTAL_PAGES
The page count for the whole document, the number your pager, thumbnail rail and scroll height are built from.
WIDTH · HEIGHT
Per-page pixel dimensions. Reserve each slot at the true aspect ratio so pages drop in without shifting the layout.
COLOR_TYPE
Per page: BW, GREYSCALE or COLOR. Badge a scan, or route bitonal pages down a lighter path.
DPI
The page's own resolution, enough to show real-world size, or to decide what render DPI is actually worth requesting.
Just need the page count?
In the main-thread build, GetPageCountInImageFile(file) takes a File object directly, no reading it into a buffer first, and returns the count in DATA. Ideal for labelling files in an upload list before anyone opens one. In the worker build, use readImageInformation and read TOTAL_PAGES.
You choose what each pass costs
Rendering settings are ordinary setter calls that apply to the pages you render next, so a thumbnail pass and a full-page pass are the same code with different numbers.
Thumbnails vs. full page
SetDPI accepts any DPI, 100 for a thumbnail rail, 300 for the page the reader is actually on. Same document, two passes.
Custom canvas
SetCustomPageLayout takes stretch (11) or aspect-fit (12) with explicit pixel dimensions and a fill colour, a fixed thumbnail size or a bespoke target.
Filters per page
SetFilter applies Normal (0), Black & White (200) or Grayscale (201) as each page is rendered.
Fixed page sizes
SetPageLayout(0..6) maps to A0 through A6; a value outside that range falls back to A4 rather than failing.
Two run modes
The main-thread build is the simplest thing that works. The worker-pool build moves rendering off the UI thread, same method names, same responses.
All pages at once
If you don't need progressive paint, compressToJPG returns one JPEG per page as a single array when the whole document is done.
Questions before you build
Explore the rest of the Browser JS Library
One license, every capability — take this and you also get all of these.