Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Generate professional slide deck images from content with 9 visual style presets and configurable slide counts.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/merge-to-pdf.ts
1import { existsSync, readdirSync, readFileSync } from "fs";2import { join, basename } from "path";3import { PDFDocument, rgb } from "pdf-lib";45interface SlideInfo {6filename: string;7path: string;8index: number;9promptPath?: string;10}1112function parseArgs(): { dir: string; output?: string } {13const args = process.argv.slice(2);14let dir = "";15let output: string | undefined;1617for (let i = 0; i < args.length; i++) {18if (args[i] === "--output" || args[i] === "-o") {19output = args[++i];20} else if (!args[i].startsWith("-")) {21dir = args[i];22}23}2425if (!dir) {26console.error("Usage: bun merge-to-pdf.ts <slide-deck-dir> [--output filename.pdf]");27process.exit(1);28}2930return { dir, output };31}3233function findSlideImages(dir: string): SlideInfo[] {34if (!existsSync(dir)) {35console.error(`Directory not found: ${dir}`);36process.exit(1);37}3839const files = readdirSync(dir);40const slidePattern = /^(\d+)-slide-.*\.(png|jpg|jpeg)$/i;41const promptsDir = join(dir, "prompts");42const hasPrompts = existsSync(promptsDir);4344const slides: SlideInfo[] = files45.filter((f) => slidePattern.test(f))46.map((f) => {47const match = f.match(slidePattern);48const baseName = f.replace(/\.(png|jpg|jpeg)$/i, "");49const promptPath = hasPrompts ? join(promptsDir, `${baseName}.md`) : undefined;5051return {52filename: f,53path: join(dir, f),54index: parseInt(match![1], 10),55promptPath: promptPath && existsSync(promptPath) ? promptPath : undefined,56};57})58.sort((a, b) => a.index - b.index);5960if (slides.length === 0) {61console.error(`No slide images found in: ${dir}`);62console.error("Expected format: 01-slide-*.png, 02-slide-*.png, etc.");63process.exit(1);64}6566return slides;67}6869async function createPdf(slides: SlideInfo[], outputPath: string) {70const pdfDoc = await PDFDocument.create();71pdfDoc.setAuthor("baoyu-slide-deck");72pdfDoc.setSubject("Generated Slide Deck");7374for (const slide of slides) {75const imageData = readFileSync(slide.path);76const isPng = imageData[0] === 0x89 && imageData[1] === 0x50 && imageData[2] === 0x4e && imageData[3] === 0x47;77const image = isPng78? await pdfDoc.embedPng(imageData)79: await pdfDoc.embedJpg(imageData);8081const { width, height } = image;82const page = pdfDoc.addPage([width, height]);8384page.drawImage(image, {85x: 0,86y: 0,87width,88height,89});9091console.log(`Added: ${slide.filename}${slide.promptPath ? " (prompt available)" : ""}`);92}9394const pdfBytes = await pdfDoc.save();95await Bun.write(outputPath, pdfBytes);9697console.log(`\nCreated: ${outputPath}`);98console.log(`Total pages: ${slides.length}`);99}100101async function main() {102const { dir, output } = parseArgs();103const slides = findSlideImages(dir);104105const dirName = basename(dir) === "slide-deck" ? basename(join(dir, "..")) : basename(dir);106const outputPath = output || join(dir, `${dirName}.pdf`);107108console.log(`Found ${slides.length} slides in: ${dir}\n`);109110await createPdf(slides, outputPath);111}112113main().catch((err) => {114console.error("Error:", err.message);115process.exit(1);116});117