{"_id":"@avisenaalwi/pdf-qr-stamp","_rev":"5-0e209ecf440b6408ba79f6847410b024","name":"@avisenaalwi/pdf-qr-stamp","dist-tags":{"latest":"1.1.3"},"versions":{"1.0.0":{"name":"@avisenaalwi/pdf-qr-stamp","version":"1.0.0","keywords":["pdf","qr","qrcode","stamp","sign","footer","electronic-signature"],"author":{"name":"Avisena Alwi","email":"avisena.abdillah98@gmail.com"},"license":"MIT","_id":"@avisenaalwi/pdf-qr-stamp@1.0.0","maintainers":[{"name":"avisenaalwi","email":"avisena.abdillah98@gmail.com"}],"homepage":"https://github.com/avisenaalwi/node-pdf-qr-stamp#readme","bugs":{"url":"https://github.com/avisenaalwi/node-pdf-qr-stamp/issues"},"dist":{"shasum":"a4dbc8f30d7b4acf912164b4ad341eee5e515ae6","tarball":"https://registry.npmjs.org/@avisenaalwi/pdf-qr-stamp/-/pdf-qr-stamp-1.0.0.tgz","fileCount":42,"integrity":"sha512-KrygreFaONu5mO4pA/uq+9mDPibqgmSmK1+IHAukKKSYWF4c35KzWrPdU9HRM6u82A5UF1BClvq63t0gvOU+Rw==","signatures":[{"sig":"MEUCIHYqezolAGBs7GIdtFzYdfrfdxz336qi/SXoLnHGnsY1AiEAsNlZIpSz05/hHauo3GaVhKI3xV6yZpbaUhR9dRWij24=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":96363},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"e173974805c7f4e527bff95ae00b4069d46cb313","scripts":{"dev":"tsx test/smoke.ts","test":"vitest run","build":"tsc -p tsconfig.json","start":"tsx test/smoke.ts","test:unit":"vitest run","test:smoke":"tsx test/smoke.ts"},"_npmUser":{"name":"avisenaalwi","email":"avisena.abdillah98@gmail.com"},"repository":{"url":"git+https://github.com/avisenaalwi/node-pdf-qr-stamp.git","type":"git"},"_npmVersion":"11.13.0","description":"Library to stamp PDFs with a QR code automatically positioned above a target anchor text, with an optional center image and a footer.","directories":{},"_nodeVersion":"24.16.0","dependencies":{"qrcode":"^1.5.4","pdf-lib":"^1.17.1","pdfjs-dist":"^4.4.168"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.16.0","vitest":"^4.1.10","typescript":"^5.5.0","@types/node":"^20.14.0","@types/qrcode":"^1.5.5"},"_npmOperationalInternal":{"tmp":"tmp/pdf-qr-stamp_1.0.0_1783396859896_0.7462251449768167","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@avisenaalwi/pdf-qr-stamp","version":"1.1.0","keywords":["pdf","qr","qrcode","stamp","sign","footer","electronic-signature"],"author":{"name":"Avisena Alwi","email":"avisena.abdillah98@gmail.com"},"license":"MIT","_id":"@avisenaalwi/pdf-qr-stamp@1.1.0","maintainers":[{"name":"avisenaalwi","email":"avisena.abdillah98@gmail.com"}],"homepage":"https://github.com/avisenaalwi/node-pdf-qr-stamp#readme","bugs":{"url":"https://github.com/avisenaalwi/node-pdf-qr-stamp/issues"},"dist":{"shasum":"8669ff232d884d0975130a1d6f67b4e299bec38a","tarball":"https://registry.npmjs.org/@avisenaalwi/pdf-qr-stamp/-/pdf-qr-stamp-1.1.0.tgz","fileCount":14,"integrity":"sha512-UHJR4os9qM+kDueLPhCEAWWckm5IvwZvaB4Z/xNHHzp1A8y+mxIga1WUXDQdcBD3TMEsNEwH5Qapd2ZlQBPcQA==","signatures":[{"sig":"MEUCIQDpvhrWexhHcOecW1YrkAjLyiO1Sxt3e+LP8lohCYGEaAIgQK+4rUdoEfomhkxfw4Raswct+ya4+4KlDsKaNB5Cbtk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":90450},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"275a944a4a2c14d4693935f1e5d74d49b3aa364f","scripts":{"dev":"tsx test/smoke.ts","test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts --clean --shims","start":"tsx test/smoke.ts","test:unit":"vitest run","test:smoke":"tsx test/smoke.ts"},"_npmUser":{"name":"avisenaalwi","email":"avisena.abdillah98@gmail.com"},"repository":{"url":"git+https://github.com/avisenaalwi/node-pdf-qr-stamp.git","type":"git"},"_npmVersion":"11.13.0","description":"Library to stamp PDFs with a QR code automatically positioned above a target anchor text, with an optional center image and a footer.","directories":{},"_nodeVersion":"24.16.0","dependencies":{"qrcode":"^1.5.4","pdf-lib":"^1.17.1","pdfjs-dist":"^4.4.168"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.16.0","tsup":"^8.5.1","vitest":"^4.1.10","typescript":"^5.5.0","@types/node":"^20.14.0","@types/qrcode":"^1.5.5"},"_npmOperationalInternal":{"tmp":"tmp/pdf-qr-stamp_1.1.0_1783403893724_0.3874629249428714","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@avisenaalwi/pdf-qr-stamp","version":"1.1.1","keywords":["pdf","qr","qrcode","stamp","sign","footer","electronic-signature"],"author":{"name":"Avisena Alwi","email":"avisena.abdillah98@gmail.com"},"license":"MIT","_id":"@avisenaalwi/pdf-qr-stamp@1.1.1","maintainers":[{"name":"avisenaalwi","email":"avisena.abdillah98@gmail.com"}],"homepage":"https://github.com/AvisenaAlwi/node-pdf-qr-stamp#readme","bugs":{"url":"https://github.com/AvisenaAlwi/node-pdf-qr-stamp/issues"},"dist":{"shasum":"4e4a6ce06144011459b9869dc25a7af526922e22","tarball":"https://registry.npmjs.org/@avisenaalwi/pdf-qr-stamp/-/pdf-qr-stamp-1.1.1.tgz","fileCount":15,"integrity":"sha512-LryVAX6NsooUwsuy2EBTtsS41alBom4RNplkgfx3V1NtuLnKzLrG0+m4WOE+jnn+9FxO/Sr3JiOj2xgHo4cQHw==","signatures":[{"sig":"MEUCIA5dRNUDHyhEpzTDvjHKxB1crcvEQUSlHf7b3obBc4kGAiEA4pgBkU2gZF8CPv+s47wIiR46g+HsrJo9DNDwMUQzfUk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":102352},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"61b4e1688b74c06fc4565b554a13e5d7ede58b1a","scripts":{"dev":"tsx test/smoke.ts","test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts --clean --shims","start":"tsx test/smoke.ts","test:unit":"vitest run","test:smoke":"tsx test/smoke.ts"},"_npmUser":{"name":"avisenaalwi","email":"avisena.abdillah98@gmail.com"},"repository":{"url":"git+https://github.com/AvisenaAlwi/node-pdf-qr-stamp.git","type":"git"},"_npmVersion":"11.13.0","description":"Library to stamp PDFs with a QR code automatically positioned above a target anchor text, with an optional center image and a footer.","directories":{},"_nodeVersion":"24.16.0","dependencies":{"qrcode":"^1.5.4","pdf-lib":"^1.17.1","pdfjs-dist":"^4.4.168"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.16.0","tsup":"^8.5.1","vitest":"^4.1.10","typescript":"^5.5.0","@types/node":"^20.14.0","@types/qrcode":"^1.5.5"},"_npmOperationalInternal":{"tmp":"tmp/pdf-qr-stamp_1.1.1_1783411936438_0.3074673269585517","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"@avisenaalwi/pdf-qr-stamp","version":"1.1.2","keywords":["pdf","qr","qrcode","stamp","sign","footer","electronic-signature"],"author":{"name":"Avisena Alwi","email":"avisena.abdillah98@gmail.com"},"license":"MIT","_id":"@avisenaalwi/pdf-qr-stamp@1.1.2","maintainers":[{"name":"avisenaalwi","email":"avisena.abdillah98@gmail.com"}],"homepage":"https://github.com/AvisenaAlwi/node-pdf-qr-stamp#readme","bugs":{"url":"https://github.com/AvisenaAlwi/node-pdf-qr-stamp/issues"},"dist":{"shasum":"d7d0e4f8b2736822a3f98056df6227512f490860","tarball":"https://registry.npmjs.org/@avisenaalwi/pdf-qr-stamp/-/pdf-qr-stamp-1.1.2.tgz","fileCount":15,"integrity":"sha512-R7Um2XLWbnw5kHhiNGkfEN5TfA4UXzmiLnEi9qN3rb70muGNtBnkwfRqzWiTNNBeaCu6eUvvxBh/pyhGlrz2DQ==","signatures":[{"sig":"MEUCIQDyff/eU+DJljDWVRfVW5n8MT0ADHFYxLFxYdMqmEZi+gIgeBtkvuIdx2m0EmNF4JjpAHZtURZV6AfTBrNNJm+EEeE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@avisenaalwi%2fpdf-qr-stamp@1.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":103150},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"cbd2ecbb27e461042d1e64be537e2a93ad61a7d8","scripts":{"dev":"tsx test/smoke.ts","test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts --clean --shims","start":"tsx test/smoke.ts","test:unit":"vitest run","test:smoke":"tsx test/smoke.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b53b1be8-95f0-4684-b825-9ae17a891ead"}},"repository":{"url":"git+https://github.com/AvisenaAlwi/node-pdf-qr-stamp.git","type":"git"},"_npmVersion":"11.16.0","description":"Library to stamp PDFs with a QR code automatically positioned above a target anchor text, with an optional center image and a footer.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"qrcode":"^1.5.4","pdf-lib":"^1.17.1","pdfjs-dist":"^4.4.168"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.16.0","tsup":"^8.5.1","vitest":"^4.1.10","typescript":"^5.5.0","@types/node":"^20.14.0","@types/qrcode":"^1.5.5"},"_npmOperationalInternal":{"tmp":"tmp/pdf-qr-stamp_1.1.2_1783413360102_0.017806225719768864","host":"s3://npm-registry-packages-npm-production"}},"1.1.3":{"name":"@avisenaalwi/pdf-qr-stamp","version":"1.1.3","description":"Library to stamp PDFs with a QR code automatically positioned above a target anchor text, with an optional center image and a footer.","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"scripts":{"build":"tsup src/index.ts --format cjs,esm --dts --clean --shims","dev":"tsx test/smoke.ts","test":"vitest run","test:unit":"vitest run","test:smoke":"tsx test/smoke.ts","start":"tsx test/smoke.ts"},"engines":{"node":">=18"},"keywords":["pdf","qr","qrcode","stamp","sign","footer","electronic-signature"],"license":"MIT","dependencies":{"pdf-lib":"^1.17.1","pdfjs-dist":"^4.4.168","qrcode":"^1.5.4"},"devDependencies":{"@types/node":"^20.14.0","@types/qrcode":"^1.5.5","tsup":"^8.5.1","tsx":"^4.16.0","typescript":"^5.5.0","vitest":"^4.1.10"},"author":{"name":"Avisena Alwi","email":"avisena.abdillah98@gmail.com"},"repository":{"type":"git","url":"git+https://github.com/AvisenaAlwi/node-pdf-qr-stamp.git"},"bugs":{"url":"https://github.com/AvisenaAlwi/node-pdf-qr-stamp/issues"},"homepage":"https://github.com/AvisenaAlwi/node-pdf-qr-stamp#readme","gitHead":"20cdcd645af2ed577c84327cca9e26bfbeb6ce20","_id":"@avisenaalwi/pdf-qr-stamp@1.1.3","_nodeVersion":"26.7.0","_npmVersion":"11.19.0","dist":{"integrity":"sha512-DThrbEAvPyQKe5PNMxSA6JDDfEF9X46Ch3RqT3/DmD4B8ppKaezkTFcoyONilYQvN7qYdMujWX2jTVd6H63Ydw==","shasum":"98452f0da3279bf108e9bb4695900295d50a9e46","tarball":"https://registry.npmjs.org/@avisenaalwi/pdf-qr-stamp/-/pdf-qr-stamp-1.1.3.tgz","fileCount":15,"unpackedSize":105238,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDUHm6Z1epC2k/iFLlnGf2UOOo8MP5sjvw9m8gix4SipgIhAPBnMEUpmAkW3nQ9XcoDv00TeUbkkeS+JkZs5Qmzsn1o"}]},"_npmUser":{"name":"avisenaalwi","email":"avisena.abdillah98@gmail.com"},"directories":{},"maintainers":[{"name":"avisenaalwi","email":"avisena.abdillah98@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pdf-qr-stamp_1.1.3_1789051376404_0.8170771820269187"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-07T04:00:59.759Z","modified":"2026-09-10T14:42:56.813Z","1.0.0":"2026-07-07T04:01:00.040Z","1.1.0":"2026-07-07T05:58:13.869Z","1.1.1":"2026-07-07T08:12:16.634Z","1.1.2":"2026-07-07T08:36:00.238Z","1.1.3":"2026-09-10T14:42:56.542Z"},"bugs":{"url":"https://github.com/AvisenaAlwi/node-pdf-qr-stamp/issues"},"author":{"name":"Avisena Alwi","email":"avisena.abdillah98@gmail.com"},"license":"MIT","homepage":"https://github.com/AvisenaAlwi/node-pdf-qr-stamp#readme","keywords":["pdf","qr","qrcode","stamp","sign","footer","electronic-signature"],"repository":{"type":"git","url":"git+https://github.com/AvisenaAlwi/node-pdf-qr-stamp.git"},"description":"Library to stamp PDFs with a QR code automatically positioned above a target anchor text, with an optional center image and a footer.","maintainers":[{"name":"avisenaalwi","email":"avisena.abdillah98@gmail.com"}],"readme":"# pdf-qr-stamp\n\nA TypeScript/Node.js library for stamping PDFs with a QR code that is\nautomatically positioned **above an anchor text** (e.g. `ditandatangani secara\nelektronik`).\n\n## Behavior\n\n1. If the anchor text is **found**, the QR code is placed a few points **above**\n   every occurrence (left-aligned with the start of the text). Multiple\n   occurrences on the same page are all stamped.\n2. If the anchor text is **not found anywhere**, the QR code is placed in the\n   **bottom-right** corner (default) of every page.\n3. Optional **logo/image** in the center of the QR code.\n4. Optional **thin footer** on every page, either as simple text or as a custom\n   builder with text and images.\n\n## What you send\n\n| # | Input | Description |\n|---|-------|-------------|\n| 1 | `pdf` | File path, `Buffer`, `Uint8Array`, or `ArrayBuffer` |\n| 2 | QR text | `qr.text` (e.g. a verification URL) |\n| 3 | Center image (optional) | `qr.image` as PNG/JPG path or bytes |\n| 4 | Footer design (optional) | `footer` (simple) or `footerBuilder` (custom) |\n\n## Install\n\n```bash\nnpm install @avisenaalwi/pdf-qr-stamp\n```\n\n## Usage\n\n### Basic — QR above anchor text\n\n```ts\nimport { stampPdf } from '@avisenaalwi/pdf-qr-stamp';\nimport { readFileSync, writeFileSync } from 'fs';\n\nconst pdfBytes = readFileSync('./document.pdf');\n\nconst result = await stampPdf({\n  pdf: pdfBytes,\n  qr: {\n    text: 'https://verify.example.com/abc123',\n    size: 90,          // QR size in points (default 90)\n    offsetAbove: 6,    // gap between QR and text top (default 0)\n  },\n  anchorText: 'ditandatangani secara elektronik',\n});\n\nwriteFileSync('./document-signed.pdf', result);\n```\n\n### QR with a center logo\n\n```ts\nconst result = await stampPdf({\n  pdf: pdfBytes,\n  qr: {\n    text: 'https://verify.example.com/abc123',\n    image: './logo.png',        // path, Buffer, Uint8Array, or ArrayBuffer\n    size: 90,\n    imageRatio: 0.22,           // logo size relative to QR (default 0.22)\n    imagePadding: 3,            // white padding around logo (default 3)\n  },\n  anchorText: 'ditandatangani secara elektronik',\n});\n```\n\n### Simple text footer (auto-wraps)\n\n```ts\nconst result = await stampPdf({\n  pdf: pdfBytes,\n  qr: { text: 'https://verify.example.com/abc123' },\n  anchorText: 'ditandatangani secara elektronik',\n  footer: {\n    left: 'Valid Document',\n    center: 'Electronically signed',\n    right: 'Confidential',\n    fontSize: 8,\n    margin: 4,\n    pageNumber: true, // appends \"Halaman X / Y\" on the right\n  },\n});\n```\n\n### Custom footer builder — text + image + styles\n\n```ts\nimport { stampPdf, FooterBuilder } from '@avisenaalwi/pdf-qr-stamp';\n\nconst footer = new FooterBuilder()\n  .fontSize(8)\n  .margin(4)\n  .leftText('PT Contoh Indonesia', { bold: true })\n  .centerImage('./logo-footer.png', { height: 16 })\n  .rightText('Page {page} / {total}', { color: [0.2, 0.2, 0.2] });\n\nconst result = await stampPdf({\n  pdf: pdfBytes,\n  qr: { text: 'https://verify.example.com/abc123' },\n  footerBuilder: footer,\n});\n```\n\n### Long footer text — full page width\n\nUse `maxWidth: 'page'` to let the text span the full page width (minus margins).\nUseful for long legal/disclaimer footers.\n\n```ts\nconst longText =\n  'This document has been electronically signed using an electronic certificate issued by the Electronic Certification Agency. To verify authenticity, please scan the QR Code.';\n\nconst footer = new FooterBuilder()\n  .fontSize(8)\n  .margin(4)\n  .centerText(longText, { maxWidth: 'page' });\n\nconst result = await stampPdf({\n  pdf: pdfBytes,\n  qr: { text: 'https://verify.example.com/abc123' },\n  footerBuilder: footer,\n});\n```\n\n### Fallback when anchor text is missing\n\n```ts\nconst result = await stampPdf({\n  pdf: pdfBytes,\n  qr: { text: 'https://verify.example.com/xyz', size: 80 },\n  anchorText: 'some text that does not exist',\n  fallback: 'bottom-right', // or 'bottom-left' / 'none'\n});\n```\n\n### Preview placeholder — black box instead of QR code\n\nUse `preview: true` to draw a solid black box with the exact same size and\nposition as the QR code. This is useful for previewing where the signature QR\ncode will appear without having to generate a real QR code.\n\n```ts\nconst result = await stampPdf({\n  pdf: pdfBytes,\n  qr: { size: 90 }, // qr.text is optional in preview mode\n  anchorText: 'ditandatangani secara elektronik',\n  preview: true,\n});\n```\n\n### Inputs supported for `pdf`\n\n```ts\n// file path\nawait stampPdf({ pdf: './document.pdf', qr: { text: 'x' } });\n\n// Buffer / Uint8Array\nawait stampPdf({ pdf: readFileSync('./document.pdf'), qr: { text: 'x' } });\n\n// ArrayBuffer\nconst bytes = new Uint8Array(readFileSync('./document.pdf'));\nconst ab = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);\nawait stampPdf({ pdf: ab, qr: { text: 'x' } });\n```\n\n\n## Screenshots\n\n### Case 1 — QR placed above the anchor text\n\n![Case 1 — QR above anchor text](https://raw.githubusercontent.com/avisenaalwi/node-pdf-qr-stamp/main/assets/case1-basic.png)\n\n### Case 2 — Fallback QR in the bottom-right corner\n\n![Case 2 — fallback bottom-right](https://raw.githubusercontent.com/avisenaalwi/node-pdf-qr-stamp/main/assets/case2-fallback.png)\n\n### Case 4 — QR with a center logo\n\n![Case 4 — QR with center logo](https://raw.githubusercontent.com/avisenaalwi/node-pdf-qr-stamp/main/assets/case4-logo.png)\n\n### Case 10b — Footer builder using full page width\n\n![Case 10b — footer builder full-width](https://raw.githubusercontent.com/avisenaalwi/node-pdf-qr-stamp/main/assets/case10b-builder-fullwidth.png)\n\n\n## API\n\n### `stampPdf(options): Promise<Uint8Array>`\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `pdf` | `string \\| Uint8Array \\| Buffer \\| ArrayBuffer` | required | PDF source |\n| `qr.text` | `string` | required | QR payload |\n| `qr.image` | `string \\| Uint8Array \\| Buffer \\| ArrayBuffer` | — | Center logo (PNG/JPG) |\n| `qr.size` | `number` | `90` | QR size in points |\n| `qr.offsetAbove` | `number` | `0` | Gap above anchor text in points |\n| `qr.errorCorrectionLevel` | `'L' \\| 'M' \\| 'Q' \\| 'H'` | `'H'` | QR error correction |\n| `qr.imageRatio` | `number` | `0.22` | Logo size relative to QR |\n| `qr.imagePadding` | `number` | `3` | White padding around logo |\n| `anchorText` | `string` | `'ditandatangani secara elektronik'` | Anchor text to search |\n| `fallback` | `'bottom-right' \\| 'bottom-left' \\| 'none'` | `'bottom-right'` | QR position when anchor not found |\n| `fallbackUnmatchedPages` | `boolean` | `false` | Also fallback on pages without anchor |\n| `preview` | `boolean` | `false` | Draw a black placeholder box instead of the QR code |\n| `footer` | `FooterOptions \\| false` | `false` | Simple text footer |\n| `footerBuilder` | `FooterBuilder \\| false` | `false` | Custom footer builder |\n| `output` | `string` | — | Write result to file path |\n\n### Footer\n\nThe simple `footer` supports `left`, `center`, `right` with `{page}` and `{total}`\ntokens, plus `pageNumber: true` to append `Halaman X / Y` on the right.\n\nLong text is automatically wrapped into multiple lines so it does not overflow\nthe page.\n\n### FooterBuilder\n\nBuild a footer with text and images arranged in three columns.\n\n```ts\nconst footer = new FooterBuilder()\n  .fontSize(8)\n  .margin(4)\n  .color([0.2, 0.2, 0.2])\n  .leftText('Company Name', { bold: true })\n  .centerImage('./logo.png', { height: 18 })\n  .rightText('Page {page} / {total}');\n```\n\nText options: `{ bold, color, fontSize, maxWidth }`.\n\n- `maxWidth: 200` → wrap at 200 points.\n- `maxWidth: 'page'` → wrap using full page width minus margins and minus any other items in the same column (e.g. an image placed before the text).\n- `maxWidth: 'remaining'` → fill the remaining space in the column after images/other items are placed, capped at the default column width.\n- omit `maxWidth` → default column width (~1/3 of the page).\n\n`image` accepts a file path, `Uint8Array`, `Buffer`, or `ArrayBuffer`.\n\n## Notes\n\n- Text positions are detected with `pdfjs-dist` in PDF user space\n  (bottom-left origin). Search is case-insensitive.\n- The footer uses the standard Helvetica font (ASCII). Non-ASCII characters may\n  not render correctly.\n- When the QR code falls back to the bottom-right or bottom-left corner, the\n  footer automatically narrows on that side so the text does not cover the QR code.\n- URLs inside footer text are automatically converted into clickable links\n  (blue, underlined) with a link annotation.\n- 1 point ≈ 0.353 mm.\n\n## Module support\n\nThis library is published as both **CommonJS** (`require(...)`) and **ESM**\n(`import ...`). Node.js will pick the right format automatically based on the\nconsumer's module system.\n\n## Development\n\n```bash\nnpm install\nnpm run build   # bundle TypeScript -> dist/ (CJS + ESM + types)\nnpm test        # unit tests (Vitest)\n```\n\nRun the examples:\n\n```bash\nnpx tsx examples/cases.ts\n```\n\nOutput PDFs are written to `examples/out/`.\n\n","readmeFilename":"README.md"}