{"id":44611402,"url":"https://github.com/Lulzx/tinypdf","last_synced_at":"2026-02-20T00:01:20.731Z","repository":{"id":338670419,"uuid":"1119104697","full_name":"Lulzx/tinypdf","owner":"Lulzx","description":"Minimal PDF creation library. \u003c400 LOC, zero dependencies, makes real PDFs.","archived":false,"fork":false,"pushed_at":"2026-02-15T20:58:32.000Z","size":71,"stargazers_count":1448,"open_issues_count":0,"forks_count":59,"subscribers_count":10,"default_branch":"main","last_synced_at":"2026-02-16T01:18:20.274Z","etag":null,"topics":["invoice-generator","pdf","pdf-generation"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Lulzx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-18T18:47:20.000Z","updated_at":"2026-02-16T00:30:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Lulzx/tinypdf","commit_stats":null,"previous_names":["lulzx/tinypdf"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Lulzx/tinypdf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lulzx%2Ftinypdf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lulzx%2Ftinypdf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lulzx%2Ftinypdf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lulzx%2Ftinypdf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lulzx","download_url":"https://codeload.github.com/Lulzx/tinypdf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lulzx%2Ftinypdf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29637400,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T22:32:43.237Z","status":"ssl_error","status_checked_at":"2026-02-19T22:32:38.330Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["invoice-generator","pdf","pdf-generation"],"created_at":"2026-02-14T12:00:22.786Z","updated_at":"2026-02-20T00:01:20.715Z","avatar_url":"https://github.com/Lulzx.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# tinypdf\n\n[![npm](https://img.shields.io/npm/v/tinypdf)](https://www.npmjs.com/package/tinypdf)\n[![size](https://img.shields.io/bundlephobia/minzip/tinypdf)](https://bundlephobia.com/package/tinypdf)\n[![license](https://img.shields.io/npm/l/tinypdf)](LICENSE)\n\nMinimal PDF creation library. **\u003c400 LOC, zero dependencies, makes real PDFs.**\n\n```bash\nnpm install tinypdf\n```\n\n![Invoice Example](examples/invoice.png)\n\n\u003e [View sample PDF](examples/invoice.pdf) — Generated with ~50 lines of code\n\n---\n\n## Why tinypdf?\n\n|  | tinypdf | jsPDF |\n|--|---------|-------|\n| **Size** | 3.3 KB | 229 KB |\n| **Dependencies** | 0 | 2 |\n\n**~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.**\n\n### Build with it\n\nInvoices, receipts, reports, shipping labels, tickets, certificates, contracts, data exports\n\n### Features\n\n| Feature | Description |\n|---------|-------------|\n| **Text** | Helvetica, any size, hex colors, align left/center/right |\n| **Shapes** | Rectangles and lines |\n| **Images** | JPEG (photos, logos, signatures) |\n| **Links** | Clickable URLs with optional underline |\n| **Pages** | Multiple pages, custom sizes |\n| **Markdown** | Convert markdown to PDF with headers, lists, rules |\n\n### Not included\n\nCustom fonts, PNG/GIF/SVG, vector graphics, forms, encryption, compression, HTML-to-PDF\n\nNeed those? Use [jsPDF](https://github.com/parallax/jsPDF) or [pdf-lib](https://github.com/Hopding/pdf-lib).\n\n---\n\n## Quick start\n\n```typescript\nimport { pdf } from 'tinypdf'\nimport { writeFileSync } from 'fs'\n\nconst doc = pdf()\n\ndoc.page((ctx) =\u003e {\n  ctx.rect(50, 700, 200, 40, '#2563eb')           // blue rectangle\n  ctx.text('Hello PDF!', 60, 712, 24, { color: '#ffffff' })\n  ctx.line(50, 680, 250, 680, '#000000', 1)       // black line\n})\n\nwriteFileSync('output.pdf', doc.build())\n```\n\n### Add images\n\n```typescript\nimport { readFileSync } from 'fs'\n\ndoc.page((ctx) =\u003e {\n  const logo = new Uint8Array(readFileSync('logo.jpg'))\n  ctx.image(logo, 50, 700, 100, 50)\n})\n```\n\n### Add links\n\n```typescript\nimport { pdf, measureText } from 'tinypdf'\n\ndoc.page((ctx) =\u003e {\n  const text = 'Visit Example.com'\n  const y = 700\n  ctx.text(text, 50, y, 14, { color: '#0066cc' })\n  ctx.link('https://example.com', 50, y - 4, measureText(text, 14), 18, { underline: '#0066cc' })\n})\n```\n\n### Measure text width\n\n```typescript\nimport { measureText } from 'tinypdf'\n\nmeasureText('Hello', 12) // =\u003e 27.34 (points)\n```\n\n### Markdown to PDF\n\n```typescript\nimport { markdown } from 'tinypdf'\nimport { writeFileSync } from 'fs'\n\nconst pdf = markdown(`\n# Hello World\n\nA minimal PDF from markdown.\n\n## Features\n- Headers (h1, h2, h3)\n- Bullet lists\n- Numbered lists\n- Horizontal rules\n\n---\n\nAutomatic word wrapping and pagination included.\n`)\n\nwriteFileSync('output.pdf', pdf)\n```\n\n---\n\n## API\n\n```typescript\npdf()                                      // create document\ndoc.page(callback)                         // add page (612×792 default)\ndoc.page(width, height, callback)          // add page with custom size\ndoc.build()                                // returns Uint8Array\n\nctx.text(str, x, y, size, options?)        // options: { color, align, width }\nctx.rect(x, y, w, h, fill)                 // filled rectangle\nctx.line(x1, y1, x2, y2, stroke, width?)   // line\nctx.image(jpegBytes, x, y, w, h)           // JPEG image\nctx.link(url, x, y, w, h, options?)        // options: { underline }\n\nmeasureText(str, size)                     // text width in points\nmarkdown(str, options?)                    // options: { width, height, margin }\n```\n\n---\n\n## Full example\n\n\u003cdetails\u003e\n\u003csummary\u003eInvoice generator (~50 lines)\u003c/summary\u003e\n\n```typescript\nimport { pdf } from 'tinypdf'\nimport { writeFileSync } from 'fs'\n\nconst doc = pdf()\n\ndoc.page(612, 792, (p) =\u003e {\n  const margin = 40, pw = 532\n\n  // Header\n  p.rect(margin, 716, pw, 36, '#2563eb')\n  p.text('INVOICE', 55, 726, 24, { color: '#fff' })\n  p.text('#INV-2025-001', 472, 728, 12, { color: '#fff' })\n\n  // Company \u0026 billing info\n  p.text('Acme Corporation', margin, 670, 16)\n  p.text('123 Business Street', margin, 652, 11, { color: '#666' })\n  p.text('New York, NY 10001', margin, 638, 11, { color: '#666' })\n\n  p.text('Bill To:', 340, 670, 12, { color: '#666' })\n  p.text('John Smith', 340, 652, 14)\n  p.text('456 Customer Ave', 340, 636, 11, { color: '#666' })\n  p.text('Los Angeles, CA 90001', 340, 622, 11, { color: '#666' })\n\n  // Table\n  p.rect(margin, 560, pw, 25, '#f3f4f6')\n  p.text('Description', 50, 568, 11)\n  p.text('Qty', 310, 568, 11)\n  p.text('Price', 380, 568, 11)\n  p.text('Total', 480, 568, 11)\n\n  const items = [\n    ['Website Development', '1', '$5,000.00', '$5,000.00'],\n    ['Hosting (Annual)', '1', '$200.00', '$200.00'],\n    ['Maintenance Package', '12', '$150.00', '$1,800.00'],\n  ]\n\n  let y = 535\n  for (const [desc, qty, price, total] of items) {\n    p.text(desc, 50, y, 11)\n    p.text(qty, 310, y, 11)\n    p.text(price, 380, y, 11)\n    p.text(total, 480, y, 11)\n    p.line(margin, y - 15, margin + pw, y - 15, '#e5e7eb', 0.5)\n    y -= 30\n  }\n\n  // Totals\n  p.line(margin, y, margin + pw, y, '#000', 1)\n  p.text('Subtotal:', 380, y - 25, 11)\n  p.text('$7,000.00', 480, y - 25, 11)\n  p.text('Tax (8%):', 380, y - 45, 11)\n  p.text('$560.00', 480, y - 45, 11)\n  p.rect(370, y - 75, 202, 25, '#2563eb')\n  p.text('Total Due:', 380, y - 63, 12, { color: '#fff' })\n  p.text('$7,560.00', 480, y - 63, 12, { color: '#fff' })\n\n  // Footer\n  p.text('Thank you for your business!', margin, 80, 12, { align: 'center', width: pw, color: '#666' })\n  p.text('Payment due within 30 days', margin, 62, 10, { align: 'center', width: pw, color: '#999' })\n})\n\nwriteFileSync('invoice.pdf', doc.build())\n```\n\n\u003c/details\u003e\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLulzx%2Ftinypdf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FLulzx%2Ftinypdf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLulzx%2Ftinypdf/lists"}