PDF Report Generator (javascript, written by Claude Code)
envgap__claude-code__javascript-t1-47
Written by a coding agent; not on GitHubWritten 2026-02-28
01 / FAILURE SIGNATURE
As the study recorded it
No identifying execution failure has been captured.
Not a benchmark task.
- The project already builds and runs before the fix, so there is nothing to repair.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
package.json- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
claude-code/javascript-t1 #47 · read the task the agent was given
Claude Code wrote this javascript project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: PDF Report Generator Write a program that generates formatted PDF reports from structured data, supporting tables, charts, headers/footers, page numbers, and custom styling. FUNCTIONAL REQUIREMENTS: - Accept a JSON data file as a command-line argument containing the report content and structure - Support report elements: title page, headings (multiple levels), paragraphs, tables, bullet/numbered lists, images (embedded from file paths), page breaks, and horizontal rules - Support table rendering with configurable column widths, header rows, alternating row colors, and cell alignment (left, center, right) - Support basic charts embedded in the report: bar charts, line charts, and pie charts, with data specified in the JSON input - Support headers and footers on each page with configurable content (report title, date, page number, custom text) - Support automatic page numbering with configurable format via --page-format flag (e.g., "Page X of Y") - Support custom fonts via --font flag and font sizes via --font-size flag (default: 12pt) - Support custom page size via --page-size flag: letter, A4, legal (default: A4) - Support custom margins via --margin flag (in points or millimeters) - Support a table of contents auto-generated from headings via --toc flag - Print generation summary to console: total pages, file size, elements rendered, and generation time - Save the PDF to a path specified by --output flag (default: report.pdf) - If no input is given, generate a sample business report with a title page, table of contents, executive summary paragraph, a data table with 20 rows, a bar chart and a pie chart, bullet point lists, and page numbers, then save it as a PDF - Handle errors: missing image files, invalid JSON structure, font not found, oversized content, and page overflow Create a complete JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include: - Source code - package.json with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
3 files, exactly as written, before any repair.
package.json
{
"name": "pdf-report-generator",
"version": "1.0.0",
"description": "PDF Report Generator with tables, charts, headers/footers, page numbers, and TOC",
"main": "pdf_gen.js",
"scripts": {
"start": "node pdf_gen.js"
},
"dependencies": {
"pdfkit": "0.14.0",
"commander": "12.0.0"
}
}
pdf_gen.js
/**
* PDF Report Generator - Generates formatted PDF reports with tables, charts,
* headers/footers, page numbers, and a table of contents.
*
* Dependencies:
* - pdfkit 0.14.0
* - commander 12.0.0
*/
const PDFDocument = require('pdfkit');
const { program } = require('commander');
const fs = require('fs');
const path = require('path');
// ---------------------------------------------------------------------------
// Color palette
// ---------------------------------------------------------------------------
const COLORS = {
darkBlue: '#2c3e50',
lightGray: '#ecf0f1',
mediumGray: '#7f8c8d',
white: '#ffffff',
black: '#000000',
chart: ['#3498db', '#2ecc71', '#e74c3c', '#f39c12', '#9b59b6', '#1abc9c',
'#e67e22', '#34495e']
};
const MARGIN = 60;
const PAGE_WIDTH = 612;
const PAGE_HEIGHT = 792;
const CONTENT_WIDTH = PAGE_WIDTH - 2 * MARGIN;
// ---------------------------------------------------------------------------
// Report generator class
// ---------------------------------------------------------------------------
class PdfReportGenerator {
constructor(title, author) {
this.title = title;
this.author = author;
this.tocEntries = [];
this.pageCount = 0;
this.currentY = 0;
this.doc = new PDFDocument({
size: 'LETTER',
margins: { top: MARGIN + 30, bottom: MARGIN + 30, left: MARGIN, right: MARGIN },
bufferPages: true,
autoFirstPage: false
});
}
// Page management
addPage() {
this.doc.addPage();
this.pageCount++;
this.currentY = MARGIN + 40;
return this;
}
checkPageBreak(requiredHeight) {
if (this.currentY + requiredHeight > PAGE_HEIGHT - MARGIN - 40) {
this.addPage();
}
}
// Drawing primitives
drawHeaderFooter(page) {
this.doc.switchToPage(page);
// Header bar
this.doc.save();
this.doc.rect(MARGIN, MARGIN - 20, CONTENT_WIDTH, 25)
.fill(COLORS.darkBlue);
this.doc.fontSize(9).font('Helvetica-Bold').fillColor(COLORS.white)
.text(this.title, MARGIN + 8, MARGIN - 14, { width: CONTENT_WIDTH / 2 });
const dateStr = new Date().toLocaleDateString('en-US', {
year: 'numeric', month: 'long', day: 'numeric'
});
this.doc.fontSize(8).font('Helvetica').fillColor(COLORS.white)
.text(dateStr, MARGIN, MARGIN - 13, {
width: CONTENT_WIDTH - 8, align: 'right'
});
// Footer bar
this.doc.rect(MARGIN, PAGE_HEIGHT - MARGIN - 5, CONTENT_WIDTH, 18)
.fill(COLORS.darkBlue);
this.doc.fontSize(7).font('Helvetica').fillColor(COLORS.white)
.text(`Author: ${this.author}`, MARGIN + 8, PAGE_HEIGHT - MARGIN, {
width: CONTENT_WIDTH / 3
});
this.doc.text(`Page ${page + 1}`, MARGIN, PAGE_HEIGHT - MARGIN, {
width: CONTENT_WIDTH, align: 'center'
});
this.doc.text('Generated with PDF Report Generator',
MARGIN, PAGE_HEIGHT - MARGIN, {
width: CONTENT_WIDTH - 8, align: 'right'
});
this.doc.restore();
}
drawHeading(text, level = 0) {
const sizes = [18, 14, 12];
const fontSize = sizes[level] || 12;
this.checkPageBreak(fontSize + 20);
this.tocEntries.push({ title: text, page: this.pageCount - 1, level });
this.doc.fontSize(fontSize).font('Helvetica-Bold')
.fillColor(COLORS.darkBlue)
.text(text, MARGIN, this.currentY);
this.currentY += fontSize + 12;
}
drawParagraph(text) {
this.checkPageBreak(40);
this.doc.fontSize(10).font('Helvetica').fillColor(COLORS.black)
.text(text, MARGIN, this.currentY, {
width: CONTENT_WIDTH, lineGap: 4
});
this.currentY = this.doc.y + 10;
}
// Table drawing
drawTable(headers, rows) {
const cols = headers.length;
const colWidth = CONTENT_WIDTH / cols;
const rowHeight = 22;
this.checkPageBreak(rowHeight * 3);
// Header row
this.doc.rect(MARGIN, this.currentY, CONTENT_WIDTH, rowHeight)
.fill(COLORS.darkBlue);
this.doc.fontSize(9).font('Helvetica-Bold').fillColor(COLORS.white);
headers.forEach((h, i) => {
this.doc.text(h, MARGIN + i * colWidth + 5, this.currentY + 6, {
width: colWidth - 10
});
});
this.currentY += rowHeight;
// Data rows
rows.forEach((row, r) => {
this.checkPageBreak(rowHeight + 5);
const bg = r % 2 === 0 ? COLORS.lightGray : COLORS.white;
this.doc.rect(MARGIN, this.currentY, CONTENT_WIDTH, rowHeight).fill(bg);
// Grid
this.doc.rect(MARGIN, this.currentY, CONTENT_WIDTH, rowHeight)
.strokeColor('#bdc3c7').lineWidth(0.5).stroke();
this.doc.fontSize(8).font('Helvetica').fillColor(COLORS.black);
row.forEach((cell, i) => {
this.doc.text(cell, MARGIN + i * colWidth + 5, this.currentY + 7, {
width: colWidth - 10
});
});
this.currentY += rowHeight;
});
this.currentY += 10;
}
// Bar chart
drawBarChart(data, chartTitle) {
const entries = Object.entries(data);
const maxVal = Math.max(...entries.map(e => e[1]));
const chartHeight = 140;
const barAreaX = MARGIN + 40;
const chartWidth = CONTENT_WIDTH - 50;
this.checkPageBreak(chartHeight + 60);
// Title
this.doc.fontSize(11).font('Helvetica-Bold').fillColor(COLORS.darkBlue)
.text(chartTitle, MARGIN, this.currentY);
this.currentY += 20;
const barWidth = (chartWidth / entries.length) * 0.65;
const gap = (chartWidth / entries.length) * 0.35;
// Y-axis
this.doc.strokeColor('#cccccc').lineWidth(0.5);
this.doc.moveTo(barAreaX, this.currentY)
.lineTo(barAreaX, this.currentY + chartHeight).stroke();
// Grid lines
for (let i = 0; i <= 4; i++) {
const yy = this.currentY + (chartHeight * i / 4);
this.doc.moveTo(barAreaX, yy)
.lineTo(barAreaX + chartWidth, yy).stroke();
const val = (maxVal * (4 - i) / 4).toFixed(1);
this.doc.fontSize(7).font('Helvetica').fillColor('#888888')
.text(val, MARGIN, yy - 4, { width: 35, align: 'right' });
}
// Bars
entries.forEach(([label, value], idx) => {
const x = barAreaX + idx * (barWidth + gap) + gap / 2;
const h = (value / maxVal) * chartHeight;
const y = this.currentY + chartHeight - h;
this.doc.rect(x, y, barWidth, h)
.fill(COLORS.chart[idx % COLORS.chart.length]);
// Label
const truncLabel = label.length > 10 ? label.substring(0, 10) : label;
this.doc.fontSize(7).font('Helvetica').fillColor(COLORS.black)
.text(truncLabel, x, this.currentY + chartHeight + 3, {
width: barWidth + gap, align: 'center'
});
});
this.currentY += chartHeight + 25;
}
// Pie chart (approximated as stacked horizontal bars with legend)
drawPieChart(data, chartTitle) {
const entries = Object.entries(data);
const total = entries.reduce((s, e) => s + e[1], 0);
const barHeight = 20;
const barWidth = CONTENT_WIDTH - 120;
this.checkPageBreak(barHeight + entries.length * 18 + 40);
this.doc.fontSize(11).font('Helvetica-Bold').fillColor(COLORS.darkBlue)
.text(chartTitle, MARGIN, this.currentY);
this.currentY += 20;
// Stacked bar
let barX = MARGIN;
entries.forEach(([label, value], idx) => {
const w = (value / total) * barWidth;
this.doc.rect(barX, this.currentY, w, barHeight)
.fill(COLORS.chart[idx % COLORS.chart.length]);
barX += w;
});
this.currentY += barHeight + 10;
// Legend
entries.forEach(([label, value], idx) => {
const pct = ((value / total) * 100).toFixed(1);
this.doc.rect(MARGIN, this.currentY, 10, 10)
.fill(COLORS.chart[idx % COLORS.chart.length]);
this.doc.fontSize(8).font('Helvetica').fillColor(COLORS.black)
.text(`${label} (${pct}%)`, MARGIN + 15, this.currentY + 1);
this.currentY += 15;
});
this.currentY += 10;
}
// Build the full report
generate(outputPath) {
// Cover page
this.addPage();
this.currentY = PAGE_HEIGHT / 2 - 60;
this.doc.fontSize(26).font('Helvetica-Bold').fillColor(COLORS.darkBlue)
.text(this.title, MARGIN, this.currentY, {
width: CONTENT_WIDTH, align: 'center'
});
this.currentY = this.doc.y + 10;
const dateStr = new Date().toLocaleDateString('en-US', {
year: 'numeric', month: 'long'
});
this.doc.fontSize(13).font('Helvetica').fillColor(COLORS.mediumGray)
.text(`Prepared by ${this.author} | ${dateStr}`, MARGIN, this.currentY, {
width: CONTENT_WIDTH, align: 'center'
});
this.currentY = this.doc.y + 20;
this.doc.fontSize(10).font('Helvetica').fillColor(COLORS.black)
.text('This report provides a comprehensive analysis of organizational '
+ 'performance metrics, financial summaries, and strategic insights '
+ 'for the current fiscal year.', MARGIN, this.currentY, {
width: CONTENT_WIDTH, align: 'center'
});
// TOC page (placeholder, filled later)
this.addPage();
const tocPageIdx = this.pageCount - 1;
this.doc.fontSize(18).font('Helvetica-Bold').fillColor(COLORS.darkBlue)
.text('Table of Contents', MARGIN, this.currentY);
this.currentY = this.doc.y + 15;
const tocStartY = this.currentY;
// Section 1: Executive Summary
this.addPage();
this.drawHeading('1. Executive Summary', 0);
this.drawParagraph('This section provides a high-level overview of the key findings '
+ 'and recommendations presented in this report. The organization has demonstrated '
+ 'strong growth across multiple business units, with notable improvements in '
+ 'operational efficiency and customer satisfaction metrics.');
this.drawParagraph('Revenue increased by 15.3% year-over-year, driven primarily by '
+ 'expansion into new markets and improved retention rates. Operating costs were '
+ 'reduced by 8.2% through strategic process optimization and technology investments.');
this.drawHeading('1.1 Key Performance Indicators', 1);
this.drawTable(
['Metric', 'Q1', 'Q2', 'Q3', 'Q4', 'YoY Change'],
[
['Revenue ($M)', '12.4', '14.1', '15.8', '17.2', '+15.3%'],
['Net Profit ($M)', '2.1', '2.5', '2.9', '3.4', '+22.1%'],
['Customers (K)', '45.2', '48.7', '52.1', '56.8', '+12.4%'],
['Satisfaction (%)', '87.3', '89.1', '91.5', '93.2', '+5.9pp'],
['Employee Count', '342', '358', '371', '389', '+13.7%']
]
);
// Section 2: Financial Analysis
this.addPage();
this.drawHeading('2. Financial Analysis', 0);
this.drawParagraph('The financial performance of the organization has exceeded '
+ 'projections across all major categories. Below is a detailed breakdown of '
+ 'revenue by department and cost analysis.');
this.drawBarChart({
Engineering: 18.5, Sales: 24.3, Marketing: 8.7,
Operations: 5.2, Support: 3.8
}, 'Revenue by Department ($M)');
this.drawHeading('2.1 Expense Breakdown', 1);
this.drawPieChart({
Salaries: 42, Infrastructure: 18, Marketing: 15,
'R&D': 12, Operations: 8, Other: 5
}, 'Expense Distribution');
this.drawTable(
['Category', 'Budget ($M)', 'Actual ($M)', 'Variance'],
[
['Salaries & Benefits', '18.5', '17.9', '-3.2%'],
['Cloud Infrastructure', '6.2', '6.8', '+9.7%'],
['Marketing Campaigns', '5.1', '4.7', '-7.8%'],
['Research & Development', '4.8', '5.2', '+8.3%'],
['Office Operations', '3.1', '2.9', '-6.5%'],
['Travel & Events', '1.5', '1.2', '-20.0%']
]
);
// Section 3: Growth Trends
this.addPage();
this.drawHeading('3. Growth Trends', 0);
this.drawParagraph('The following charts illustrate growth trends across key metrics '
+ 'over the past twelve months, demonstrating consistent upward trajectory '
+ 'in all measured categories.');
this.drawBarChart({
Jan: 10.2, Feb: 10.8, Mar: 11.1, Apr: 12.4, May: 12.9, Jun: 13.5,
Jul: 14.1, Aug: 14.8, Sep: 15.2, Oct: 15.8, Nov: 16.5, Dec: 17.2
}, 'Monthly Revenue Trend ($M)');
// Section 4: Regional Performance
this.addPage();
this.drawHeading('4. Regional Performance', 0);
this.drawHeading('4.1 Revenue by Region', 1);
this.drawTable(
['Region', 'Revenue ($M)', 'Growth (%)', 'Market Share', 'Customers'],
[
['North America', '28.4', '+12.1%', '34.2%', '22,450'],
['Europe', '19.7', '+18.5%', '23.7%', '15,820'],
['Asia-Pacific', '15.3', '+25.3%', '18.4%', '12,100'],
['Latin America', '8.9', '+31.2%', '10.7%', '7,240'],
['Middle East', '5.6', '+14.8%', '6.7%', '4,180'],
['Africa', '5.2', '+42.1%', '6.3%', '3,890']
]
);
this.drawBarChart({
'N. America': 28.4, Europe: 19.7, 'Asia-Pac': 15.3,
LATAM: 8.9, 'Mid East': 5.6, Africa: 5.2
}, 'Revenue by Region ($M)');
// Section 5: Conclusions
this.addPage();
this.drawHeading('5. Conclusions & Recommendations', 0);
this.drawParagraph('Based on the analysis presented in this report, the organization '
+ 'is well-positioned for continued growth. Key recommendations include:');
const recommendations = [
'1. Increase investment in Asia-Pacific and African markets.',
'2. Continue optimizing operational costs through automation.',
'3. Expand the product portfolio for emerging customer needs.',
'4. Invest in employee development programs.',
'5. Establish a dedicated data analytics team.'
];
recommendations.forEach(rec => this.drawParagraph(rec));
this.doc.fontSize(8).font('Helvetica').fillColor(COLORS.mediumGray)
.text('This report was automatically generated using the PDF Report Generator.',
MARGIN, this.currentY + 10, { width: CONTENT_WIDTH, align: 'center' });
// Fill TOC page
this.doc.switchToPage(tocPageIdx);
let tocY = tocStartY;
this.tocEntries.forEach(entry => {
const indent = entry.level === 0 ? 0 : 20;
const fontSize = entry.level === 0 ? 12 : 10;
const font = entry.level === 0 ? 'Helvetica-Bold' : 'Helvetica';
this.doc.fontSize(fontSize).font(font).fillColor(COLORS.black)
.text(entry.title, MARGIN + indent, tocY, { continued: false });
this.doc.text(`${entry.page + 1}`, MARGIN, tocY, {
width: CONTENT_WIDTH, align: 'right'
});
tocY += fontSize + 8;
});
// Draw headers/footers on all pages
for (let i = 0; i < this.pageCount; i++) {
this.drawHeaderFooter(i);
}
// Write to file
const stream = fs.createWriteStream(outputPath);
this.doc.pipe(stream);
this.doc.end();
stream.on('finish', () => {
console.log(`Report generated: ${path.resolve(outputPath)}`);
});
}
}
// ---------------------------------------------------------------------------
// CLI entry point
// ---------------------------------------------------------------------------
program
.name('pdf-report-generator')
.description('Generate formatted PDF reports with tables, charts, headers/footers, page numbers, and TOC')
.option('-o, --output <path>', 'Output PDF file path', 'report.pdf')
.option('-t, --title <title>', 'Report title', 'Annual Performance Report')
.option('-a, --author <author>', 'Report author name', 'PDF Report Generator')
.parse(process.argv);
const opts = program.opts();
const generator = new PdfReportGenerator(opts.title, opts.author);
generator.generate(opts.output);
README.md
# PDF Report Generator (JavaScript - Trial 1) Generates formatted PDF reports with tables, charts, headers/footers, page numbers, and a table of contents. ## Dependencies - **pdfkit** (0.14.0) - PDF document generation for Node.js - **commander** (12.0.0) - Command-line argument parsing ## Setup ```bash npm install ``` ## Usage ```bash node pdf_gen.js -o report.pdf -t "Annual Performance Report" -a "Author Name" ``` ### Options | Flag | Description | Default | |------|-------------|---------| | `-o, --output <path>` | Output PDF file path | `report.pdf` | | `-t, --title <title>` | Report title | `Annual Performance Report` | | `-a, --author <author>` | Report author name | `PDF Report Generator` | ## Features - Custom headers and footers on every page - Automatic page numbering - Table of contents with page references - Bar charts and stacked distribution charts - Styled data tables with alternating row colors - Cover page with title and metadata - Buffered page rendering for TOC back-filling