# Quick Start

> **One install, one function.** `npm install pdfnative`, then `buildDocumentPDFBytes({ title, blocks })` returns a `Uint8Array` — synchronously, in Node, browsers, Deno and Bun. No config, no build step, no service.

Generate your first PDF in under a minute.

## Install

```bash
npm install pdfnative
```

> **Requirements:** Node.js ≥ 22 · modern browsers · Deno · Bun.
> Zero runtime dependencies are installed.

## Node.js

```typescript
import { writeFileSync } from 'node:fs';
import { buildDocumentPDFBytes } from 'pdfnative';

const pdf = buildDocumentPDFBytes({
  title: 'Hello',
  blocks: [
    { type: 'heading',   text: 'Hello, pdfnative', level: 1 },
    { type: 'paragraph', text: 'Pure native PDF — zero dependencies, ISO 32000-1.' },
  ],
});

writeFileSync('hello.pdf', pdf);
```

For optimal compression on Node.js, enable native zlib once at startup:

```typescript
import { initNodeCompression, buildDocumentPDFBytes } from 'pdfnative';

await initNodeCompression();

const pdf = buildDocumentPDFBytes(params, { compress: true });
```

## Browser

```html
<script type="module">
  import { buildDocumentPDFBytes, downloadBlob } from 'https://esm.sh/pdfnative@1.7.0';

  document.getElementById('go').addEventListener('click', () => {
    const pdf = buildDocumentPDFBytes({
      title: 'Hello',
      blocks: [
        { type: 'heading', text: 'Hello from the browser', level: 1 },
      ],
    });
    downloadBlob(pdf, 'hello.pdf');
  });
</script>
```

A stored-block compression fallback works automatically — no native zlib needed in the browser.

## Tabular reports — `buildPDFBytes`

For bank statements, invoices, and any single-table report:

```typescript
import { buildPDFBytes } from 'pdfnative';

const pdf = buildPDFBytes({
  title: 'Monthly Report',
  infoItems: [
    { label: 'Period',  value: 'January 2026' },
    { label: 'Account', value: 'Main' },
  ],
  balanceText: 'Balance: $1,234.56',
  countText: '42 transactions',
  headers: ['Date', 'Description', 'Amount'],
  rows: [
    { cells: ['01/15', 'Grocery', '-$45.00'],   type: 'debit',  pointed: false },
    { cells: ['01/16', 'Salary',  '+$3,000.00'], type: 'credit', pointed: true  },
  ],
  footerText: 'Generated by MyApp',
});
```

## Free-form documents — `buildDocumentPDFBytes`

For mixed content (manuals, articles, multi-section reports):

```typescript
const pdf = buildDocumentPDFBytes({
  title: 'Project Report',
  blocks: [
    { type: 'toc' },
    { type: 'heading',   text: 'Executive Summary', level: 1 },
    { type: 'paragraph', text: 'This quarter saw strong growth.' },
    { type: 'list', style: 'bullet', items: ['Revenue +15%', 'Costs −8%'] },
    { type: 'image', data: chartBytes, width: 400, alt: 'Revenue chart' },
    { type: 'barcode', format: 'qr', data: 'https://example.com', width: 80 },
    { type: 'pageBreak' },
    { type: 'heading',   text: 'Details', level: 1 },
    { type: 'table', headers: ['Q', 'Revenue'], rows: [
      { cells: ['Q1', '$1.2M'], type: '', pointed: false },
      { cells: ['Q2', '$1.4M'], type: '', pointed: false },
    ] },
  ],
  footerText: 'Confidential',
});
```

13 block types are available: `heading`, `paragraph`, `list`, `table`, `image`, `link`, `spacer`, `pageBreak`, `toc`, `barcode`, `svg`, `formField`, `chart`.

## Multi-language

```typescript
import { registerFonts, loadFontData, buildDocumentPDFBytes } from 'pdfnative';

registerFonts({
  th: () => import('pdfnative/fonts/noto-thai-data.js'),
  ar: () => import('pdfnative/fonts/noto-arabic-data.js'),
});

const fontEntries = await Promise.all([
  loadFontData('th').then(fd => ({ fontData: fd!, fontRef: '/F3', lang: 'th' })),
  loadFontData('ar').then(fd => ({ fontData: fd!, fontRef: '/F4', lang: 'ar' })),
]);

const pdf = buildDocumentPDFBytes({
  blocks: [{ type: 'paragraph', text: 'สวัสดี — مرحبا' }],
  fontEntries,
});
```

