DOCUMENT CONVERSION · FOR EVERY TEAM

Convert images, PDF and TIFF on-device, in the browser

Turn a folder of JPEGs, PNGs and TIFFs into one PDF, a scan batch into a multi-page TIFF, or any image or PDF into one JPEG per page, with a single call to the Abscode Browser JS SDK. Because every byte is processed on the user's device, there is nothing to upload, store or move: less data transferred, fewer server cycles, lower cost, no drop in quality. Production-grade conversion any team can run.

Runs on-device Works offline Multi-page TIFF + PDF input Affordable to all
FOUR CONVERSION METHODS

One input array · four ways out

All four start from the same thing, an array of Uint8Array buffers, and, like every method in the SDK, resolve to the same {STATUS, DESCRIPTION, DATA} object. What changes is the shape of DATA.

compressToPdf

Every input, in order, becomes one PDF. DATA is a single Uint8Array you can blob, download, or hand to a viewer.

compressToTiff

Every input becomes one multi-page TIFF. DATA is a single Uint8Array, the archival format most scan pipelines still expect.

compressToJPG

DATA is an array, one JPEG per page, not one file. The only method that fans its output out this way.

compressToJPGStream

The same per-page JPEGs, but delivered as each page is produced, with an index, a total, and a progress value. The full array still resolves at the end.

MULTI-PAGE INPUT

TIFF and PDF go in, not just out

This is the part most browser-side converters miss. An input buffer does not have to be a single image. Hand the SDK a 40-page TIFF or a PDF and it reads every page inside it, so a two-file selection can be a 60-page job.

That fan-out is why compressToJPG returns an array: page count is a property of the content, not of how many files the user picked. Ask the SDK what it found before you convert.

Rule of thumb

Files in ≠ pages out. Read TOTAL_PAGES first if your UI needs to show a count, size a progress bar, or bill per page.

inspect-before-convert.js
// A single buffer may hold many pages
const buf = new Uint8Array(await file.arrayBuffer());

const info = await helper.readImageInformation(buf);

if (info.STATUS) {
  console.log('pages:', info.DATA.TOTAL_PAGES);
  console.log('size KB:', info.DATA.FILESIZE);

  info.DATA.INFORMATION.forEach(p => {
    // PAGE_NO · WIDTH · HEIGHT · COLOR_TYPE · DPI
    console.log(
      `p${p.PAGE_NO}: ${p.WIDTH}x${p.HEIGHT} ` +
      `${p.COLOR_TYPE} ${p.DPI}dpi`
    );
  });
}
// COLOR_TYPE: "BW" | "GREYSCALE" | "COLOR"
IMAGES → PDF

Convert images to PDF in JavaScript

Read the selected files as buffers, set your output profile, call compressToPdf. The result is a single PDF, assembled in the order you passed the buffers.

images-to-pdf.js
const helper = new ImageWizHelper();
await helper.ready;
await helper.initialize(LICENSE_BLOB);

// Mix freely: JPG, PNG, TIFF, PDF
const bufs = await Promise.all(
  [...input.files].map(async f =>
    new Uint8Array(await f.arrayBuffer()))
);

await helper.SetDPI(300);
await helper.SetPageLayout(4);     // A4

const res = await helper.compressToPdf(bufs);

if (res.STATUS) {
  const blob = new Blob([res.DATA], {
    type: 'application/pdf'
  });
  window.open(URL.createObjectURL(blob));
} else {
  // Nothing threw, read the reason
  console.error(res.DESCRIPTION);
}

The output profile

Three setters shape every page the converter emits. Set them once; they apply to the conversion calls that follow.

SetDPI(dpi)

Any DPI, 100, 150, 200, 300, 500, 600. Drives the rasterisation density of each output page.

SetPageLayout(l)

06 maps to A0–A6. Anything else falls back to A4. For a fixed pixel canvas use SetCustomPageLayout(layout, w, h, colorInt) with 11 Stretch or 12 AspectFit.

SetFilter(f)

0 Normal · 200 Black & White · 201 Grayscale. Applied per page during the conversion.

images-to-tiff.js
// Bitonal archive TIFF from a scan batch
await helper.SetFilter(200);   // Black & White
await helper.SetDPI(200);

const res = await helper.compressToTiff(bufs);

if (res.STATUS) {
  // DATA = ONE multi-page TIFF of every input page
  save('archive.tiff', res.DATA, 'image/tiff');
}

// Tiny download helper used throughout
function save(name, bytes, type) {
  const a = document.createElement('a');
  a.href = URL.createObjectURL(
    new Blob([bytes], { type })
  );
  a.download = name;
  a.click();
}
IMAGES → MULTI-PAGE TIFF

JavaScript multi-page TIFF, both directions

compressToTiff is the mirror of compressToPdf, same input array, same single-buffer output, different container. Because TIFF is also accepted as an input, you can round-trip: split a multi-page TIFF into JPEGs, or merge JPEGs, PNGs and TIFFs back into one.

Pair it with SetFilter(200) and you get a black-and-white TIFF, the shape most document archives and imaging back-ends are built around.

Talk to us about licensing →

ANY DOCUMENT → JPEG PER PAGE

One JPEG per page, batched or streamed

Both methods produce the same pages. compressToJPG hands you the whole array when the job is done. compressToJPGStream hands you each page the moment it exists, then resolves with the array anyway.

jpeg-batch.js
const res = await helper.compressToJPG(bufs);

