canvas2mvg

Die Prozedur canvas2mvg erzeugt aus dem Inhalt des Canvas-Elements einen String für ImageMagick-Vektorgrafik. Wenn Sie diesen in eine Datei mit Endung .mvg speichern, kann ImageMagick dies in ein anders Dateiformat umwandeln, etwa .jpg oder .gif.


proc canvas2mvg canvas {
  set col2hex {color {
      if {[winfo exists $color] && [winfo class $color] eq "Canvas"} then {
        set color [$color cget -bg]
      }
      if {$color eq ""} then {
        set result none
      } else {
        set result #
        foreach x [winfo rgb . $color] {
          append result [format %02x [expr {int($x / 256)}]]
        }
      }
      set result
    }}
  set splinecoords2mvg {{coords {canBeClosed yes}} {
      set closed [expr {$canBeClosed &&
                        [lindex $coords 0] == [lindex $coords end-1] &&
                        [lindex $coords 1] == [lindex $coords end]}]
      if {$closed} then {
        lassign [lrange $coords end-3 end] x0 y0 x1 y1
        set x [expr {($x0+$x1)/2.0}]
        set y [expr {($y0+$y1)/2.0}]
        lset coords end-1 $x
        lset coords end $y
        set coords [concat $x $y $coords]
      }
      if {[llength $coords] == 6} then {
        lreplace $coords 2 1 Q
      } else {
        lappend result {*}[lrange $coords 0 1]
        set co1 [lrange $coords 2 end-4]
        set co2 [lrange $coords 4 end-2]
        foreach {x1 y1} $co1 {x2 y2} $co2 {
          lappend result $x1 $y1 [expr {($x1+$x2)/2.0}] [expr {($y1+$y2)/2.0}]
        }
        lappend result {*}[lrange $coords end-3 end]
        lreplace $result 2 1 Q
      }
    }}
  array set mode {fill "" 
                  stroke "" 
                  strokewidth "" 
                  joinstyle "" 
                  capstyle ""
                  fontfamily "" 
                  fontsize ""}
  lappend result [list viewbox 0 0 [winfo width $canvas] [winfo height $canvas]]\
    [list stroke none]\
    [list fill [apply $col2hex $canvas]]\
    [list rectangle 0 0 [winfo width $canvas] [winfo height $canvas]]
  foreach item [$canvas find all] {
    set type [$canvas type $item]
    lappend result "# $type ... [$canvas gettags $item]"
    # outline width
    if {$type in {polygon oval arc rectangle line}} then {
      set width [$canvas itemcget $item -width]
      if {$width != $mode(strokewidth)} then {
        set mode(strokewidth) $width
        lappend result [list stroke-width $width]
      }
    }
    # fill, stroke
    if {$type in {polygon oval arc rectangle}} then {
      set fill [apply $col2hex [$canvas itemcget $item -fill]]
      if {$mode(fill) ne $fill} then {
        set mode(fill) $fill
        lappend result [list fill $fill]
      }
      set stroke [apply $col2hex [$canvas itemcget $item -outline]]
      if {$mode(stroke) ne $stroke} then {
        set mode(stroke) $stroke
        lappend result [list stroke $stroke]
      }
    }
    # joinstyle
    if {$type in {polygon line}} then {
      set joinstyle [$canvas itemcget $item -joinstyle]
      if {$mode(joinstyle) ne $joinstyle} then {
        set mode(joinstyle) $joinstyle
        lappend result [list stroke-linejoin $joinstyle]
      }
    }
    # line color, capstyle
    if {$type in {line}} then {
      if {$mode(fill) ne "none"} then {
        set mode(fill) none
        lappend result [list fill none]
      }
      set stroke [apply $col2hex [$canvas itemcget $item -fill]]
      if {$mode(stroke) ne $stroke} then {
        set mode(stroke) $stroke
        lappend result [list stroke $stroke]
      }
      set capstyle [dict get {butt butt projecting square round round}\
                      [$canvas itemcget $item -capstyle]]
      if {$mode(capstyle) ne $capstyle} then {
        set mode(capstyle) $capstyle
        lappend result [list stroke-linecap $capstyle]
      }
    }
    # text color, font, size
    if {$type in {text}} then {
      if {$mode(stroke) ne "none"} then {
        set mode(stroke) none
        lappend result [list stroke none]
      }
      set fill [apply $col2hex [$canvas itemcget $item -fill]]
      if {$mode(fill) ne $fill} then {
        set mode(fill) $fill
        lappend result [list fill $fill]
      }
      set font [$canvas itemcget $item -font]
      # font-family, font-size
      if {$font in font names} then {
        set fontsize [font configure $font -size]
        set fontfamily [font configure $font -family]
      } else {
        if {[llength $font] == 1} then {
          set fontsize 12
        } else {
          set fontsize [lindex $font 1]
        }
        set fontfamily [lindex $font 0]
      }
      if {$fontsize < 0} then {
        set fontsize [expr {int(-$fontsize / [tk scaling])}]
      }
      if {$mode(fontsize) ne $fontsize} then {
        set mode(fontsize) $fontsize
        lappend result [list font-size $fontsize]
      }
      #
      # Attention! In some cases, IM assumes 72dpi,
      # where 90dpi is necessary.
      # Then, on cmd line, use switch -density as follows:
      # convert -density 90 test.mvg test.png
      #
      if {$mode(fontfamily) ne $fontfamily} then {
        set mode(fontfamily) $fontfamily
        lappend result [list font $fontfamily]
      }
      #
      # Attention! Care that IM has access to fonts.
      # If not, an error msg is shown,
      # then the default font is used silently.
      #
    }
    set line {}
    set coords [$canvas coords $item]
    switch -exact -- $type {
      line {
        # start of path
        lappend line path 'M
        set smooth [$canvas itemcget $item -smooth]
        if {[string is true -strict $smooth]} then {
          if {[$canvas itemcget $item -arrow] eq "none"} then {
            lappend line {*}[apply $splinecoords2mvg $coords]
          } else {
            lappend line {*}[apply $splinecoords2mvg $coords false]
          }
        } elseif {[string is false -strict $smooth]} then {
          lappend line {*}[lrange $coords 0 1] L {*}[lrange $coords 2 end]
        } else {
          lappend line {*}[lrange $coords 0 1] C {*}[lrange $coords 2 end]
        }
        append line '
        lappend result $line
      }
      polygon {
        lappend line path 'M
        set smooth [$canvas itemcget $item -smooth]
        if {[string is false -strict $smooth]} then {
          lassign $coords x0 y0
          lassign [lrange $coords end-1 end] x1 y1
          set x [expr {($x0+$x1)/2.0}]
          set y [expr {($y0+$y1)/2.0}]
          lappend line $x $y L {*}$coords $x $y Z
        } elseif {[string is true -strict $smooth]} then {
          if {[lindex $coords 0] != [lindex $coords end-1] ||
              [lindex $coords 1] != [lindex $coords end]} then {
            lappend coords {*}[lrange $coords 0 1]
          }
          lappend line {*}[apply $splinecoords2mvg $coords]
        } else {
          lappend line {*}[lrange $coords 0 1] C {*}[lrange $coords 2 end]
        }
        append line '
        lappend result $line
      }
      oval {
        lassign $coords x0 y0 x1 y1
        lappend line ellipse [expr {($x0+$x1)/2.0}] [expr {($y0+$y1)/2.0}]\
          [expr {$x1-($x0+$x1)/2.0}] [expr {$y1-($y0+$y1)/2.0}] 0 360
        lappend result $line
      }
      arc {
        lappend result [list push graphic-context]
        lappend result [list stroke-linejoin miter]
        # lappend result [list stroke-linejoin bevel]
        # lappend result [list stroke-linejoin round]
        lappend line path 'M
        lassign $coords x0 y0 x1 y1
        set rx [expr {($x1-$x0)/2.0}]
        set ry [expr {($y1-$y0)/2.0}]
        set x [expr {($x0+$x1)/2.0}]
        set y [expr {($y0+$y1)/2.0}]
        set f [expr {acos(0)/90}]
        set start [$canvas itemcget $item -start]
        set startx [expr {cos($start*$f)*$rx+$x}]
        set starty [expr {sin(-$start*$f)*$ry+$y}]
        set angle [expr {$start+[$canvas itemcget $item -extent]}]
        set endx [expr {cos($angle*$f)*$rx+$x}]
        set endy [expr {sin(-$angle*$f)*$ry+$y}]
        # start point
        lappend line\
          [expr {($startx+$x)/2.0}] [expr {($starty+$y)/2.0}]\
          $startx $starty
        lappend line A
        # radiusx, radiusy
        lappend line $rx $ry
        # angle -- always 0
        lappend line 0
        # "big" or "small"?
        lappend line [expr {$angle-$start > 180}]
        # right side (always)
        lappend line 0
        # end point
        lappend line $endx $endy
        # close path
        lappend line L $x $y Z
        append line '
        lappend result $line
        lappend result [list pop graphic-context]
      }
      rectangle {
        lappend result\
          [list push graphic-context]\
          [list stroke-linejoin miter]\
          [concat rectangle $coords]\
          [list pop graphic-context]
      }
      text {
        lassign [$canvas bbox $item] x0 y0 x1 y1
        lappend line text $x0 $y1
        append line " '[$canvas itemcget $item -text]'"
        lappend result $line
      }
      image - bitmap {
        set img [$canvas itemcget $item -image]
        set file [$img cget -file]
        lassign [$canvas bbox $item] x0 y0
        lappend result [list image over $x0 $y0 0 0 '$file']       
      }
      default {
        lappend result\
          "# not yet done:\
             [$canvas type $item] [$canvas coords $item] ([$canvas gettags $item])"
      }
    }
  }
  join $result \n
}

11.3.2022