install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/TerminalSkills/skills/pdfkit" ~/.claude/skills/comeonoliver-skillshub-pdfkit && rm -rf "$T"
manifest:
skills/TerminalSkills/skills/pdfkit/SKILL.mdsource content
PDFKit
Programmatic PDF generation for Node.js. Streaming architecture — write to files or HTTP responses.
Setup
# Install PDFKit for PDF generation. npm install pdfkit npm install -D @types/pdfkit
Basic Document
// src/pdf/basic.ts — Create a simple PDF with text and save to file. import PDFDocument from "pdfkit"; import fs from "fs"; const doc = new PDFDocument({ size: "A4", margin: 50 }); doc.pipe(fs.createWriteStream("output.pdf")); doc.fontSize(24).text("Monthly Report", { align: "center" }); doc.moveDown(); doc.fontSize(12).text("Generated on " + new Date().toLocaleDateString()); doc.moveDown(2); doc.fontSize(11).text( "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", { align: "justify", lineGap: 4 } ); doc.end();
Tables
// src/pdf/table.ts — Draw a table with headers and rows. // PDFKit has no built-in table; draw it with lines and positioned text. import PDFDocument from "pdfkit"; export function drawTable( doc: PDFKit.PDFDocument, headers: string[], rows: string[][], startY: number ) { const colWidth = (doc.page.width - 100) / headers.length; const rowHeight = 25; let y = startY; // Header row doc.font("Helvetica-Bold").fontSize(10); headers.forEach((header, i) => { doc.text(header, 50 + i * colWidth, y, { width: colWidth, align: "left" }); }); y += rowHeight; doc.moveTo(50, y).lineTo(doc.page.width - 50, y).stroke(); // Data rows doc.font("Helvetica").fontSize(10); for (const row of rows) { y += 5; row.forEach((cell, i) => { doc.text(cell, 50 + i * colWidth, y, { width: colWidth, align: "left" }); }); y += rowHeight; // Page break if needed if (y > doc.page.height - 100) { doc.addPage(); y = 50; } } }
Images and Graphics
// src/pdf/graphics.ts — Add images, draw shapes, and use vector graphics. import PDFDocument from "pdfkit"; import fs from "fs"; const doc = new PDFDocument(); doc.pipe(fs.createWriteStream("report.pdf")); // Embed an image doc.image("logo.png", 50, 50, { width: 150 }); // Draw a colored rectangle doc.rect(50, 250, 200, 100).fill("#3498db"); // Draw a circle doc.circle(400, 300, 50).fill("#e74c3c"); // Draw a line doc.moveTo(50, 400).lineTo(550, 400).dash(5, { space: 5 }).stroke("#999"); doc.end();
Headers and Footers
// src/pdf/headers.ts — Add repeating headers and footers with page numbers. import PDFDocument from "pdfkit"; import fs from "fs"; export function createDocWithHeaderFooter(title: string, outputPath: string) { const doc = new PDFDocument({ size: "A4", margin: 50, bufferPages: true }); doc.pipe(fs.createWriteStream(outputPath)); // Write content across multiple pages doc.fontSize(20).text(title, { align: "center" }); doc.moveDown(2); for (let i = 0; i < 200; i++) { doc.fontSize(11).text(`Line ${i + 1}: Sample content for the report.`); } // Add headers and footers to all pages const pageCount = doc.bufferedPageRange().count; for (let i = 0; i < pageCount; i++) { doc.switchToPage(i); // Header doc.fontSize(8).fillColor("#999") .text(title, 50, 20, { align: "left" }) .text(new Date().toLocaleDateString(), 50, 20, { align: "right" }); // Footer with page number doc.text(`Page ${i + 1} of ${pageCount}`, 50, doc.page.height - 30, { align: "center", }); } doc.end(); }
Custom Fonts
// src/pdf/fonts.ts — Register and use custom fonts for branded documents. import PDFDocument from "pdfkit"; import fs from "fs"; const doc = new PDFDocument(); doc.pipe(fs.createWriteStream("branded.pdf")); // Register custom font doc.registerFont("Inter", "./fonts/Inter-Regular.ttf"); doc.registerFont("Inter-Bold", "./fonts/Inter-Bold.ttf"); doc.font("Inter-Bold").fontSize(24).text("Branded Report"); doc.font("Inter").fontSize(12).text("Using custom Inter font throughout."); doc.end();
Streaming to HTTP Response
// src/pdf/stream.ts — Generate PDFs on the fly and stream to Express responses. import PDFDocument from "pdfkit"; import type { Response } from "express"; export function streamPdf(res: Response, title: string) { const doc = new PDFDocument(); res.setHeader("Content-Type", "application/pdf"); res.setHeader("Content-Disposition", `attachment; filename="${title}.pdf"`); doc.pipe(res); doc.fontSize(20).text(title); doc.end(); }