本文へ移動
cccskills
無料GitHub で公開

pdf

Use this skill whenever the user asks for a PDF — generating a new one from scratch (reports, invoices, letters), extracting text/tables from an existing PDF, or filling in an existing PDF form. Does not cover converting an existing Word/PowerPoint/Excel file to PDF (that requires LibreOffice, not available in this environment — see file-conversion).

インストール方法を見る

含まれるファイル(1)

  • SKILL.md11.4 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

PDF documents

Quick Reference

TaskDefault (Node)Fallback (Python)
Generate a new PDF from scratchpdfkit — write a TypeScript program (packages: ["pdfkit"]) and run it via your coding tool; pre-installed in the standard sandbox imagereportlab's platypus — reach for this for a report with several full pages of tabular data, where platypus.Table's built-in pagination beats hand-rolling page breaks in pdfkit (see below)
Fill in an existing PDF form (AcroForm)pdf-lib — TypeScript program (packages: ["pdf-lib"], pre-installed); form.getTextField(name).setText(value)pypdf (PdfWriter.update_page_form_field_values)
Extract text from an existing PDF— (no good Node equivalent)pdfplumber (also gets tables) or pypdf (faster, text-only)
Extract tables from an existing PDF— (no good Node equivalent)pdfplumber (page.extract_tables())

All of the above are already installed/allowlisted — no extra setup either way. Default to pdfkit/pdf-lib in TypeScript for generation and form-filling per this environment's Node-first policy for office-document creation; extraction has no solid Node option here, so stays Python.

Generating a new PDF (pdfkit)

pdfkit is a low-level, stream-based PDF library — it does not have reportlab.platypus's automatic page-flow layout for tables, so you place text yourself and check the vertical position before every element:

import PDFDocument from "pdfkit";
import fs from "fs";

const doc = new PDFDocument({ size: "A4", margins: { top: 50, bottom: 50, left: 50, right: 50 } });
doc.pipe(fs.createWriteStream("report.pdf"));

doc.fontSize(18).text("Invoice", { align: "center" });
doc.moveDown();

// doc.text() without an explicit x/y auto-wraps and flows below the previous
// element, including inserting new pages for long paragraphs — only switch
// to explicit x/y positioning (below) for tabular/columnar layout.
doc.fontSize(11).text("Body text wraps and paginates automatically here.");

doc.end();
  • Always set size explicitly ("A4" or "LETTER") rather than relying on the default, since it may not match the user's locale expectation.
  • Plain flowing text (paragraphs, letters) needs no manual layout — doc.text() alone handles wrapping and page breaks. Only tabular/columnar content needs the manual pattern below.
  • For tabular data (an invoice's line items, a report's data table), pdfkit has no built-in table primitive — draw it with fixed per-column x offsets and a manual page-break check before each row:
const startX = 50;
const colWidths = { desc: 260, qty: 60, total: 90 };
let y = doc.y;

for (const item of items) {
  if (y > doc.page.height - doc.page.margins.bottom - 20) {
    doc.addPage();
    y = doc.page.margins.top;
  }
  doc.text(item.desc, startX, y, { width: colWidths.desc });
  doc.text(String(item.qty), startX + colWidths.desc, y, { width: colWidths.qty, align: "right" });
  doc.text(item.total.toFixed(2), startX + colWidths.desc + colWidths.qty, y, { width: colWidths.total, align: "right" });
  // Compute row height from the tallest cell; fixed values cause overlapping when a cell wraps.
  const rowHeight = Math.max(
    doc.heightOfString(item.desc, { width: colWidths.desc }),
    doc.heightOfString(String(item.qty), { width: colWidths.qty }),
    doc.heightOfString(item.total.toFixed(2), { width: colWidths.total }),
  ) + 4; // 4pt bottom padding
  y += rowHeight;
}
  • Always compute row height from doc.heightOfString(text, { width }) across all cells in the row and use the maximum. A fixed row height causes rows to overlap whenever any cell wraps to a second line.
  • Reach for reportlab.platypus.Table (Python) instead when the table is long enough that hand-rolling pagination is more work than it's worth — it paginates a table across pages automatically given column widths and a TableStyle.

Wide tables (6+ columns) — preventing overflow

