# pdf-qdf-source-edit

Small qpdf/QDF source text inspection and patching package.

The API works on QDF text and can inspect page source programs without a browser runtime. `qpdf-run` is exposed only as an optional byte runner adapter for callers that want to convert PDF bytes to QDF and compose patched QDF back to PDF bytes.

This package expects **QDF-style uncompressed PDF text** produced by qpdf, for example:

```js
await runQpdfBytes({
  inputs: { 'input.pdf': pdfBytes },
  args: ['--qdf', '--object-streams=disable', '--decode-level=all', '--', 'input.pdf', 'output.qdf.pdf'],
  outputs: ['output.qdf.pdf'],
  workerUrl,
  qpdfJsUrl,
  wasmUrl
});
```

## Public API

```js
import {
  inspectQdfPageSourceProgram,
  parseContentStreamOperators,
  parsePdfObjects,
  planQdfTextReplacement,
  applyQdfPatch,
  repairQdfStreamLengths,
  repairQdfXref
} from 'pdf-qdf-source-edit';
```

`inspectQdfPageSourceProgram(qdf, { pageNum })` parses QDF objects, resolves page content streams, and returns a page source text operator program.

`parsePdfObjects(qdf)` returns a `Map` of indirect objects keyed by refs such as `3 0 R`.

`parseContentStreamOperators(stream)` is a small content-stream tokenizer/parser intended for direct package tests and diagnostics around text operators such as `Tj`, `TJ`, `'`, and `"`.

`planQdfTextReplacement(qdf, options)` and `applyQdfPatch(qdf, plan)` are early low-level write APIs. They operate on QDF strings and text ranges, not on application-specific state.

### Node-only helpers

```js
import { decompressQdfStreams } from 'pdf-qdf-source-edit/node';
```

`decompressQdfStreams(qdf)` decompresses any Flate-encoded streams in the QDF, returning a modified QDF string with uncompressed streams. This helper imports `node:zlib` and is intentionally kept out of the browser-clean main entrypoint.

### Optional qpdf runner

```js
import { runQpdfBytes } from 'pdf-qdf-source-edit';

const qdfBytes = await runQpdfBytes({
  inputs: { 'input.pdf': pdfBytes },
  args: ['--qdf', '--object-streams=disable', '--decode-level=all', '--', 'input.pdf', 'output.qdf.pdf'],
  outputName: 'output.qdf.pdf',
  workerUrl,
  qpdfJsUrl,
  wasmUrl
});
```

`workerUrl`, `qpdfJsUrl`, and `wasmUrl` are required. Resolve them in the host app or bundler; this package does not assume `node_modules` paths at runtime.

## Examples

This repository includes a tiny QDF-style PDF fixture in `examples/simple-qdf.pdf`.

```bash
node examples/inspect-simple.mjs
```

## Current Limits

- Designed for QDF/uncompressed content streams, not arbitrary compressed PDFs.
- `inspectQdfPageSourceProgram` is a lightweight source inspector; it does not yet model fonts, glyph inventories, ToUnicode CMaps, or PDF text matrices as deeply as a full PDF application may need.
- `planQdfTextReplacement` is experimental and intentionally narrow. It is suitable for simple literal text replacements in QDF fixtures, not full production text editing.
- The package does not contain app-specific bridges, PDF.js/PDFium geometry matching, whiteout strategies, or font fallback policy.

## Host App Bridges

Application-specific bridges should live outside this package. Host apps should import these core APIs and keep application-specific compatibility layers in their own source tree.
