Skip to content

Examples

carboncopy の使用例を紹介します。

Hello World

最もシンプルなPDF生成例です。

typescript
import { createPDF } from "@osaxyz/carboncopy"

const pdf = createPDF({ format: "A4" })
pdf.addPage()
pdf.drawText("Hello, World!", 50, 750, {
    font: "Helvetica",
    fontSize: 24,
})

const result = pdf.save()
result.download("hello.pdf")

請求書テンプレート

typescript
import { createPDF, PAGE_SIZES } from "@osaxyz/carboncopy"

const pdf = createPDF({
    format: "A4",
    title: "請求書",
})

pdf.addPage()

const page_width = PAGE_SIZES.A4.width
const page_height = PAGE_SIZES.A4.height

// ヘッダー
pdf.drawText("請求書", 50, page_height - 50, {
    font: "Helvetica-Bold",
    fontSize: 28,
})

pdf.drawText("Invoice #INV-2024-001", page_width - 200, page_height - 50, {
    font: "Helvetica",
    fontSize: 12,
    color: { r: 0.5, g: 0.5, b: 0.5 },
})

// 区切り線
pdf.drawLine(50, page_height - 70, page_width - 50, page_height - 70, {
    color: { r: 0.8, g: 0.8, b: 0.8 },
    width: 1,
})

// 請求先情報
pdf.drawText("請求先:", 50, page_height - 100, {
    font: "Helvetica-Bold",
    fontSize: 12,
})

pdf.drawText("株式会社サンプル", 50, page_height - 120, {
    font: "Helvetica",
    fontSize: 12,
})

// テーブルヘッダー
const table_y = page_height - 200
pdf.drawRect(50, table_y - 25, page_width - 100, 25, {
    fill: { r: 0.2, g: 0.4, b: 0.6 },
})

pdf.drawText("品目", 60, table_y - 18, {
    font: "Helvetica-Bold",
    fontSize: 11,
    color: { r: 1, g: 1, b: 1 },
})

pdf.drawText("数量", 300, table_y - 18, {
    font: "Helvetica-Bold",
    fontSize: 11,
    color: { r: 1, g: 1, b: 1 },
})

pdf.drawText("単価", 380, table_y - 18, {
    font: "Helvetica-Bold",
    fontSize: 11,
    color: { r: 1, g: 1, b: 1 },
})

pdf.drawText("金額", 480, table_y - 18, {
    font: "Helvetica-Bold",
    fontSize: 11,
    color: { r: 1, g: 1, b: 1 },
})

// テーブル行
const items = [
    { name: "Web Design", qty: 1, price: 500000 },
    { name: "Development", qty: 1, price: 800000 },
    { name: "Maintenance", qty: 12, price: 10000 },
]

let row_y = table_y - 50
items.forEach((item, i) => {
    if (i % 2 === 0) {
        pdf.drawRect(50, row_y - 5, page_width - 100, 25, {
            fill: { r: 0.95, g: 0.95, b: 0.95 },
        })
    }

    pdf.drawText(item.name, 60, row_y, { fontSize: 11 })
    pdf.drawText(String(item.qty), 300, row_y, { fontSize: 11 })
    pdf.drawText(`¥${item.price.toLocaleString()}`, 380, row_y, { fontSize: 11 })
    pdf.drawText(`¥${(item.qty * item.price).toLocaleString()}`, 480, row_y, { fontSize: 11 })

    row_y -= 25
})

// 合計
const total = items.reduce((sum, item) => sum + item.qty * item.price, 0)
pdf.drawLine(380, row_y - 10, page_width - 50, row_y - 10, { width: 1 })
pdf.drawText("合計:", 380, row_y - 30, {
    font: "Helvetica-Bold",
    fontSize: 14,
})
pdf.drawText(`¥${total.toLocaleString()}`, 480, row_y - 30, {
    font: "Helvetica-Bold",
    fontSize: 14,
})

const result = pdf.save()
result.download("invoice.pdf")

日本語文書

typescript
import { PDFDocument, PAGE_SIZES, FontManager, PDFResult } from "@osaxyz/carboncopy"
import { readFileSync, writeFileSync } from "fs"

