All Downloads are FREE. Search and download functionalities are using the official Maven repository.

izumi.fundamentals.platform.graphs.dotml.Backend.scala Maven / Gradle / Ivy

The newest version!
package izumi.fundamentals.platform.graphs.dotml

import java.io.File

import izumi.fundamentals.graphs.dotml.GraphVizDotML
import izumi.fundamentals.platform.language.Quirks
import izumi.fundamentals.platform.os.{IzOs, OsType}

import scala.language.postfixOps

/**
  * @author Depeng Liang
  *
  * https://github.com/Ldpe2G/Graphviz4S/blob/master/src/main/scala/com/liangdp/graphviz4s/Dot.scala
  */
object Backend {

  /**
    * Save the source to file and render with the Graphviz engine.
    * @param engine The layout commmand used for rendering ('dot', 'neato', ...).
    * @param format The output format used for rendering ('pdf', 'png', ...).
    * @param fileName Name of the DOT source file to render.
    * @param directory Directory to save the Dot source file.
    * @param view Whether open the rendered result with the default application.
    * @param cleanup Whether delete the source file after rendering.
    * @return The (possibly relative) path of the rendered file.
    */
  def render(
    graphVizDotML: GraphVizDotML,
    engine: String = "dot",
    format: String = "pdf",
    fileName: String,
    directory: String,
    view: Boolean = false,
    cleanUp: Boolean = false,
  ): String = {
    val filePath = graphVizDotML.save(fileName, directory)
    val rendered = doRender(engine, format, filePath)
    if (cleanUp) new File(filePath).delete
    if (view) doView(rendered)
    rendered
  }

  /**
    * Save the source to file, open the rendered result in a viewer.
    * @param engine The layout commmand used for rendering ('dot', 'neato', ...).
    * @param format The output format used for rendering ('pdf', 'png', ...).
    * @param fileName Name of the DOT source file to render.
    * @param directory Directory to save the Dot source file.
    * @param cleanup Whether delete the source file after rendering.
    * @return The (possibly relative) path of the rendered file.
    */
  def view(graphVizDotML: GraphVizDotML, engine: String = "dot", format: String = "pdf", fileName: String, directory: String, cleanUp: Boolean = false): String = {
    render(graphVizDotML, engine, format, fileName, directory, view = true, cleanUp)
  }

  // http://www.graphviz.org/cgi-bin/man?dot
  private val ENGINES = Set(
    "dot",
    "neato",
    "twopi",
    "circo",
    "fdp",
    "sfdp",
    "patchwork",
    "osage",
  )

  // http://www.graphviz.org/doc/info/output.html
  private val FORMATS = Set(
    "bmp",
    "canon",
    "dot",
    "gv",
    "xdot",
    "xdot1.2",
    "xdot1.4",
    "cgimage",
    "cmap",
    "eps",
    "exr",
    "fig",
    "gd",
    "gd2",
    "gif",
    "gtk",
    "ico",
    "imap",
    "cmapx",
    "imap_np",
    "cmapx_np",
    "ismap",
    "jp2",
    "jpg",
    "jpeg",
    "jpe",
    "pct",
    "pict",
    "pdf",
    "pic",
    "plain",
    "plain-ext",
    "png",
    "pov",
    "ps",
    "ps2",
    "psd",
    "sgi",
    "svg",
    "svgz",
    "tga",
    "tif",
    "tiff",
    "tk",
    "vml",
    "vmlz",
    "vrml",
    "wbmp",
    "webp",
    "xlib",
    "x11",
  )

  /**
    * Return command for execution and name of the rendered file.
    *
    * @param engine The layout commmand used for rendering ('dot', 'neato', ...).
    * @param format The output format used for rendering ('pdf', 'png', ...).
    * @param filePath The output path of the source file.
    * @return render command to execute.
    * @return rendered file path.
    */
  def command(engine: String, format: String, filePath: String = null): (String, String) = {
    require(ENGINES.contains(engine), s"unknown engine: $engine")
    require(FORMATS.contains(format), s"unknown format: $format")
    Option(filePath) match {
      case Some(path) => (s"$engine -T$format -O $path", s"$path.$format")
      case None => (s"$engine -T$format", null)
    }
  }

  /**
    * Render file with Graphviz engine into format,  return result filename.
    *
    * @param engine The layout commmand used for rendering ('dot', 'neato', ...).
    * @param format The output format used for rendering ('pdf', 'png', ...).
    * @param filepath Path to the DOT source file to render.
    */
  @throws(classOf[RuntimeException])
  def doRender(engine: String = "dot", format: String = "pdf", filePath: String): String = {
    val (args, rendered) = command(engine, format, filePath)
    import sys.process._
    try {
      args !
    } catch {
      case _: Throwable =>
        val errorMsg = s"""failed to execute "$args", """ +
          """"make sure the Graphviz executables are on your systems' path"""
        throw new RuntimeException(errorMsg)
    }
    rendered
  }

  /**
    * Open filepath with its default viewing application (platform-specific).
    * For know only support linux.
    */
  @throws(classOf[RuntimeException])
  def doView(filePath: String): Unit = {
    val command = IzOs.osType match {
      case OsType.Mac =>
        s"open $filePath"

      case OsType.Linux =>
        s"xdg-open $filePath"

      case OsType.Windows =>
        s"start $filePath"

      case OsType.Unknown =>
        throw new IllegalArgumentException(s"Unsupported OS")
    }

    import sys.process._
    try {
      Quirks.discard(command !)
    } catch {
      case _: Throwable =>
        val errorMsg = s"failed to execute $command"
        throw new RuntimeException(errorMsg)
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy