package drawio import ( "bufio" "errors" "fmt" "io" "io/fs" "os" "os/exec" "path" "strconv" "strings" xmlparser "github.com/tamerh/xml-stream-parser" ) // Создать нового экспортёра диаграмм func New(opt ...Option) Exporter { options := &Options{ Application: "drawio", Output: "export", Format: Format("pdf"), } for _, opt := range opt { opt.apply(options) } return NewWithOptions(options) } // Создать нового экспортёра с параметрами func NewWithOptions(opts *Options) Exporter { return Exporter{opts: opts} } // Экспортёр диаграмм type Exporter struct { opts *Options } // Экспорт диаграмм из указанный файлов или папок func (exp Exporter) Export(fileOrDir ...string) error { var ( err error errs = []error{} args = exp.opts.Args() info fs.FileInfo ) for _, item := range fileOrDir { if info, err = os.Stat(item); err != nil { errs = append(errs, err) continue } if info.IsDir() { err = exp.exportDir(item, args) } else { err = exp.exportFile(item, args) } if err != nil { errs = append(errs, err) } } return errors.Join(errs...) } // Разбирает данные и возвращает срез имён диаграмм в них func Diagrams(reader io.Reader) ([]string, error) { var ( result = []string{} br = bufio.NewReader(reader) parser = xmlparser.NewXMLParser( br, "mxfile", "diagram", ).ParseAttributesOnly("diagram") ) for xml := range parser.Stream() { if xml.Err != nil { return result, xml.Err } if items, ok := xml.Childs["diagram"]; ok { for _, item := range items { result = append(result, item.Attrs["name"]) } } } return result, nil } // Экспорт диаграмм из всех файлов в папке func (exp Exporter) exportDir(dirPath string, args []string, subDir ...string) error { var ( entries []fs.DirEntry err error errs []error ) if entries, err = os.ReadDir(dirPath); err != nil { return err } for _, entry := range entries { name := entry.Name() outDir := make([]string, len(subDir), len(subDir)+1) outDir = append(outDir, name) if entry.IsDir() && exp.opts.Recursive { err = exp.exportDir(path.Join(dirPath, name), args, outDir...) } else { err = exp.exportFile(path.Join(dirPath, name), args, outDir...) } if err != nil { errs = append(errs, err) } } return errors.Join(errs...) } // Экспорт файла с диаграммами func (exp Exporter) exportFile(filePath string, args []string, subDir ...string) error { var ( file *os.File diagrams []string err error ) if file, err = os.Open(filePath); err != nil { return err } defer file.Close() if diagrams, err = Diagrams(file); err != nil { return err } outDirs := make([]string, 1, len(subDir)+1) outDirs[0] = exp.opts.Output outDirs = append(outDirs, subDir...) output := path.Join(outDirs...) errs := []error{} for i, name := range diagrams { outName := strings.TrimSuffix(path.Base(filePath), path.Ext(filePath)) if !exp.opts.RemovePageSuffix || len(diagrams) > 1 { outName += "-" + name } outName += exp.opts.OutExt() drawioArgs := make([]string, len(args), len(args)+4) copy(drawioArgs, args) drawioArgs = append(drawioArgs, "--page-index", strconv.Itoa(i), "--output", path.Join(output, outName), "--export", filePath, ) cmd := exec.Command(exp.opts.App(), drawioArgs...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr fmt.Println("Run command:", cmd.Path, cmd.Args) if err = cmd.Run(); err != nil { errs = append(errs, err) } } return errors.Join(errs...) }