// Package exporter provides advanced export functionality package exporter import ( "fmt" "os/exec" "runtime" "mermaid-go/pkg/ast" ) // AdvancedExporter provides high-quality export using external tools type AdvancedExporter struct { svgExporter *SVGExporter } // NewAdvancedExporter creates a new advanced exporter func NewAdvancedExporter() *AdvancedExporter { return &AdvancedExporter{ svgExporter: NewSVGExporter(), } } // ExportWithExternalTool exports using external tools for better quality func (e *AdvancedExporter) ExportWithExternalTool(diagram ast.Diagram, outputPath string, options *ExportOptions) error { if options == nil { options = DefaultExportOptions() } // First export to SVG e.svgExporter.SetSize(options.Width, options.Height) svgContent, err := e.svgExporter.ExportToSVG(diagram) if err != nil { return fmt.Errorf("failed to generate SVG: %w", err) } // If target is SVG, we're done if options.Format == FormatSVG { return writeFile(outputPath, []byte(svgContent)) } // For PNG/JPEG, try to use external tools return e.convertSVGToRaster(svgContent, outputPath, options) } // convertSVGToRaster converts SVG to raster format using external tools func (e *AdvancedExporter) convertSVGToRaster(svgContent, outputPath string, options *ExportOptions) error { // Try different conversion methods in order of preference converters := []func(string, string, *ExportOptions) error{ e.convertWithInkscape, e.convertWithImageMagick, e.convertWithRSVG, e.convertWithChrome, } var lastErr error for _, converter := range converters { if err := converter(svgContent, outputPath, options); err == nil { return nil } else { lastErr = err } } return fmt.Errorf("all conversion methods failed, last error: %w", lastErr) } // convertWithInkscape uses Inkscape for conversion func (e *AdvancedExporter) convertWithInkscape(svgContent, outputPath string, options *ExportOptions) error { if !e.isCommandAvailable("inkscape") { return fmt.Errorf("inkscape not available") } // Create temporary SVG file tempSVG := outputPath + ".temp.svg" if err := writeFile(tempSVG, []byte(svgContent)); err != nil { return err } defer removeFile(tempSVG) // Convert using Inkscape args := []string{ "--export-type=" + string(options.Format), "--export-width=" + fmt.Sprintf("%d", options.Width), "--export-height=" + fmt.Sprintf("%d", options.Height), "--export-dpi=" + fmt.Sprintf("%d", options.DPI), "--export-filename=" + outputPath, tempSVG, } cmd := exec.Command("inkscape", args...) return cmd.Run() } // convertWithImageMagick uses ImageMagick for conversion func (e *AdvancedExporter) convertWithImageMagick(svgContent, outputPath string, options *ExportOptions) error { if !e.isCommandAvailable("convert") { return fmt.Errorf("imagemagick not available") } tempSVG := outputPath + ".temp.svg" if err := writeFile(tempSVG, []byte(svgContent)); err != nil { return err } defer removeFile(tempSVG) args := []string{ "-density", fmt.Sprintf("%d", options.DPI), "-resize", fmt.Sprintf("%dx%d", options.Width, options.Height), tempSVG, outputPath, } cmd := exec.Command("convert", args...) return cmd.Run() } // convertWithRSVG uses rsvg-convert for conversion func (e *AdvancedExporter) convertWithRSVG(svgContent, outputPath string, options *ExportOptions) error { if !e.isCommandAvailable("rsvg-convert") { return fmt.Errorf("rsvg-convert not available") } tempSVG := outputPath + ".temp.svg" if err := writeFile(tempSVG, []byte(svgContent)); err != nil { return err } defer removeFile(tempSVG) format := "png" args := []string{ "--format=" + format, "--width=" + fmt.Sprintf("%d", options.Width), "--height=" + fmt.Sprintf("%d", options.Height), "--dpi-x=" + fmt.Sprintf("%d", options.DPI), "--dpi-y=" + fmt.Sprintf("%d", options.DPI), "--output=" + outputPath, tempSVG, } cmd := exec.Command("rsvg-convert", args...) return cmd.Run() } // convertWithChrome uses headless Chrome for conversion func (e *AdvancedExporter) convertWithChrome(svgContent, outputPath string, options *ExportOptions) error { chromePath := e.findChrome() if chromePath == "" { return fmt.Errorf("chrome not available") } // Create HTML wrapper html := fmt.Sprintf(` %s `, options.Width, options.Height, svgContent) tempHTML := outputPath + ".temp.html" if err := writeFile(tempHTML, []byte(html)); err != nil { return err } defer removeFile(tempHTML) args := []string{ "--headless", "--disable-gpu", "--no-sandbox", "--window-size=" + fmt.Sprintf("%d,%d", options.Width, options.Height), "--screenshot=" + outputPath, "file://" + tempHTML, } cmd := exec.Command(chromePath, args...) return cmd.Run() } // isCommandAvailable checks if a command is available in PATH func (e *AdvancedExporter) isCommandAvailable(command string) bool { _, err := exec.LookPath(command) return err == nil } // findChrome finds Chrome/Chromium executable func (e *AdvancedExporter) findChrome() string { candidates := []string{ "google-chrome", "google-chrome-stable", "chromium", "chromium-browser", } if runtime.GOOS == "darwin" { candidates = append(candidates, "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome") } else if runtime.GOOS == "windows" { candidates = append(candidates, "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", ) } for _, candidate := range candidates { if path, err := exec.LookPath(candidate); err == nil { return path } } return "" } // GetAvailableConverters returns list of available conversion tools func (e *AdvancedExporter) GetAvailableConverters() []string { var available []string tools := map[string]string{ "Inkscape": "inkscape", "ImageMagick": "convert", "rsvg-convert": "rsvg-convert", "Chrome": "", } for name, command := range tools { if name == "Chrome" { if e.findChrome() != "" { available = append(available, name) } } else if e.isCommandAvailable(command) { available = append(available, name) } } return available } // Helper functions func writeFile(path string, data []byte) error { // Implementation would write file return nil } func removeFile(path string) { // Implementation would remove file } // InstallationGuide provides installation instructions for external tools func (e *AdvancedExporter) InstallationGuide() map[string]string { return map[string]string{ "Inkscape": ` macOS: brew install inkscape Ubuntu/Debian: sudo apt-get install inkscape Windows: Download from https://inkscape.org/`, "ImageMagick": ` macOS: brew install imagemagick Ubuntu/Debian: sudo apt-get install imagemagick Windows: Download from https://imagemagick.org/`, "rsvg-convert": ` macOS: brew install librsvg Ubuntu/Debian: sudo apt-get install librsvg2-bin Windows: Part of GTK+ runtime`, "Chrome": ` macOS: Download from https://www.google.com/chrome/ Ubuntu/Debian: sudo apt-get install google-chrome-stable Windows: Download from https://www.google.com/chrome/`, } }