// DATA is an ARRAY, one JPEG per page.
// A 12-page PDF in → 12 buffers out.
if (res.STATUS) {
  res.DATA.forEach((jpg, i) =>
    save(`page_${i + 1}.jpg`, jpg, 'image/jpeg'));
}
jpeg-stream.js
// Worker mode, pass a callback
const res = await helper.compressToJPGStream(bufs, (page) => {
  // { index, total, name, fileData, progress }
  appendThumbnail(page.fileData);
  bar.value = page.progress;
  label.textContent = `${page.index} / ${page.total}`;
});

res.DATA;  // Uint8Array[], every page, also collected

// Main-thread mode, subscribe to an event id
helper.on('jpgStream', (page) => appendThumbnail(page.fileData));
await helper.compressToJPGStream(bufs, 'jpgStream');

Appending pages to an existing TIFF

Conversion is not always a fresh document. AppendToTiff(src, target) on the main-thread build, appendToTiff(src, target) on the worker build, returns a new Uint8Array: the target's pages followed by the source's. Useful when a user adds a page to a batch they already assembled.

append-page.js
// Main-thread build
let r = await helper.AppendToTiff(/* src */ newPage, /* target */ existingTiff);

// Worker build, same arguments, lower-case name
// let r = await helper.appendToTiff(newPage, existingTiff);

if (r.STATUS) save('combined.tiff', r.DATA, 'image/tiff');
else console.error(r.DESCRIPTION);
THE SHAPE OF EVERY JOB

Four steps, no server

1

Load and wait

Construct the helper and await helper.ready. Pick the main-thread build for simple, one-document-at-a-time tools, or the worker-pool build to keep the UI responsive under bulk jobs.

2

Initialize

initialize(LICENSE_BLOB). It is non-fatal: without a valid blob the SDK still initializes and every conversion still runs, output is watermarked.

3

Read as buffers

Turn each File into a Uint8Array. Multi-page TIFFs and PDFs count as one buffer and many pages.

4

Convert and check

Call the method, then check STATUS. Nothing throws across the API, a failure is a false with a DESCRIPTION.

One note on the worker build

The worker facade transfers input ArrayBuffers into the worker, which detaches them on the caller side. If you need the same bytes for a second conversion, re-read the file.

WHY IT MATTERS

Conversion that never leaves the tab

No uploads to reason about

The document is converted on the user's own device. There is no transfer, no bucket, no retention policy to write, and no round-trip to explain to a security reviewer.

Works offline

Once the SDK files are cached the converter keeps working with no network at all, field capture, air-gapped desks, flaky connections.

Two run modes

The same method names and the same response contract on both builds. Start on the main thread; move to the worker pool when the jobs get big, the calls do not change.

A response you can branch on

Every method resolves to {STATUS, DESCRIPTION, DATA} and never throws. No try/catch scaffolding around conversion calls.

Page-accurate accounting

readImageInformation returns TOTAL_PAGES, FILESIZE, and per-page width, height, colour type and DPI, before you commit to the conversion.

Try before you license

An unlicensed build still initializes and still converts, the output simply carries a watermark band. Wire the whole flow up, then add the key.

Efficient by design, affordable to all

Doing the work on-device is not only a privacy choice; it is a cost and efficiency one. Nothing is transferred, buffered or stored server-side, so there are fewer server cycles to pay for and less data to move and keep, a smaller footprint that scales down as easily as up. That is why this is production-grade document technology any team can run, a startup, an SMB, a system integrator, an enterprise, or one developer, without an enterprise-only price tag. See pricing or pair it with in-browser compression.

FAQ

Conversion questions

Can I convert a multi-page TIFF to PDF entirely in the browser?

Yes. TIFF and PDF are accepted as inputs, not just produced as outputs. Read the TIFF into a Uint8Array, pass it in the input array to compressToPdf, and every page inside it is carried into the resulting PDF. No server step is involved.

Does compressToJPG return one file or many?

Many. It is the one conversion method whose DATA is an array, one JPEG per page. compressToPdf, compressToTiff and AppendToTiff each return a single Uint8Array. If you index into DATA expecting bytes, that is the method to check first.

How many pages will I get out of a selection of files?

As many as the content holds, not as many as the user picked. One buffer can be a 40-page TIFF or PDF. Call readImageInformation(buffer) and read DATA.TOTAL_PAGES before converting if the count matters to your UI.

Do the documents get uploaded anywhere?

No. The SDK is a WebAssembly build that runs on the user's device, in their tab. Conversion happens there, output lands there. Once the SDK files are cached it works with no network connection at all.

How do I show progress on a long conversion?

Use compressToJPGStream. Each page arrives as it is produced with index, total, name, fileData and progress. On the worker build you pass a callback; on the main-thread build you subscribe with helper.on(eventId, cb) and pass the event id. Either way the promise still resolves with the full array of pages.

Will a big conversion freeze the page?

On the main-thread build, a large job blocks the UI while it runs, that build is intended for simple, one-document-at-a-time tools. The worker-pool build runs the same methods off the main thread and fans work across the pool, so the interface stays responsive. Method names and the response contract are identical between the two.

What happens if a conversion fails?

Nothing throws. Every method resolves to {STATUS, DESCRIPTION, DATA}. On failure STATUS is false, DESCRIPTION carries the message, and DATA is null. Branch on STATUS rather than wrapping calls in try/catch.

Can I evaluate it without a licence key?

initialize() accepts an empty blob and is non-fatal, the SDK initializes and every conversion still succeeds, but output carries a watermark band. A false from initialize means the process handle could not be created, not merely that the licence was rejected. Talk to us when you are ready to licence.

THE FULL TOOLKIT

Explore the rest of the Browser JS Library

One license, every capability — take this and you also get all of these.

Convert your first document today

Wire the whole flow up unlicensed, then add the key.