docs-companion-kit
v0.1.17
Published
Zero-runtime-dependency PDF and image workflows for Node.js and browsers, authored in TypeScript.
Downloads
2,361
Maintainers
Readme
docs-companion-kit
Zero-dependency TypeScript library for PDF and image processing across Node.js and browsers.
Ships PDF authoring, parsing and rendering, multi-format image codecs (PNG/JPEG/GIF/TIFF/WebP), CLI tools, batch processing, and layout helpers.
Runs in Node.js >= 18 and modern browsers. No native bindings, no WASM, no runtime dependencies.
Platform and Build
- Source: src/**/*.ts (TypeScript ES2022, strict)
- Output: dist/ (ESM + .d.ts)
- Zero runtime dependencies
- Conditional exports: ".", "./browser", "./node"
- Primary binary type: Uint8Array
Features
PDF Creation
- Documents and pages: new PDFDocument(), addPage(), deletePage(), reorderPages()
- Text and shapes: drawText(), drawLine(), drawRect(), drawPath()
- Image embedding: drawImage() for PNG/JPEG/GIF/TIFF inputs
- Links and forms: drawLink(), drawTextField(), drawCheckbox()
- Embedded fonts (TrueType / OpenType):
doc.embedFont(fontBytes: Uint8Array): EmbeddedFont— parse + register a TTF or OTF font (whole-font embedding, no subsetting); returns anEmbeddedFonthandlepage.drawEmbeddedText(text, { font, x, y, size?, color? })— draw text using an embedded font; the font is automatically added to the page's/Fontresource dictfont.measureText(text, size): number— measure advance width in pointsfont.width1000(charCode): number— advance width in 1/1000 em units (matches/Widthsarray)doc.getEmbeddedFonts(): EmbeddedFont[]— list all embedded fonts in the document- Encoding: WinAnsiEncoding (char codes 32–255);
/FontFile2for TTF,/FontFile3for CFF/OTF parseTtfMetrics(bytes): TtfMetrics— low-level font parser (head, hhea, hmtx, cmap, OS/2, name, post)
- AcroForm extensions:
page.drawDropdown({ name, x, y, width, height, options, value?, fontSize? })— combobox field with /Ff 131072page.drawListBox({ name, x, y, width, height, options, value?, fontSize?, multiSelect? })— list box; multiSelect adds /Ff 2097152page.drawRadioGroup({ name, options: [{exportValue, rect}], value? })— radio button group; serialised as one /Btn parent field + one /Widget annotation per option
- Document review annotations (ISO 32000-1 §12.5):
page.drawHighlight(x, y, w, h, opts?)— yellow highlight overlay with /QuadPoints and /CA opacity; opts: color [0–1], opacitypage.drawUnderline(x, y, w, h, opts?)— underline; opts: color, opacitypage.drawSquiggly(x, y, w, h, opts?)— squiggly underline (default red); opts: color, opacitypage.drawStrikeOut(x, y, w, h, opts?)— strikethrough (default red); opts: color, opacitypage.drawFreeText(text, x, y, width, height, opts?)— floating text box with /DA appearance string; opts: font, fontSize, textColor, bgColor, borderColor (all colors 0–1)page.drawStamp(name, x, y, width, height, opts?)— rubber stamp with 15 predefined names:Approved,Draft,Confidential,Experimental,NotApproved,AsIs,Expired,NotForPublicRelease,Final,Sold,Departmental,ForComment,TopSecret,ForPublicRelease,Void; opts: color, opacity
- Internal navigation links:
page.drawGoToLink(pageIndex, x, y, w, h, targetY?)— clickable in-document page jumppage.drawNamedLink(destName, x, y, w, h)— jump to a named destinationdoc.addNamedDestination({ name, pageIndex, y? })— register/Names /Destsin the catalog
- Bookmarks (document outline):
doc.addBookmark({ title, pageIndex, y?, children?, bold?, italic?, color?, collapsed? })doc.setBookmarks(list)/doc.getBookmarks()/doc.clearBookmarks()- Nested hierarchies with /First, /Last, /Next, /Prev linkage
- Collapsed parent bookmarks encoded with negative /Count
- Page labels:
doc.setPageLabels(ranges)— styles D (Arabic), r/R (Roman), a/A (alpha), with optional prefix and startValuebuildPageLabelsEntry(ranges)— utility to preview the /PageLabels literal
- Layout APIs:
- page.drawWrappedText()
- page.drawTable()
- drawParagraph(page, text, options)
- drawTextColumns(page, text, options)
- Color authoring:
- RGB via color
- Grayscale via gray (PDF
g/G) - CMYK via cmyk (PDF
k/K) - ICC output intent embedding via
doc.setOutputIntentICC(...) - Supported across drawText, drawWrappedText, drawRect/drawPath/drawLine, and drawTable
- Metadata: doc.metadata = { title, author, subject, keywords, creator, producer }
- Deterministic output: doc.save({ deterministic: true })
- Watermarks:
drawTextWatermark(page, text, { opacity?, angle?, font?, size?, color?, x?, y? })— semi-transparent rotated text overlay using PDF ExtGStateapplyWatermark(doc, text, opts)— apply the same watermark to every page in the documentdrawImageWatermark(page, image, { opacity?, angle?, width?, height?, x?, y? })— RGBA image overlay; width/height preserve aspect ratio if only one is givenapplyImageWatermark(doc, image, opts)— apply image watermark to every page- Default: 18% opacity, 45° angle, 72pt Helvetica-Bold, medium gray; viewers render the transparency natively
- Page rotation:
page.setRotation(0 | 90 | 180 | 270)— sets/Rotatein the page dictpage.rotationgetter; preserved onclone(); 0° never written to PDF (viewer default)
- Viewer preferences:
doc.setViewerPreferences(prefs)— serializes/ViewerPreferences,/PageMode,/PageLayoutin the catalogprefs.PageMode—'UseNone' | 'UseOutlines' | 'UseThumbs' | 'FullScreen' | 'UseOC' | 'UseAttachments'prefs.PageLayout—'SinglePage' | 'OneColumn' | 'TwoColumnLeft' | 'TwoColumnRight' | 'TwoPageLeft' | 'TwoPageRight'- Inner prefs:
HideToolbar,HideMenubar,HideWindowUI,FitWindow,CenterWindow,DisplayDocTitle,Duplex,PrintScaling,NumCopies,Direction,NonFullScreenPageMode
PDF Parsing and Analysis
- parsePDF(bytes, { strict? })
- Streaming parse: parsePDFStream(), parsePDFAsyncStream()
- Text extraction: extractPdfText(parsed)
- Text search: searchPdfText(parsed, query, { caseSensitive?, maxMatches?, pageIndices? }) → { totalMatches, matches: [{ pageIndex, matchText, x, y, font, size }] }
- Image extraction: extractPdfImages(parsed)
- Diagnostics: parsed.diagnostics[]
PDF Rendering
- renderPageToPng(parsed, index, { scale })
- renderPdfToPngs(parsed, { scale })
- renderPageToRGBA(parsed, index, opts)
- convertPdfToImages(input, { format?, pageNumbers?, scale?, width?, height?, base64?, quality?, concurrency?, strict?, maxInputBytes?, maxPagePixels? })
- Custom operator hooks via render options
- Corpus compatibility matrix demo for local PDF fixtures
Corpus Compatibility API
- analyzePdfCorpus(entries, { renderScale? }) for parse/render matrix reporting
Image Processing
- Codecs:
- readPng(), writePng()
- readJpeg(), writeJpeg()
- readGif(), writeGif()
- readTiff(), readTiffPages(), writeTiff(), writeTiffMultiPage()
- writeWebp() (lossless encoder)
- Transform and color: resize/crop/rotate/flip/grayscale/brightness/contrast
- Blend and alpha: blendImages(), applyAlphaMask(), composite()
- Analysis: imageHistogram(), adjustLevels()
- Convolution: blur(), sharpen(), edgeDetect(), emboss(), convolve()
- Format conversion: detectImageFormat(), convertImage(), convertImageDirect()
PDF and TIFF Interchange
- renderPdfToTiff(parsed, options)
- embedTiffInPdf(doc, tiffBytes, options)
- createPdfFromTiff(tiffBytes, options)
Node Utilities
- readFile(), writeFile()
- batchConvert(files, outputDir, options)
- batchExtractPdfImages(pdfFile, outputDir, options)
- convertPdfToImages(input, options) from
docs-companion-kit/node(data URI/path/Buffer/Uint8Array, plus opt-in URL support viaallowRemote)
CLI
- doc-kit convert -o [--format png|jpeg|gif|tiff|webp] [--quality 1-100] [--compression none|lzw|deflate|packbits] [--scale n] [--pages 1,3-5]
- doc-kit extract <input.pdf> -o [--format jpeg|png|tiff] [--quality 1-100]
- doc-kit batch-convert -o [--format ...] [--pattern ...]
Installation
npm install docs-companion-kitCLI usage:
npx doc-kit --helpQuick Start
Create a PDF
import { PDFDocument } from 'docs-companion-kit';
const doc = new PDFDocument();
const page = doc.addPage({ width: 420, height: 300 });
page.drawText('Hello PDF', { x: 40, y: 240, size: 18 });
const bytes = doc.save({ deterministic: true });Paragraph and Column Layout
import { PDFDocument, drawParagraph, drawTextColumns } from 'docs-companion-kit';
const doc = new PDFDocument();
const page = doc.addPage({ width: 500, height: 700 });
const y = drawParagraph(page, 'Paragraph one...\n\nParagraph two...', {
x: 40,
y: 660,
width: 420,
firstLineIndent: 14,
paragraphSpacing: 8,
lineHeight: 14
});
const cols = drawTextColumns(page, 'Long body text ...', {
x: 40,
y: y - 10,
width: 420,
height: 120,
columns: 2,
gutter: 18
});
console.log(cols.overflowText.length);Convert Image Format
import { convertImage } from 'docs-companion-kit';
const output = convertImage(inputBytes, { format: 'png' });Convert PDF to TIFF
import { parsePDF, renderPdfToTiff } from 'docs-companion-kit';
const parsed = parsePDF(pdfBytes);
const tiff = renderPdfToTiff(parsed, { scale: 2, compression: 'lzw' });Convert PDF to Images (Multi-Format)
import { convertPdfToImages } from 'docs-companion-kit';
const pages = await convertPdfToImages(pdfBytes, {
format: 'webp', // png | jpeg | gif | tiff | webp
pageNumbers: [1, 3], // 1-based
width: 1600,
concurrency: 4
});Node URL Input (Security Opt-In)
import { convertPdfToImages } from 'docs-companion-kit/node';
const pages = await convertPdfToImages('https://example.com/file.pdf', {
format: 'png',
allowRemote: true,
// Off by default. Needed for localhost/private-network targets.
allowPrivateNetwork: false,
requestTimeoutMs: 10_000,
maxRemoteBytes: 50 * 1024 * 1024
});High-Fidelity Native PDF Rendering (Node)
For shipping labels, barcodes, and PDFs with embedded/special fonts, use the Node native renderer. It delegates the actual page painting to a system PDF renderer, so the output matches what a PDF viewer renders much more closely than the dependency-free fallback.
import { convertPdfToImages } from 'docs-companion-kit/node';
const pages = await convertPdfToImages('./label.pdf', {
renderEngine: 'native',
// "auto" prefers Poppler's pdftoppm and falls back to macOS Quick Look
// for single-page PDFs when Poppler is not installed.
nativeRenderer: 'auto',
format: 'png',
scale: 4
});Poppler (pdftoppm) is recommended for multi-page/high-volume conversion. On macOS without Poppler, Quick Look supports single-page PDFs.
Author Grayscale and CMYK PDF Content
import { PDFDocument } from 'docs-companion-kit';
const doc = new PDFDocument();
const page = doc.addPage({ width: 320, height: 220 });
page.drawRect({ x: 20, y: 140, width: 60, height: 40, fill: true, cmyk: [0, 1, 1, 0] });
page.drawRect({ x: 100, y: 140, width: 60, height: 40, fill: true, gray: 0.5 });
page.drawText('CMYK + Gray', { x: 20, y: 110, cmyk: [1, 0, 0, 0], size: 12 });
const bytes = doc.save({ deterministic: true });Attach an ICC Output Intent Profile
import { PDFDocument } from 'docs-companion-kit';
const doc = new PDFDocument();
doc.setOutputIntentICC({
profile: new Uint8Array([73, 67, 67, 80, 82, 79, 70, 73, 76, 69]),
components: 3,
outputConditionIdentifier: 'sRGB IEC61966-2.1',
info: 'Demo RGB Profile'
});
const page = doc.addPage({ width: 200, height: 200 });
page.drawText('ICC output intent attached', { x: 20, y: 140, size: 12 });
const bytes = doc.save({ deterministic: true });Analyze a PDF Corpus Matrix
import { analyzePdfCorpus } from 'docs-companion-kit';
const report = analyzePdfCorpus([
{ name: 'sample-1.pdf', bytes: pdf1 },
{ name: 'sample-2.pdf', bytes: pdf2 }
], { renderScale: 1 });
console.log(report.summary.passed, report.summary.total);Scripts
npm run build # compile TypeScript → dist/
npm test # run test suite (requires a prior build)
npm run typecheckLicense
MIT
