package drawio import ( "flag" "fmt" "io" "os" "strconv" ) // Интерфейс параметра экспортёра диаграмм type Option interface { apply(opts *Options) // Применить параметр к хранилищу параметров } // Ошибка "неподдерживаемый формат" type UnsupportedFormatError struct { Format string } // Описание ошибки func (e UnsupportedFormatError) Error() string { return fmt.Sprintf("unsupported format: '%s'", e.Format) } // Проверка эквивалентности ошибок func (e UnsupportedFormatError) Is(target error) bool { if t, ok := target.(UnsupportedFormatError); ok { return t.Format == e.Format } return false } var _ error = (*UnsupportedFormatError)(nil) // Проверка реализации интерфейса type Format string // Формат экспортированного файла const ( PDF Format = "pdf" PNG Format = "png" JPG Format = "jpg" SVG Format = "svg" VSDX Format = "vsdx" XML Format = "xml" ) // Строковое представление формата func (f Format) String() string { return string(f) } // Загрузка формата из строки func (f *Format) Set(str string) error { for _, fmt := range SupportedFormats() { if str == string(fmt) { *f = Format(str) return nil } } return &UnsupportedFormatError{str} } // Список всех поддерживаемых форматов экспорта func SupportedFormats() []Format { return []Format{"pdf", "png", "jpg", "svg", "vsdx", "xml"} } var _ flag.Value = (*Format)(nil) // Поверка реализации интерфейса // Расширение файла заданного формата func (f Format) ext() string { return "." + string(f) } // Параметры экспортёра диаграмм type Options struct { Application string // Путь к приложению drawio EnableXvfb bool // Запускать drawio внутри xvfb-run Output string // Путь к папке с экспортированными файлами Format Format // Формат экспортированных файлов Recursive bool // Рекурсивно сканировать вложенные папки с файлами RemovePageSuffix bool // Удалять суффикс страницы, если это возможно Quality uint // Качество экспортированного изображения (только для JPEG) Transparent bool // Прозрачный фона для PNG // Включать копию диаграммы в экспортированный файл для PDF, PNG и SVG EmbedDiagram bool EmbedSvgImages bool // Встраивать изображения в файл формата SVG Border uint // Ширина рамки вокруг диаграмм Scale uint // Масштаб в процентах размера экспортированных диаграмм Width uint // Ширина экспортированной диаграммы с сохранением масштаба Height uint // Высота экспортированной диаграммы с сохранением масштаба Crop bool // Обрезать результирующий PDF до размера диаграммы Uncompressed bool // Выводить несжатый XML EnablePlugins bool // Включить подключаемые модули openFile OpenFileFunc // Открыть файл readDir ReadDirFunc // Прочитать папку на диске } // Функция открытия файла type OpenFileFunc func(string) (io.ReadCloser, error) // Функция чтения папки на диске type ReadDirFunc func(string) ([]os.DirEntry, error) // Путь к приложению drawio func (opts Options) App() string { if opts.EnableXvfb { return "xvfb-run" } if len(opts.Application) == 0 { return "drawio" } return opts.Application } // Путь к папке с экспортированными файлами func (opts Options) OutDir() string { out := opts.Output if len(out) == 0 { out = "export" } return out } // Расширение экспортированных файлов func (opts Options) OutExt() string { fmt := opts.Format if len(fmt) == 0 { fmt = Format("pdf") } return fmt.ext() } // Аргументы командной строки drawio func (opts Options) Args() []string { args := []string{} if opts.EnableXvfb { var app string if len(opts.Application) == 0 { app = "drawio" } else { app = opts.Application } args = append(args, "-a", "-l", app) } if len(opts.Format) > 0 { args = append(args, "--format", string(opts.Format)) } if opts.Quality != 0 { args = append(args, "--quality", strconv.Itoa(int(opts.Quality))) } if opts.Transparent { args = append(args, "--transparent") } if opts.EmbedDiagram { args = append(args, "--embed-diagram") } if opts.EmbedSvgImages { args = append(args, "--embed-svg-images") } if opts.Border != 0 { args = append(args, "--border", strconv.Itoa(int(opts.Border))) } if opts.Scale != 0 { args = append(args, "--scale", strconv.Itoa(int(opts.Scale))) } if opts.Width != 0 { args = append(args, "--width", strconv.Itoa(int(opts.Width))) } if opts.Height != 0 { args = append(args, "--height", strconv.Itoa(int(opts.Height))) } if opts.Crop { args = append(args, "--crop") } if opts.Uncompressed { args = append(args, "--uncompressed") } if opts.EnablePlugins { args = append(args, "--enable-plugins") } return args } // Путь к приложению drawio func WithAppPath(path string) optionApplication { return optionApplication(path) } type optionApplication string var _ Option = (*optionApplication)(nil) // Проверка реализации интерфейса func (opt optionApplication) apply(opts *Options) { opts.Application = string(opt) } // Запускать drawio внутри xvfb-run func WithXvfb() optionXvfb { return optionXvfb{} } type optionXvfb struct{} var _ Option = optionXvfb{} func (opt optionXvfb) apply(opts *Options) { opts.EnableXvfb = true } // Путь к папке с экспортированными файлами func WithOutput(path string) optionOutput { return optionOutput(path) } type optionOutput string var _ Option = (*optionOutput)(nil) // Проверка реализации интерфейса func (opt optionOutput) apply(opts *Options) { opts.Output = string(opt) } // Формат экспортированных файлов func WithFormat(format Format) Format { return format } var _ Option = (*Format)(nil) // Проверка реализации интерфейса func (f Format) apply(opts *Options) { opts.Format = f } // Рекурсивно сканировать вложенные папки с файлами func WithRecursive() optionRecursive { return optionRecursive{} } type optionRecursive struct{} var _ Option = optionRecursive{} // Проверка реализации интерфейса func (opt optionRecursive) apply(opts *Options) { opts.Recursive = true } // Удалять суффикс страницы, если это возможно func WithRemovePageSuffix() optionRemovePageSuffix { return optionRemovePageSuffix{} } type optionRemovePageSuffix struct{} var _ Option = optionRemovePageSuffix{} // Проверка реализации интерфейса func (opt optionRemovePageSuffix) apply(opts *Options) { opts.RemovePageSuffix = true } // Качество экспортированного изображения (только для JPEG) func WithQuality(q uint) optionQuality { return optionQuality(q) } type optionQuality uint var _ Option = (*optionQuality)(nil) // Проверка реализации интерфейса func (opt optionQuality) apply(opts *Options) { opts.Quality = uint(opt) } // Прозрачный фона для PNG func WithTransparent() optionTransparent { return optionTransparent{} } type optionTransparent struct{} var _ Option = optionTransparent{} // Проверка реализации интерфейса func (opt optionTransparent) apply(opts *Options) { opts.Transparent = true } // Включать копию диаграммы в экспортированный файл для PDF, PNG и SVG func WithEmbedDiagram() optionEmbedDiagram { return optionEmbedDiagram{} } type optionEmbedDiagram struct{} var _ Option = optionEmbedDiagram{} // Проверка реализации интерфейса func (opt optionEmbedDiagram) apply(opts *Options) { opts.EmbedDiagram = true } // Встраивать изображения в файл формата SVG func WithEmbedSvgImages() optionEmbedSvgImages { return optionEmbedSvgImages{} } type optionEmbedSvgImages struct{} var _ Option = optionEmbedSvgImages{} // Проверка реализации интерфейса func (opt optionEmbedSvgImages) apply(opts *Options) { opts.EmbedSvgImages = true } // Ширина рамки вокруг диаграмм func WithBorder(border uint) optionBorder { return optionBorder(border) } type optionBorder uint var _ Option = (*optionBorder)(nil) // Проверка реализации интерфейса func (opt optionBorder) apply(opts *Options) { opts.Border = uint(opt) } // Масштаб в процентах размера экспортированных диаграмм func WithScale(scale uint) optionScale { return optionScale(scale) } type optionScale uint var _ Option = (*optionScale)(nil) // Проверка реализации интерфейса func (opt optionScale) apply(opts *Options) { opts.Scale = uint(opt) } // Ширина экспортированной диаграммы с сохранением масштаба func WithWidth(width uint) optionWidth { return optionWidth(width) } type optionWidth uint var _ Option = (*optionWidth)(nil) // Проверка реализации интерфейса func (opt optionWidth) apply(opts *Options) { opts.Width = uint(opt) } // Высота экспортированной диаграммы с сохранением масштаба func WithHeight(height uint) optionHeight { return optionHeight(height) } type optionHeight uint var _ Option = (*optionHeight)(nil) // Проверка реализации интерфейса func (opt optionHeight) apply(opts *Options) { opts.Height = uint(opt) } // Обрезать результирующий PDF до размера диаграммы func WithCrop() optionCrop { return optionCrop{} } type optionCrop struct{} var _ Option = optionCrop{} // Проверка реализации интерфейса func (opt optionCrop) apply(opts *Options) { opts.Crop = true } // Выводить несжатый XML func WithUncompressed() optionUncompressed { return optionUncompressed{} } type optionUncompressed struct{} var _ Option = optionUncompressed{} // Проверка реализации интерфейса func (opt optionUncompressed) apply(opts *Options) { opts.Uncompressed = true } // Включить подключаемые модули func WithEnablePlugins() optionEnablePlugins { return optionEnablePlugins{} } type optionEnablePlugins struct{} var _ Option = optionEnablePlugins{} // Проверка реализации интерфейса func (opt optionEnablePlugins) apply(opts *Options) { opts.EnablePlugins = true } // Открыть файл func WithOpenFile(openFile OpenFileFunc) optionOpenFile { return optionOpenFile{openFile} } type optionOpenFile struct { openFile OpenFileFunc } var _ Option = optionOpenFile{} // Проверка реализации интерфейса func (opt optionOpenFile) apply(opts *Options) { opts.openFile = opt.openFile } // Прочитать папку на диске func WithReadDir(readDir ReadDirFunc) optionReadDir { return optionReadDir{readDir} } type optionReadDir struct { readDir ReadDirFunc } var _ Option = optionReadDir{} // Проверка реализации интерфейса func (opt optionReadDir) apply(opts *Options) { opts.readDir = opt.readDir } // Пустой параметр func WithNop() optionNop { return optionNop{} } type optionNop struct{} var _ Option = optionNop{} func (opt optionNop) apply(_ *Options) { // Не требуется действий, так как это пустой параметр }