async function main() {
    const font_manager = new FontManager()
    const font_data = readFileSync("./NotoSansJP-Regular.ttf")
    const loaded_font = await font_manager.loadFromUint8Array(
        new Uint8Array(font_data),
        "NotoSansJP"
    )

    const doc = new PDFDocument()
    doc.setInfo({ title: "日本語サンプル", author: "carboncopy" })

    const text = "こんにちは、世界!\nこれは日本語のPDFです。"

    for (const char of text) {
        const cp = char.codePointAt(0)
        if (cp) loaded_font.usedCodePoints.add(cp)
    }

    doc.addCustomFont("NotoSansJP", loaded_font, text)

    const page = doc.addPage(PAGE_SIZES.A4.width, PAGE_SIZES.A4.height)
    doc.applyFontToPage(page, "NotoSansJP")

    const lines = text.split("\n")
    let y = PAGE_SIZES.A4.height - 50

    for (const line of lines) {
        const hex = doc.textToHex("NotoSansJP", line)
        page.drawTextCID(hex, 50, y, {
            font: "NotoSansJP",
            fontSize: 18,
        })
        y -= 30
    }

    writeFileSync("japanese.pdf", new PDFResult(doc.save()).toUint8Array())
}

main()

画像付きレポート

typescript
import { PDFDocument, PAGE_SIZES, PDFResult, embedImage } from "@osaxyz/carboncopy"

async function createReport(chart_image: Uint8Array) {
    const doc = new PDFDocument()
    doc.setInfo({ title: "Monthly Report" })

    doc.addStandardFont("F1", "Helvetica-Bold")
    doc.addStandardFont("F2", "Helvetica")

    const page = doc.addPage(PAGE_SIZES.A4.width, PAGE_SIZES.A4.height)
    doc.applyFontToPage(page, "F1")
    doc.applyFontToPage(page, "F2")

    // タイトル
    page.drawText("Monthly Sales Report", 50, 780, {
        font: "F1",
        fontSize: 24,
    })

    page.drawText("January 2024", 50, 750, {
        font: "F2",
        fontSize: 14,
        color: { r: 0.5, g: 0.5, b: 0.5 },
    })

    // 画像を埋め込み
    const context = doc.getEmbedContext()
    const embedded = await embedImage(chart_image, context)
    doc.syncEmbedContext(context)

    doc.addImage(embedded.name, embedded.ref, embedded.width, embedded.height)
    doc.applyImageToPage(page, embedded.name)

    // チャートを描画
    page.drawText("Sales Chart", 50, 700, {
        font: "F1",
        fontSize: 16,
    })

    page.drawImage(embedded.name, 50, 400, 400, 250)

    // 説明文
    page.drawText("Key highlights:", 50, 370, {
        font: "F1",
        fontSize: 14,
    })

    const highlights = [
        "• Total revenue increased by 15%",
        "• New customer acquisition up 20%",
        "• Customer retention rate: 92%",
    ]

    let text_y = 340
    for (const text of highlights) {
        page.drawText(text, 60, text_y, {
            font: "F2",
            fontSize: 12,
        })
        text_y -= 20
    }

    return new PDFResult(doc.save())
}

複数ページのドキュメント

typescript
import { createPDF, PAGE_SIZES } from "@osaxyz/carboncopy"

const content = [
    { title: "Chapter 1", body: "Introduction to the topic..." },
    { title: "Chapter 2", body: "Main content goes here..." },
    { title: "Chapter 3", body: "Conclusion and summary..." },
]

const pdf = createPDF({ format: "A4", title: "My Book" })

content.forEach((chapter, index) => {
    pdf.addPage()

    // ヘッダー
    pdf.drawText(chapter.title, 50, PAGE_SIZES.A4.height - 50, {
        font: "Helvetica-Bold",
        fontSize: 24,
    })

    // 本文
    pdf.drawText(chapter.body, 50, PAGE_SIZES.A4.height - 100, {
        font: "Helvetica",
        fontSize: 12,
    })

    // フッター(ページ番号)
    pdf.drawText(`Page ${index + 1}`, PAGE_SIZES.A4.width / 2 - 20, 30, {
        font: "Helvetica",
        fontSize: 10,
        color: { r: 0.5, g: 0.5, b: 0.5 },
    })
})

const result = pdf.save()
result.download("book.pdf")

HTML要素の変換

typescript
import { convert } from "@osaxyz/carboncopy"

// HTML要素を取得
const element = document.getElementById("report-content")

// PDFに変換
const result = await convert(element, {
    format: "A4",
    margin: { top: 40, right: 40, bottom: 40, left: 40 },
    title: "Report",
    includeBackground: true,
})

// ダウンロード
result.download("report.pdf")

Node.js でのファイル保存

typescript
import { createPDF } from "@osaxyz/carboncopy"
import { writeFileSync } from "fs"

const pdf = createPDF({ format: "A4" })
pdf.addPage()
pdf.drawText("Server-side PDF", 50, 750, { fontSize: 24 })

const result = pdf.save()
writeFileSync("output.pdf", result.toUint8Array())

console.log(`PDF saved: ${result.size} bytes`)

Released under the MIT License.