package svg import ( "errors" "io" "log" "net/http" "os" "path/filepath" "strings" "golang.org/x/image/font/sfnt" ) type SvgStorage interface { Create(id string, svg io.Reader) (string, error) Get(id string) (io.ReadCloser, error) AddFont(reader io.Reader, format string) error GetFonts() ([]string, error) GetFontsDir() (string, error) CreatePublic(data io.Reader, filetype string) (string, error) GetPublic(path string) (io.ReadCloser, error) GetPublicDir() http.Dir } var _ SvgStorage = FileSvgStorage{} var TempDir string = "" type FileSvgStorage struct { basepath, publicSubPath, fontssubpath string } func NewFileStorage(path, publicSubPath, fontssubpath string) *FileSvgStorage { err := os.MkdirAll(path, 0755) if err != nil { panic(err) } err = os.MkdirAll(filepath.Join(path, fontssubpath), 0755) if err != nil { panic(err) } log.Println("Initialized file storage handler") return &FileSvgStorage{path, publicSubPath, fontssubpath} } func (f FileSvgStorage) Create(id string, svg io.Reader) (string, error) { file, err := os.Create(filepath.Join(f.basepath, id+".svg")) if err != nil { return "", err } defer file.Close() _, err = io.Copy(file, svg) if err != nil { return "", err } log.Println("Created File: ", file.Name()) return file.Name(), nil } 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, f.publicSubPath) if err := os.MkdirAll(path, 0755); err != nil { return "", err } file, err := os.CreateTemp(path, "*."+filetype) if err != nil { return "", err } defer file.Close() _, err = io.Copy(file, data) if err != nil { return "", err } return strings.TrimPrefix(file.Name(), path), nil } func (f FileSvgStorage) GetPublic(path string) (io.ReadCloser, error) { file, err := os.Open(filepath.Join(f.basepath, f.publicSubPath, path)) if err != nil { return nil, err } return file, nil } func (f FileSvgStorage) GetPublicDir() http.Dir { return http.Dir(filepath.Join(f.basepath, f.publicSubPath)) } 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 } func (f FileSvgStorage) AddFont(fontreader io.Reader, format string) error { if format != "ttf" && format != "otf" { return errors.New("Format not supported") } file, err := os.CreateTemp(filepath.Join(f.basepath, f.fontssubpath), "*."+format) if err != nil { return err } defer file.Close() log.Printf("Writing %s font to %s\n", format, file.Name()) _, err = io.Copy(file, fontreader) if err != nil { return err } return nil } func (f FileSvgStorage) GetFonts() ([]string, error) { path := filepath.Join(f.basepath, f.fontssubpath) entries, err := os.ReadDir(path) if err != nil { return nil, err } fonts := make([]string, len(entries)) for i, entry := range entries { if entry.IsDir() { continue } if strings.HasSuffix(entry.Name(), ".ttf") || strings.HasSuffix(entry.Name(), ".otf") { fontblob, err := os.ReadFile(filepath.Join(path, entry.Name())) if err != nil { return nil, err } fontname, err := getFontName(fontblob) if err != nil { return nil, err } fonts[i] = fontname } } log.Printf("Got fonts \n%s\n", strings.Join(fonts, "\n")) return fonts, nil } func (f FileSvgStorage) GetFontsDir() (string, error) { return filepath.Join(f.basepath, f.fontssubpath), nil } func getFontName(svgblob []byte) (string, error) { font, err := sfnt.Parse(svgblob) if err != nil { return "", err } name, err := font.Name(nil, sfnt.NameIDFull) if err != nil { return "", err } return name, nil }