展示HN:TinyPDF – 3kb PDF库(比jsPDF小70倍)
Show HN: TinyPDF – 3kb pdf library (70x smaller than jsPDF)

原始链接: https://github.com/Lulzx/tinypdf

这段 JavaScript 代码使用 `tinypdf` 库生成一个简单的发票 PDF。代码创建一个页面大小为 612x792 点的文档,并添加元素,例如带有“INVOICE”字样的蓝色页眉和发票号码。 然后,它包含公司(Acme Corporation)和账单(John Smith)信息。绘制一个表格,包含描述、数量、价格和总计几列,并填充了三个示例项目:网站开发、托管和维护套餐。水平线分隔表格行和部分。 最后,代码计算并显示小计、税费(8%)和应付总额,并在蓝色矩形中突出显示。带有感谢信息和付款条款的页脚完成了发票。生成的 PDF 文件保存为“invoice.pdf”。

## TinyPDF:一个轻量级PDF库 开发者lulzx创建了**TinyPDF**,一个极简的Node.js PDF生成库,大小仅为**3.3KB**(压缩&gzip)——相比jsPDF的229KB,这是一个显著的减小。它使用400行TypeScript编写,没有依赖项,专注于核心功能:文本、矩形、线条和JPEG图像,跨多页且具有自定义尺寸。 该库有意省略了自定义字体、PNG/SVG支持、表单和加密等功能,目标是常见的用例,如发票、收据和报告。讨论强调了*生成*PDF与*解析*PDF的难易程度,其他人也分享了类似的小型、专注的PDF库。 作者根据用户反馈迅速添加了Markdown支持。虽然仅限于ASCII字符和Helvetica字体,但TinyPDF仍然很有价值,尤其是在对捆绑包大小有严格限制的环境中,例如Cloudflare Workers(现在有10MB的限制,但仍然受益于更小的尺寸)。该项目可在[GitHub](https://github.com/Lulzx/tinypdf)和npm上获取。
相关文章

原文
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())
联系我们 contact @ memedata.com