Discover OfficeForge: Enhance Microsoft Office with Open-Source Automation

Published: (December 7, 2025 at 03:39 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

Part I: For Any Developer in Any Language

A Lightweight CLI That Works From Any Language

OfficeForge comes with a CLI tool that lets you automate Microsoft Office documents without touching Go. Whether you code in Python, Node.js, PHP, or Bash, you can generate Word files, fill templates, or batch‑process documents while keeping the formatting intact and without juggling parameters.

Get started by downloading the binary for your Windows architecture from the Releases page.

Replace a single keyword in a template

officeforge docx-single --input template.docx --output output.docx --key "{{NAME}}" --value "John Doe"

Generate multiple documents from CSV/JSON

officeforge docx-batch --input template.docx --output ./contracts --data employees.csv

Flexible Naming Patterns

Control how your generated documents are named using patterns:

# Sequential numbering
officeforge docx-batch --input template.docx --output ./output --data employees.csv --pattern "contract_%03d.docx"
# Creates: contract_001.docx, contract_002.docx, contract_003.docx

# Data‑based naming (CSV must have NAME column)
officeforge docx-batch --input template.docx --output ./output --data employees.csv --pattern "{NAME}_contract.docx"
# Creates: Alice_contract.docx, Bob_contract.docx

# Multiple fields (CSV has ID and COMPANY columns)
officeforge docx-batch --input template.docx --output ./output --data clients.csv --pattern "{COMPANY}_{ID}_agreement.docx"
# Creates: Acme_001_agreement.docx, TechCorp_002_agreement.docx

# Combine data and index
officeforge docx-batch --input template.docx --output ./output --data employees.csv --pattern "{DEPARTMENT}_report_{INDEX}.docx"
# Creates: Engineering_report_1.docx, Sales_report_2.docx

Because OfficeForge is a standalone binary with zero dependencies, you can call it from any language that can run system commands.

Node.js Example (using child_process)

import { exec } from "node:child_process";
import { promisify } from "node:util";

const execAsync = promisify(exec);

(async () => {
  try {
    const { stdout, stderr } = await execAsync(
      `officeforge docx-batch --input template.docx --output ./contracts --data employees.csv --pattern "{EMPLOYEE_NAME} Contract.docx"`
    );

    if (stderr) console.error("CLI stderr:", stderr);
    console.log("Success:", stdout);
  } catch (err) {
    console.error("Error:", err);
  }
})();

Python Example (using subprocess)

import subprocess

cmd = [
    "officeforge",
    "docx-batch",
    "--input", "template.docx",
    "--output", "./contracts",
    "--data", "employees.csv",
    "--pattern", "{EMPLOYEE_NAME} Contract.docx"
]

result = subprocess.run(cmd, capture_output=True, text=True)

if result.returncode != 0:
    print("Error:", result.stderr)
else:
    print("Success:", result.stdout)

PHP Example (using exec)

&1", $output, $returnCode);

if ($returnCode !== 0) {
    echo "Error:\n";
    echo implode("\n", $output);
} else {
    echo "Success:\n";
    echo implode("\n", $output);
}
?>

Whether you’re working in Node.js, Python, PHP, Bash, Go, or anything else, OfficeForge’s CLI gives you a language‑agnostic way to automate DOCX generation at high speeds.

Part II: For Go Developers Who Want Native Integration

Integrate OfficeForge Directly in Your Go Code

For Go developers, OfficeForge is also a fully programmatic library that can be embedded in your applications.

Installation

go get github.com/siliconcatalyst/officeforge@latest

Or install the CLI for mixed usage:

go install github.com/siliconcatalyst/officeforge/cmd/officeforge@latest

Single Keyword Replacement

import "github.com/siliconcatalyst/officeforge/docx"

err := docx.ProcessDocxSingle("template.docx", "output.docx", "{{NAME}}", "John Doe")
if err != nil {
    log.Fatal(err)
}

Multiple Replacements

import "github.com/siliconcatalyst/officeforge/docx"

replacements := map[string]string{
    "{{NAME}}":  "John Doe",
    "{{EMAIL}}": "john@example.com",
    "{{DATE}}":  "2025-12-07",
}

err := docx.ProcessDocxMulti("template.docx", "output.docx", replacements)
if err != nil {
    log.Fatal(err)
}

Batch Document Generation

import "github.com/siliconcatalyst/officeforge/docx"

records := []map[string]string{
    {"NAME": "Alice", "EMAIL": "alice@example.com"},
    {"NAME": "Bob", "EMAIL": "bob@example.com"},
}

// Sequential numbering
err := docx.ProcessDocxMultipleRecords("template.docx", "./output", records, "contract_%03d.docx")
// Creates: contract_001.docx, contract_002.docx

// Data‑based naming using fields from records
err = docx.ProcessDocxMultipleRecords("template.docx", "./output", records, "{NAME}_contract.docx")
// Creates: Alice_contract.docx, Bob_contract.docx

// Multiple fields
err = docx.ProcessDocxMultipleRecords("template.docx", "./output", records, "{NAME}_{EMAIL}.docx")
// Creates: Alice_alice@example.com.docx, Bob_bob@example.com.docx

// Combine data and index
err = docx.ProcessDocxMultipleRecords("template.docx", "./output", records, "employee_{INDEX}_{NAME}.docx")
// Creates: employee_1_Alice.docx, employee_2_Bob.docx

Advanced Naming Logic

import (
    "fmt"
    "github.com/siliconcatalyst/officeforge/docx"
)

nameFunc := func(record map[string]string, index int) string {
    // Custom logic based on data
    if record["PRIORITY"] == "high" {
        return fmt.Sprintf("urgent_%s_%d.docx", record["NAME"], index)
    }
    return fmt.Sprintf("standard_%s_%d.docx", record["NAME"], index)
}

err := docx.ProcessDocxMultipleRecordsWithNames("template.docx", "./output", records, nameFunc)
Back to Blog

Related posts

Read more »