npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@junglo/impose

v0.1.0

Published

PDF imposition SDK: impose(pdfBytes, options) → print-ready saddle-stitch booklets and folded signatures. Pure TypeScript, pdf-lib only, runs in browser/Bun/Deno/Node.

Readme

@junglo/impose

PDF imposition as a pure function: impose(pdfBytes, options) → booklet PDF.

Takes PDF bytes from any source and returns an imposed PDF — pages reordered and placed N-up so they read in order after duplex printing and folding. Saddle-stitch booklets up to print-shop signature sheets (e.g. 70×100 cm). No I/O, no global state, no server: runs in the browser, Bun, Deno and Node with a single runtime dependency (pdf-lib). Vector content passes through untouched — nothing is rasterized.

PDF generation is deliberately out of scope: produce your PDF with anything (Paged.js, a layout tool, an export button) and pass the bytes in.

Install

bun add @junglo/impose     # or npm/pnpm/yarn

Use

import { impose } from "@junglo/impose";

const result = await impose(pdfBytes, {
  mode: "saddle",        // fold the whole stack once (zine style)
  sheet: "A4",           // A4 | A3 | Letter | { width, height, unit }
  pagesPerSide: 2,       // 2 | 4 | 8 | 16
  flip: "short-edge",    // duplex flip your printer will use
});

result.pdf;         // Uint8Array — print-ready
result.sheets;      // physical sheets of paper
result.blanksAdded; // padding pages appended to fill the last sheet
result.layout;      // page→slot map: drive a preview UI without rendering

Browser (e.g. behind a "print as zine" button — all client-side):

const bytes = new Uint8Array(await file.arrayBuffer());
const { pdf } = await impose(bytes, { mode: "saddle", sheet: "A4", pagesPerSide: 2, flip: "short-edge" });
const url = URL.createObjectURL(new Blob([pdf], { type: "application/pdf" }));

Preview without rendering — the pure core is exported separately:

import { computeOrder } from "@junglo/impose";

// Zero pdf-lib involved: just math. Ideal for live UI estimates.
const layout = computeOrder(20, { mode: "saddle", pagesPerSide: 2, flip: "short-edge" });
// layout[0].front → [{ slot: 0, page: 20, rotation: 0 }, { slot: 1, page: 1, rotation: 0 }]

Options

| Option | Values | Notes | |---|---|---| | mode | saddle | signature | saddle: one folded stack; signature: bundles of sheets | | sheet | A4 A3 Letter or {width, height, unit} | named sizes auto-orient (landscape for 2/8-up, portrait for 4/16-up); custom sizes used exactly as given | | pagesPerSide | 2 4 8 16 | grid: 2×1, 2×2, 4×2, 4×4 | | signatureSheets | number (default 4) | sheets per signature; last signature may be shorter | | creep | mm (default 0) | per-sheet shift toward the spine on inner sheets (saddle 2-up) | | bleed | mm (default 0) | source pages expected at trim+bleed size; crop marks offset outside the bleed | | marks | { crop?, fold? } | drawn only where slack exists — never on or across page content | | flip | short-edge | long-edge | must match your printer's duplex setting (see below) | | scale | fit (default) | none | fit scales pages into slots preserving aspect ratio; mixed source sizes are fine |

Duplex flip — the classic imposition bug, made explicit

Back sides are laid out for a book-page turn (flip about the sheet's vertical axis). In printer-driver terms that is:

  • 2-up / 8-up (landscape sheets): choose "flip on short edge".
  • 4-up / 16-up (portrait sheets): choose "flip on long edge".

Pass that same value as flip. If your printer is set the other way, pass the other value — the back sides are rotated 180° to compensate. Symptom of a mismatch: back pages upside down.

How to fold

Hold the printed stack with the front of sheet 1 facing you (for a 2-up saddle booklet that's the side showing page 1 on its right half), sheets in print order.

  1. 2-up: fold the left half behind the right half. Done — page 1 is the cover.
  2. 4-up: fold left half behind, then the top half behind.
  3. 8-up: fold left behind, top behind, left behind.
  4. 16-up: fold left behind, top behind, left behind, top behind.

Then staple/sew the spine and trim the other three edges (cut layouts of 4-up and beyond need the trim to free the pages).

Bleed & marks

If your pages carry bleed, export them at trim + bleed size and pass bleed in mm. Crop marks are drawn at the trim corners, offset to sit outside the bleed, and suppressed wherever they would touch another page's area — so on an exact-fit layout (A5 on A4) you simply get no marks, while on a roomy custom sheet (e.g. 70×100 cm signature work) you get full crop and fold marks in the slack space.

Limits

  • Very large PDFs (hundreds of MB) can strain browser memory; the same code runs server-side (Bun/Node) unchanged if you need big files.
  • Mixed page sizes are handled by scale: "fit"; with scale: "none" oversized pages may overlap neighboring slots — know your sources.
  • pdf-lib is pinned (stable, ubiquitous, maintenance mode); the surface used is small (embed + draw).

License

MIT © junglostore