dia2kroki

NAME

dia2kroki - convert textual descriptions for various diagram tools to image URL's using the https://kroki.io/ webservice.

kroki4tcl - extended version bundled as package, GUI and terminal application (see below)

CODE

# dia2kroki.tcl - unicode versions see below
proc dia2kroki {text {dia graphviz} {ext svg}} {
    set b64 [string map {+ - / _ = ""}  [binary encode base64 [zlib compress $text]]]
    set uri https://kroki.io//$dia/$ext/$b64
}
# like to know the code of an URL
proc kroki2dia {url} {
    set text [regsub {.+/} $url ""]
    set dia [zlib decompress [binary decode base64 [string map {- + _ /} $text]]]
}

EXAMPLES

dia2kroki

% source dia2kroki.tcl
% puts [dia2kroki "digraph G { A -> B }"]
https://kroki.io//graphviz/svg/eJxLyUwvSizIUHBXqFZwVNC1U3BSqAUAREAFzQ

This URL can be directly embedded into HTML pages or on Markdown code.

Unfortunately as far as I know Tcler's Wiki can't embed external images without an extension like png, svg etc.

So just the link to the image is shown.

https://kroki.io//graphviz/svg/eJxLyUwvSizIUHBXqFZwVNC1U3BSqAUAREAFzQ

To fix this we have to use the inlinehtml syntax:

<<inlinehtml>>
<img src="https://kroki.io//graphviz/svg/eJxLyUwvSizIUHBXqFZwVNC1U3BSqAUAREAFzQ"> </img>.
<<inlinehtml>>

Here the output:

.

Here an example for plantuml: http://www.plantuml.com

% source dia2kroki.tcl
% dia2kroki {
@startuml
Bob -> Alice : hello
@enduml
} plantuml png
https://kroki.io//plantuml/png/eJzjciguSSwqKc3N4XLKT1LQtVNwzMlMTlWwUshIzcnJ53JIzUsBSQIABnMM1A

Here this image, this time a PNG:

kroki2dia

Using kroki2dia you can get the code of an kroki URL back to text:

% kroki2dia https://kroki.io//graphviz/svg/eJxLyUwvSizIUHBXqFZwVNC1U3BSqAUAREAFzQ
digraph G { A -> B }

GUI

Let's build around those two functions a graphical user interface where you can enter your diagram code in a text widget, after file saving the extensions determines the diagram type and the data are send to the server. The produced image is then fetched to our local machine in parallel to the source file and then displayed in a ttk::label below. For tools like Pikchr which can only create SVG images cairosvg is required to convert the image then locally to a PNG image. Here follows the code:

namespace eval ::kroki { }

proc ::kroki::dia2kroki {text {dia graphviz} {ext svg}} {
    set b64 [string map {+ - / _ = ""}  [binary encode base64 [zlib compress $text]]]
    set uri https://kroki.io//$dia/$ext/$b64
}
proc ::kroki::kroki2dia {url} {
    set text [regsub {.+/} $url ""]
    set dia [zlib decompress [binary decode base64 [string map {- + _ /} $text]]]
}