Note that `lang` is a property of each **font entry**, not of a block: pdfnative detects the script per character and routes to the matching font, so a single paragraph can mix Thai, Arabic and Latin. BiDi resolution and OpenType shaping follow from that routing. See the [FAQ → Fonts and Unicode](faq.html#fonts-and-unicode) for the full list of codes.

## Web Worker

`createPDF` is the recommended entry point — it decides between the main thread and a worker for you:

```typescript
import { createPDF } from 'pdfnative';

const pdf = await createPDF(params, {
  workerUrl: new URL('./pdf-worker.js', import.meta.url), // your worker script
  threshold: 500,   // rows above this go to the worker (default WORKER_THRESHOLD = 500)
  onProgress: (p) => console.log(`${p}%`),
});
```

At or below the `threshold` (or when `Worker` / `workerUrl` is unavailable) the PDF is generated on the main thread; above it, the worker at `workerUrl` is spawned, with an automatic main-thread fallback if the worker fails.

To drive a worker directly, use `generatePDFInWorker(workerUrl, params, { timeout, onProgress })` — note the worker URL is the **first** argument, and the options are `timeout` (ms, default 60 000) and `onProgress` (there is no threshold at this level).

## Streaming

```typescript
import { buildDocumentPDFStream, concatChunks } from 'pdfnative';

// buildDocumentPDFStream(params, layoutOptions?, streamOptions?)
// chunkSize lives in the 3rd argument (StreamOptions)
const chunks: Uint8Array[] = [];

for await (const chunk of buildDocumentPDFStream(
  params,
  {},                        // layoutOptions
  { chunkSize: 65536 },      // streamOptions
)) {
  chunks.push(chunk);
}

const pdf = concatChunks(chunks);
```

The async iterable yields `Uint8Array` chunks as the PDF is produced — no full-document buffering. The three-argument API (`params`, `layoutOptions`, `streamOptions`) keeps layout concerns (tagged, compress, watermark) and streaming concerns (chunk size) separate.

## Playgrounds

The interactive playgrounds on [pdfnative.dev](https://pdfnative.dev) run entirely in the browser:

- [Extreme scripts](../playgrounds/extreme-scripts.html) — live BiDi, Tamil, Bengali + Devanagari, Arabic harakat
- [Scale](../playgrounds/scale.html) — 1,000 to 100,000 pages, Web Worker + true streaming

> **Local testing:** opening the playgrounds as `file://` disables the Web Worker
> (browsers block cross-origin Worker imports from `file:` origins).
> Serve the docs directory instead:
> ```bash
> npm run docs:serve   # → http://localhost:5000
> ```

## Command line — pdfnative-cli

Prefer the terminal? [`pdfnative-cli`](https://github.com/Nizoka/pdfnative-cli) wraps the same library with 21 commands — including `render`, `fill`, `sign`, `verify`, `ltv`, `encrypt`, `decrypt`, `merge`, `split`, `extract`, `extract-text` and `compare`:

```bash
# Install once
npm install --global pdfnative-cli

# Render a JSON document to a PDF
pdfnative render --input report.json --output report.pdf

# Sign with an RSA key (loaded from env var, never logged)
export PDFNATIVE_SIGN_KEY="$(cat private.pem)"
export PDFNATIVE_SIGN_CERT="$(cat cert.pem)"
pdfnative sign --input report.pdf --output report.signed.pdf

# Inspect any PDF (encryption, signatures, PDF/A, metadata)
pdfnative inspect --input report.signed.pdf --format text
```

Stdin/stdout makes it composable in shell pipelines:

```bash
cat report.json | pdfnative render | pdfnative sign | pdfnative inspect --format text
```

See the dedicated [CLI guide](cli.html) for the full command reference, security model, and CI/CD recipes.

## Next steps

- [Architecture](architecture.html) — modules, builders, generation pipeline.
- [CLI](cli.html) — pdfnative-cli command-line interface.
- [Accessibility](accessibility.html) — tagged PDF, PDF/UA, PDF/A.
- [FAQ](faq.html) — fonts, encryption, signatures, comparisons.
- [Troubleshooting](troubleshooting.html) — common pitfalls and fixes.
- [Sample generators](https://github.com/Nizoka/pdfnative/tree/main/scripts/generators) — 242 PDFs across 37 categories (48 generators).
