Skip to content

Lulzx/tinypdf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tinypdf

npm size license

Minimal PDF creation library. <400 LOC, zero dependencies, makes real PDFs.

npm install tinypdf

Invoice Example

View sample PDF — Generated with ~50 lines of code


Why tinypdf?

tinypdf jsPDF
Size 3.3 KB 229 KB
Dependencies 0 2

~70x smaller. We removed TTF fonts, PNG/SVG, HTML-to-PDF, forms, encryption, and compression. What's left is the 95% use case: put text and images on a page.

Build with it

Invoices, receipts, reports, shipping labels, tickets, certificates, contracts, data exports

Features

Feature Description
Text Helvetica, any size, hex colors, align left/center/right
Shapes Rectangles and lines
Images JPEG (photos, logos, signatures)
Pages Multiple pages, custom sizes
Markdown Convert markdown to PDF with headers, lists, rules

Not included

Custom fonts, PNG/GIF/SVG, vector graphics, forms, encryption, compression, HTML-to-PDF

Need those? Use jsPDF or pdf-lib.


Quick start

import { pdf } from 'tinypdf'
import { writeFileSync } from 'fs'

const doc = pdf()

doc.page((ctx) => {
  ctx.rect(50, 700, 200, 40, '#2563eb')           // blue rectangle
  ctx.text('Hello PDF!', 60, 712, 24, { color: '#ffffff' })
  ctx.line(50, 680, 250, 680, '#000000', 1)       // black line
})

writeFileSync('output.pdf', doc.build())

Add images

import { readFileSync } from 'fs'

doc.page((ctx) => {
  const logo = new Uint8Array(readFileSync('logo.jpg'))
  ctx.image(logo, 50, 700, 100, 50)
})

Measure text width

import { measureText } from 'tinypdf'

measureText('Hello', 12) // => 27.34 (points)

Markdown to PDF

import { markdown } from 'tinypdf'
import { writeFileSync } from 'fs'

const pdf = markdown(`
# Hello World

A minimal PDF from markdown.

## Features
- Headers (h1, h2, h3)
- Bullet lists
- Numbered lists
- Horizontal rules

---

Automatic word wrapping and pagination included.
`)

writeFileSync('output.pdf', pdf)

API

pdf()                                      // create document
doc.page(callback)                         // add page (612×792 default)
doc.page(width, height, callback)          // add page with custom size
doc.build()                                // returns Uint8Array

ctx.text(str, x, y, size, options?)        // options: { color, align, width }
ctx.rect(x, y, w, h, fill)                 // filled rectangle
ctx.line(x1, y1, x2, y2, stroke, width?)   // line
ctx.image(jpegBytes, x, y, w, h)           // JPEG image

measureText(str, size)                     // text width in points
markdown(str, options?)                    // options: { width, height, margin }

Full example

Invoice generator (~50 lines)
import { pdf } from 'tinypdf'
import { writeFileSync } from 'fs'

const doc = pdf()

doc.page(612, 792, (p) => {
  const margin = 40, pw = 532

  // Header
  p.rect(margin, 716, pw, 36, '#2563eb')
  p.text('INVOICE', 55, 726, 24, { color: '#fff' })
  p.text('#INV-2025-001', 472, 728, 12, { color: '#fff' })

  // Company & billing info
  p.text('Acme Corporation', margin, 670, 16)
  p.text('123 Business Street', margin, 652, 11, { color: '#666' })
  p.text('New York, NY 10001', margin, 638, 11, { color: '#666' })

  p.text('Bill To:', 340, 670, 12, { color: '#666' })
  p.text('John Smith', 340, 652, 14)
  p.text('456 Customer Ave', 340, 636, 11, { color: '#666' })
  p.text('Los Angeles, CA 90001', 340, 622, 11, { color: '#666' })

  // Table
  p.rect(margin, 560, pw, 25, '#f3f4f6')
  p.text('Description', 50, 568, 11)
  p.text('Qty', 310, 568, 11)
  p.text('Price', 380, 568, 11)
  p.text('Total', 480, 568, 11)

  const items = [
    ['Website Development', '1', '$5,000.00', '$5,000.00'],
    ['Hosting (Annual)', '1', '$200.00', '$200.00'],
    ['Maintenance Package', '12', '$150.00', '$1,800.00'],
  ]

  let y = 535
  for (const [desc, qty, price, total] of items) {
    p.text(desc, 50, y, 11)
    p.text(qty, 310, y, 11)
    p.text(price, 380, y, 11)
    p.text(total, 480, y, 11)
    p.line(margin, y - 15, margin + pw, y - 15, '#e5e7eb', 0.5)
    y -= 30
  }

  // Totals
  p.line(margin, y, margin + pw, y, '#000', 1)
  p.text('Subtotal:', 380, y - 25, 11)
  p.text('$7,000.00', 480, y - 25, 11)
  p.text('Tax (8%):', 380, y - 45, 11)
  p.text('$560.00', 480, y - 45, 11)
  p.rect(370, y - 75, 202, 25, '#2563eb')
  p.text('Total Due:', 380, y - 63, 12, { color: '#fff' })
  p.text('$7,560.00', 480, y - 63, 12, { color: '#fff' })

  // Footer
  p.text('Thank you for your business!', margin, 80, 12, { align: 'center', width: pw, color: '#666' })
  p.text('Payment due within 30 days', margin, 62, 10, { align: 'center', width: pw, color: '#999' })
})

writeFileSync('invoice.pdf', doc.build())

License

MIT

About

Minimal PDF creation library. <400 LOC, zero dependencies, makes real PDFs.

Resources

License

Stars

Watchers

Forks

Packages

No packages published