diff --git a/cmd/svg-templater/main.go b/cmd/svg-templater/main.go index b6de69e..0eec781 100644 --- a/cmd/svg-templater/main.go +++ b/cmd/svg-templater/main.go @@ -5,9 +5,13 @@ import ( "tomatentum.net/svg-templater/internal/command" "tomatentum.net/svg-templater/internal/database" + "tomatentum.net/svg-templater/pkg/format" ) func main() { + if !format.CheckInkscape() { + panic("Inkscape not found") + } if err := database.OpenSQLite(); err != nil { log.Fatal("Failed opening DB:\n", err) return diff --git a/pkg/format/formatconverter.go b/pkg/format/formatconverter.go new file mode 100644 index 0000000..d497b8e --- /dev/null +++ b/pkg/format/formatconverter.go @@ -0,0 +1,97 @@ +package format + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + + "tomatentum.net/svg-templater/pkg/svg" +) + +type ConversionParameters struct { + Format string + Width, Height int +} + +func ConvertByte(svgblob []byte, param ConversionParameters) ([]byte, error) { + reader, err := ConvertReader(svgblob, param) + if err != nil { + return nil, err + } + defer reader.Close() + + result, err := io.ReadAll(reader) + if err != nil { + return nil, err + } + return result, nil +} + +func ConvertReader(svgblob []byte, param ConversionParameters) (io.ReadCloser, error) { + if param.Format == "svg" { + return io.NopCloser(bytes.NewReader(svgblob)), nil + } + file, err := svg.CreateTemp(bytes.NewReader(svgblob), "svg") + if err != nil { + return nil, err + } + defer os.Remove(file) + + return runCommand(file, param) +} + +func runCommand(input string, param ConversionParameters) (io.ReadCloser, error) { + var args []string + + var outExt string + switch param.Format { + case "png": + args = append(args, "--export-type=png") + outExt = ".png" + case "jpg": + args = append(args, "--export-type=png") // inkscape doesn't export jpg directly + outExt = ".png" + case "pdf": + args = append(args, "--export-type=pdf") + outExt = ".pdf" + default: + return nil, fmt.Errorf("format not supported: %s", param.Format) + } + + if param.Width > 0 { + args = append(args, "-w", strconv.Itoa(param.Width)) + } + if param.Height > 0 { + args = append(args, "-h", strconv.Itoa(param.Height)) + } + + outFile := strings.TrimSuffix(input, filepath.Ext(input)) + outExt + args = append(args, "-o", outFile) + args = append(args, input) + + cmd := exec.Command("/opt/homebrew/bin/inkscape", args...) + cmd.Dir = svg.TempDir + + out, err := cmd.CombinedOutput() + + if err != nil { + return nil, fmt.Errorf("inkscape failed: %w: %s", err, string(out)) + } + + file, err := os.Open(outFile) + if err != nil { + return nil, err + } + + return file, nil +} + +func CheckInkscape() bool { + _, err := exec.LookPath("inkscape") + return err == nil +} diff --git a/pkg/svg/storage.go b/pkg/svg/storage.go index b3349f7..45d3e7b 100644 --- a/pkg/svg/storage.go +++ b/pkg/svg/storage.go @@ -9,47 +9,18 @@ import ( type SvgStorage interface { Create(id string, svg io.Reader) (string, error) - Get(id string) (io.Reader, error) - CreateTemp(data io.Reader, filetype string) (string, error) + Get(id string) (io.ReadCloser, error) CreatePublic(data io.Reader, filetype string) (string, error) + GetPublic(path string) (io.ReadCloser, error) } var _ SvgStorage = FileSvgStorage{} +var TempDir string = "" type FileSvgStorage struct { basepath string } -func (f FileSvgStorage) CreatePublic(data io.Reader, filetype string) (string, error) { - path := filepath.Join(f.basepath, "public") - if err := os.Mkdir(path, 0755); err != nil { - return "", err - } - - file, err := os.CreateTemp(path, "*."+filetype) - if err != nil { - return "", err - } - defer file.Close() - - return file.Name(), nil -} - -func (f FileSvgStorage) CreateTemp(data io.Reader, filetype string) (string, error) { - path := filepath.Join(f.basepath, "temp") - if err := os.Mkdir(path, 0755); err != nil { - return "", err - } - - file, err := os.CreateTemp(path, "*."+filetype) - if err != nil { - return "", err - } - defer file.Close() - - return file.Name(), nil -} - func NewFileStorage(path string) *FileSvgStorage { err := os.MkdirAll(path, 0755) if err != nil { @@ -76,10 +47,47 @@ func (f FileSvgStorage) Create(id string, svg io.Reader) (string, error) { return file.Name(), nil } -func (f FileSvgStorage) Get(id string) (io.Reader, error) { - file, err := os.Open(filepath.Join(f.basepath, "svg", id+".svg")) +func (f FileSvgStorage) Get(id string) (io.ReadCloser, error) { + file, err := os.Open(filepath.Join(f.basepath, id+".svg")) if err != nil { return nil, err } return file, nil } + +func (f FileSvgStorage) CreatePublic(data io.Reader, filetype string) (string, error) { + path := filepath.Join(f.basepath, "public") + if err := os.Mkdir(path, 0755); err != nil { + return "", err + } + + file, err := os.CreateTemp(path, "*."+filetype) + if err != nil { + return "", err + } + defer file.Close() + + return file.Name(), nil +} + +func (f FileSvgStorage) GetPublic(path string) (io.ReadCloser, error) { + file, err := os.Open(filepath.Join(f.basepath, "public", path)) + if err != nil { + return nil, err + } + return file, nil +} + +func CreateTemp(data io.Reader, filetype string) (string, error) { + file, err := os.CreateTemp(TempDir, "*."+filetype) + if err != nil { + return "", err + } + defer file.Close() + _, err = io.Copy(file, data) + if err != nil { + return "", err + } + + return file.Name(), nil +}