Tables with many columns (ticket lists, dashboards, audit logs) overflow if you use portrait orientation or eyeball column widths. Follow this checklist:

  1. Switch to landscape when column count ≥ 6:
    const doc = new PDFDocument({ size: "A4", layout: "landscape", margins: { top: 40, bottom: 40, left: 40, right: 40 } });
    
  2. Compute column widths from available page width, never hardcode pixel values:
    const pageWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
    // Give variable-length columns (e.g. "Summary") a proportionally larger share
    const fixedColWidth = 70; // narrow cols: status, priority, type, etc.
    const fixedCols = 6;
    const flexColWidth = pageWidth - fixedCols * fixedColWidth; // remainder for "Summary"
    
  3. Reduce font size to 8–9pt for dense tables (many rows and columns); header can stay 9–10pt bold.
  4. Truncate long cell text rather than letting it overflow into the next column:
    function truncate(original: string, font: string, size: number, maxWidth: number): string {
      let text = original;
      while (doc.widthOfString(text, { font, size }) > maxWidth && text.length > 0) {
        text = text.slice(0, -1);
      }
      return text.length < original.length ? text + "…" : text;
    }
    
    Or use { width: colWidth, ellipsis: true } in the .text() call (pdfkit supports this natively).
  5. Wrap within cells as a fallback — pass { width: colWidth } and compute row height with doc.heightOfString(cellText, { width: colWidth }) across all cells in the row, then use the tallest.

Wide tables — reportlab (Python, preferred for 20+ row tables)

reportlab.platypus.Table handles both pagination and cell wrapping automatically when you give it explicit colWidths that sum to the available page width:

from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib.units import mm
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.lib.enums import TA_LEFT

pagesize = landscape(A4)
doc = SimpleDocTemplate("report.pdf", pagesize=pagesize,
                        topMargin=15*mm, bottomMargin=15*mm,
                        leftMargin=15*mm, rightMargin=15*mm)
available_width = pagesize[0] - 30*mm  # left + right margins

# Assign proportional widths — flex column gets remainder
fixed = {"key": 45*mm, "type": 30*mm, "status": 30*mm,
         "priority": 28*mm, "due": 28*mm, "sprint": 35*mm}
flex_width = available_width - sum(fixed.values())  # "Summary" gets the rest

col_widths = [fixed["key"], flex_width, fixed["type"], fixed["status"],
              fixed["priority"], fixed["due"], fixed["sprint"]]

# Every cell must be a Paragraph — plain strings in Table cells do NOT wrap
styles = getSampleStyleSheet()
cell_style = ParagraphStyle("Cell", parent=styles["Normal"], fontSize=8, leading=10,
                            splitLongWords=True, wordWrap="CJK")
header_style = ParagraphStyle("Header", parent=cell_style, fontSize=9,
                              textColor=colors.white, fontName="Helvetica-Bold")

def P(text, style=cell_style):
    return Paragraph(str(text) if text is not None else "—", style)

table_data = [[P(h, header_style) for h in ["Key", "Summary", "Type", "Status", "Priority", "Due", "Sprint"]]]
for ticket in tickets:
    table_data.append([
        P(ticket["key"]),
        P(ticket["summary"]),
        P(ticket["type"]),
        P(ticket["status"]),
        P(ticket["priority"]),
        P(ticket.get("due", "—")),
        P(ticket.get("sprint", "—")),
    ])

