Skip to content

Main

go
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	gotemplatedocx "github.com/JJJJJJack/go-template-docx"
)

const (
	VERSION = "v1.4.6"
)

var USAGE = fmt.Sprintf("\nUsage: %s <file.docx> <values.json> [<values2.json> ...]\n\nThe output files will have the filename of the json file they are generated from (values.docx, values2.docx ...)\n%s", filepath.Base(os.Args[0]),
	`
optional flags:

-h, --help: show this help message and exit

--version: show go-template-docx version and exit

--verbose: enable verbose error messages

-i <image.jpg|png>: load an image from disk
(you can use multiple -i flags, make sure the filenames are unique)

`)

func filenameStartIndex(path string) int {
	start := strings.LastIndex(path, "/")
	if start == -1 {
		start = strings.LastIndex(path, "\\")
	}

	return start
}

func main() {
	if len(os.Args) < 3 {
		fmt.Print(USAGE)
		return
	}

	docxFilepath := os.Args[1]

	jsonFilenames := []string{}
	for _, arg := range os.Args[2:] {
		if !strings.HasSuffix(arg, ".json") {
			break
		}

		jsonFilenames = append(jsonFilenames, arg)
	}

	flags := make(map[string]bool)
	for _, arg := range os.Args {
		if !strings.HasPrefix(arg, "-") {
			continue
		}

		switch arg {
		case "-h", "--help":
			fmt.Print(USAGE)
			return
		case "--version":
			fmt.Println(VERSION)
			return
		case "--verbose", "-v":
			flags["verbose"] = true
		case "-i":
		default:
			fmt.Printf("Unknown flag: %s\n", arg)
			return
		}
	}

	images := make([]string, 0, ((len(os.Args)-3)/2)+1)
	for i, arg := range os.Args {
		if arg != "-i" {
			continue
		}

		if i+1 >= len(os.Args) {
			fmt.Println("Error: -i flag provided but no image file specified")
			return
		}

		images = append(images, os.Args[i+1])
	}

	for _, jsonFilepath := range jsonFilenames {
		jsonFilename := jsonFilepath[filenameStartIndex(jsonFilepath)+1 : strings.LastIndex(jsonFilepath, ".")]
		docxFilename := docxFilepath[filenameStartIndex(docxFilepath)+1 : strings.LastIndex(docxFilepath, ".")]

		if jsonFilename == docxFilename {
			fmt.Printf("The file JSON %s have the same filename as the docx template file, this could result in overwriting the template file, use unique filenames\n", jsonFilepath)
			return
		}

		jsonBytes, err := os.ReadFile(jsonFilepath)
		if err != nil {
			fmt.Println("Error reading JSON file:", err)
			return
		}

		templateValues := any(nil)
		err = json.Unmarshal(jsonBytes, &templateValues)
		if err != nil {
			fmt.Println("Error unmarshalling JSON:", err)
			return
		}

		docxTemplate, err := gotemplatedocx.NewDocxTemplateFromFilename(docxFilepath)
		if err != nil {
			fmt.Println("Error creating template:", err)
			return
		}

		for _, image := range images {
			imageData, err := os.ReadFile(image)
			if err != nil {
				fmt.Println("Error reading image file:", err)
				return
			}

			docxTemplate.Media(image, imageData)
		}

		err = docxTemplate.Apply(templateValues)
		if err != nil {
			if !flags["verbose"] {
				deepestErr := errors.Unwrap(err)
				for deepestErr != nil {
					err = deepestErr
					deepestErr = errors.Unwrap(err)
				}
			}

			fmt.Println("Error applying template:", err)
			return
		}

		start := strings.LastIndex(jsonFilepath, "/")
		if start == -1 {
			start = strings.LastIndex(jsonFilepath, "\\")
		}
		filename := jsonFilepath[start+1:strings.LastIndex(jsonFilepath, ".")] + ".docx"

		err = docxTemplate.Save(filename)
		if err != nil {
			fmt.Println("Error saving docx:", err)
			return
		}
	}
}