proc ::kroki::gui {{path ""}} {
    package require Tk
    variable txt
    variable img
    variable lastfile
    variable filetypes
    variable maps
    ttk::frame $path.top
    foreach btn [list New Open Save SaveAs Exit] {
        ttk::button "$path.top.[string tolower $btn]" -width 10 -text $btn -command ::kroki::file$btn
        pack "$path.top.[string tolower $btn]" -side left -padx 5 -pady 5
    }
    pack $path.top -side top
    ttk::panedwindow $path.pwd  -orient horizontal
    ttk::frame $path.pwd.frame
    ttk::label $path.pwd.frame.lbl -text "Hello"
    place $path.pwd.frame.lbl -relx 0.5 -rely 0.5 -anchor center
    tk::text $path.pwd.text
    $path.pwd add $path.pwd.text 
    $path.pwd add $path.pwd.frame 
    pack $path.pwd -side top -fill both -expand true
    # variables
    set txt $path.pwd.text 
    set img $path.pwd.frame.lbl
    set lastfile ""
    set filetypes {
        {{BlockDiag Files} {.bdia}      }                
        {{Ditaa     Files} {.ditaa}     }                        
        {{Graphviz Files}  {.dot}       }        
        {{Mermaid  Files}  {.mmd}       }                
        {{Pikchr   Files}  {.pik}       }                
        {{PlantUML Files}  {.puml}      }
        {{SeqDia   Files}  {.sdia}      }                        
        {{All Files}        *           }
    }
    set maps [dict create bdia blockdiag ditaa ditaa dot graphviz puml plantuml \
              mmd mermaid pik pikchr sdia seqdiag]
    # bindings
    bind $txt <Control-s> ::kroki::fileSave
    # styles
    ttk::style layout WLabel [ttk::style layout TLabel]
    ttk::style layout WFrame [ttk::style layout TFrame]    
    ttk::style configure WLabel -background white
    ttk::style configure WFrame -background white    
    $img configure -style WLabel
    $path.pwd.frame configure -style WFrame
}
proc ::kroki::fileNew {} {
    variable txt
    variable lastfile ""
    $txt delete 1.0 end
}
proc ::kroki::fileSave {} {
    variable lastfile
    variable txt
    variable maps
    variable img
    if {$lastfile eq ""} {
        ::kroki::fileSaveAs
        return
    }
    if {$lastfile ne ""} {
        set text [$txt get 1.0 end] 
        set out [open $lastfile w 0600]
        puts $out "$text"
        close $out
        set ext [string range [file extension $lastfile] 1 end]
        if {![dict exists $maps $ext]} {
            tk_messageBox -title "Error!" -icon error -message "Wrong file extension `.$ext`!\nUse .dot, .mmd, .puml or .pik!" -type ok
            return
        }
        if {$ext eq "pik"} {
            if {[auto_execok cairosvg] eq ""} {
                tk_messageBox -title "Error!" -icon error \
                      -message "Error: Pikchr diagrams need cairosvg!\nPlease install: pip3 install cairosvg --user" \
                      -type ok
                return
            }
            set uri [::kroki::dia2kroki $text [dict get $maps $ext] svg]
            puts "fetching $uri"
            exec -ignorestderr wget $uri -O [file rootname $lastfile].svg 2>@1
            exec -ignorestderr cairosvg -f png  -o [file rootname $lastfile].png [file rootname $lastfile].svg 
        } else {
            set uri [::kroki::dia2kroki $text [dict get $maps $ext] png]
            puts "fetching $uri"
            exec -ignorestderr wget $uri -O [file rootname $lastfile].png 2>@1
            
        }
        if {[file exists [file rootname $lastfile].png]} {
            image create photo ::kroki::img -file [file rootname $lastfile].png
            $img configure -image ::kroki::img
        }
        wm title . "kroki-gui [file tail $lastfile]"
    }
}
proc ::kroki::fileSaveAs {} {
    variable filetypes
    variable txt
    variable lastfile
    set types $filetypes
    unset -nocomplain savefile
    set savefile [tk_getSaveFile -filetypes $types]
    if {$savefile != ""} {
        set lastfile $savefile
        ::kroki::fileSave
    }
}
proc ::kroki::fileOpen {{filename ""}} {
    variable txt
    variable lastfile
    variable filetypes
    set types $filetypes
    if {$filename eq ""} {
        set filename [tk_getOpenFile -filetypes $types]
    }
    if {$filename != ""} {
        $txt delete 1.0 end
        if [catch {open $filename r} infh] {
            puts stderr "Cannot open $filename: $infh"
            exit
        } else {
            while {[gets $infh line] >= 0} {
                $txt insert end "$line\n"
            }
            close $infh
        }
        set lastfile $filename
    }
}
proc ::kroki::fileExit {} {
    set answer [tk_messageBox -title "Question!" -message "Really exit ?" -type yesno -icon question]
    if { $answer } {
        exit 0
    } 
}
if {[info exists argv0] && $argv0 eq [info script]} {
    kroki::gui 
    if {[llength $argv] > 0} {
        if {[file exists [lindex $argv 0]]} {
            ::kroki::fileOpen [lindex $argv 0]
            ::kroki::fileSave
        }
    }
}

Here two example images for the running application first editing Pikchr code:

And then, if you prefer more the old ASCII style, you can create as well ditaa images:

kroki-gui-ditaa

UNICODE

Here versions of the two functions which can as well encode Unicode diagrams (not yet completely tested):

proc ::kroki::dia2kroki {text {dia graphviz} {ext svg}} {
    set b64 [string map {+ - / _ = ""}  [binary encode base64 [zlib compress [encoding convertto utf-8 $text]]]]
    set uri https://kroki.io//$dia/$ext/$b64
}
proc ::kroki::kroki2dia {url} {
    set text [regsub {.+/} $url ""]
    set dia [encoding convertfrom utf-8 [zlib decompress [binary decode base64 [string map {- + _ /} $text]]]]
}

Here is a diagram which I encoded that way:

A₀ ---> A₁ ---> A₂ ---> A₃ ---> A₄

|       |       |       |       |            
| f₀    | f₁    | f₂    | f₃    | f₄    
|       |       |       |       |      
v       v       v       v       v      

B₀ ---> B₁ ---> B₂ ---> B₃ ---> B₄

And that is the image which was generated using kroki::dia2kroki:

TODO's

  • convert without GUI kroki.tcl dia.ditaa dia.png
  • display the URL not only in the terminal
  • tls and http package instead of wget
  • some more tools from https://krokio.io

kroki4tcl package

An improved version is available as package on Github.

You can start the GUI application with kroki4tcl.tcl --gui

https://raw.githubusercontent.com/mittelmark/DGTcl/master/lib/kroki4tcl/examples/sample-sbob-gui.png

The application allows you to edit one diagram per file or using the Markdown mode multiple images per one file. Just switch to the code chunk you would like to preview with your insert cursor and press Ctrl-s to save the file. Here an example:

https://raw.githubusercontent.com/mittelmark/DGTcl/master/lib/kroki4tcl/examples/sample-markdown-gui.png

That way you can collect multiple graphics in one file.

Here the links to start (click on the help button to start):

Please note: This is a WIP.


DISCUSSION

DDG - 2022-02-18: The main advantage of this little function is that you can create these images without having all these diagram tools installed just using basic Tcl (8.6). You can embed the images directly into your documentation using a simple hyperlink or you can download the images using Tcl with the http and tls libraries or a tool like wget. In the afternoon I added as well a little GUI which needs a running Tcl and the wget application. For pikchr graphics as well cairosvg is required to convert from SVG to PNG.

DDG - 2022-02-21: Adding Unicode version which needs probably more testing.

DDG - 2022-02-23: Extended version as package kroki4tcl. GUI allows you to edit several images in one Markdown file within separate code blocks.