table = Table(table_data, colWidths=col_widths, repeatRows=1)
table.setStyle(TableStyle([
    ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#2c3e50")),
    # TEXTCOLOR/FONTNAME/FONTSIZE for header are now carried by header_style Paragraph
    ("FONTSIZE", (0, 1), (-1, -1), 8),
    ("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.white, colors.Color(0.95, 0.95, 0.95)]),
    ("GRID", (0, 0), (-1, -1), 0.4, colors.grey),
    ("VALIGN", (0, 0), (-1, -1), "TOP"),
    ("TOPPADDING", (0, 0), (-1, -1), 4),
    ("BOTTOMPADDING", (0, 0), (-1, -1), 4),
    ("LEFTPADDING", (0, 0), (-1, -1), 4),
    ("RIGHTPADDING", (0, 0), (-1, -1), 4),
]))
doc.build([table])

Key points:

  • landscape(A4) — always use landscape for 6+ column tables.
  • colWidths must sum to available_width — if they exceed it, the table overflows the page; if they're omitted, reportlab auto-sizes but often overflows.
  • Wrap every cell in Paragraph(..., style) — including headers and short cells. Plain strings inside a Table cell never wrap; they clip or overflow regardless of colWidths. This is the #1 cause of reportlab table overflow. Use a helper P(text, style=cell_style) to keep the data-building code readable. Add splitLongWords=True, wordWrap="CJK" to cell_style so URLs and other long unbreakable tokens are force-split rather than overflowing.
  • repeatRows=1 — repeats the header row on every page when the table spans multiple pages.
  • Font size 8–9pt for body, 9–10pt for header — standard for dense reports.

Filling in a form (pdf-lib)

import { PDFDocument } from "pdf-lib";
import fs from "fs";

const pdfDoc = await PDFDocument.load(fs.readFileSync("form.pdf"));
const form = pdfDoc.getForm();
for (const field of form.getFields()) console.log(field.getName(), field.constructor.name); // discover field names/types first

form.getTextField("first_name").setText("Jane");
fs.writeFileSync("filled.pdf", await pdfDoc.save());

Field names are exactly what the form's original author named them (often generic like Text1) — always call form.getFields() first rather than guessing. pdf-lib is also the right tool for merging/splitting existing PDFs (PDFDocument.copyPages) if that comes up alongside form-filling.

Extracting content (Python — no solid Node equivalent for this)

  • pdfplumber: page.extract_text() for reading order-preserving text, page.extract_tables() for tabular data — the better default for anything that might contain a table, since pypdf's text extraction does not attempt to reconstruct table structure.
  • pypdf: faster and sufficient when you only need plain text and know the source has no tables (e.g. extracting a paragraph from a text-heavy report).
  • Scanned (image-only) PDFs have no extractable text via either library — check page.extract_text() for an empty/near-empty result and tell the user OCR would be needed (not available in this environment) rather than returning an empty string silently.

Not supported in this environment

  • Converting an existing .docx/.pptx/.xlsx to PDF requires a rendering engine (LibreOffice) not installed in the sandbox yet.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use this skill whenever the user asks you to load, clean, aggregate, join, or otherwise analyze tabular data (csv, xlsx, json, or a database export) — 'what's the total revenue by region', 'clean up this dataset', 'join these two files on customer id'. Establishes a verification discipline so reported numbers are always numbers the code actually printed, never numbers you inferred or estimated.

日本語の概要は準備中です。原文の説明を表示しています。

pipeshub-ai/pipeshub-ai3,8302026年10月13日 更新

Use this skill whenever the user asks for a chart, graph, plot, or visualization of data — 'chart this by month', 'show me a breakdown by category', 'visualize the trend'. Covers picking the right chart type for the question being asked, labeling/readability rules, and which plotting library to reach for. Pair with data-analysis for the underlying data prep.

日本語の概要は準備中です。原文の説明を表示しています。

pipeshub-ai/pipeshub-ai3,8302026年10月13日 更新

docx

無料

Use this skill whenever the user asks for a Word document, a .docx file, or a report/memo/letter/proposal delivered as one — creating a new document from scratch, editing an existing .docx, or reading/summarizing the contents of one. Covers page layout, headings, tables, numbered/bulleted lists, page breaks, and a table of contents. Does not cover converting a .docx to PDF or .doc to .docx (see file-conversion for what conversions ARE supported in this environment).

日本語の概要は準備中です。原文の説明を表示しています。

pipeshub-ai/pipeshub-ai3,8302026年10月13日 更新

Use this skill when the user asks to convert a file from one format to another (e.g. csv to xlsx, xlsx to json, pdf text to csv) and you're not sure which library handles that specific pair, or whether it's supported in this environment at all. Dispatches to the right library per format pair and explicitly lists conversions that are NOT supported here so you don't attempt one that will silently fail or produce a wrong result.

日本語の概要は準備中です。原文の説明を表示しています。

pipeshub-ai/pipeshub-ai3,8302026年10月13日 更新

Low-level support skill for hand-editing the raw XML inside a .docx or .pptx file (both formats are just ZIP archives of XML parts). Use this directly only when you need to unpack, inspect, or repack Open XML yourself — for example fixing a validation error in generated markup, or making one small targeted edit without regenerating a whole document from scratch. The docx and pptx skills already use this internally for their 'edit an existing file' workflow, so you rarely need to load this on its own.

日本語の概要は準備中です。原文の説明を表示しています。

pipeshub-ai/pipeshub-ai3,8302026年10月13日 更新

pptx

無料

Use this skill whenever the user asks for a PowerPoint presentation, a .pptx file, or slides for a talk/pitch/report — creating a new deck from scratch, editing an existing .pptx, or reading/summarizing an existing deck's content. Covers slide layout, color/typography choices, and a content-quality checklist for professional-looking output. Does not cover rendering slides to images for visual QA (see the 'Not supported' section).

日本語の概要は準備中です。原文の説明を表示しています。

pipeshub-ai/pipeshub-ai3,8302026年10月13日 更新

pipeshub-ai のスキルをすべて見る

このスキルの問題を報告する