Dev Blog


Original heme by orderedlist (CC-BY-SA)

Where applicable, all content is licensed under a CC-BY-SA.
Creative Commons License

GCode Conversion Tools

At one point, I had good success with an Inkscape plugin called Gcodetools but it seems to have succumbed to bit-rot and doesn't work on my current Ubuntu installation (16.04).

I'm settling on a rough toolchain that takes some base format (PostScript/PDF/SVG/etc.), converts to "GNUPlot format" then converts to GCocde.

Basic Workflow


Some tools of relevance are:

Under Ubuntu, some of the tools can be installed via:

sudo apt-get install pstoedit librsvg2-bin


Though this is pretty hodge-podge, there are a few things to consider:

The following is an example script to convert an input SVG file into GCode:

sf=`echo '72/25.4' | bc -l`
premul=`echo 1000000 | bc -l`
invmul=`echo "1/$premul" | bc -l`


if [[ "$inpsvg" == "" ]] ; then
  echo "provide input svg"
  exit 1

rawtype=`file $inpsvg`
checktype=`file -b $inpsvg | cut -f1 -d' '`
if [[ "$checktype" != "SVG" ]] ; then
  echo -e "file $inpsvg is of type:\n\n$rawtype\n\nNnot an SVG file? Exiting.\n"
  exit 1

bn=`basename $inpsvg .svg`

# causes duplicate paths otherwise
sed -i 's/fill="[^"]*"/fill="none"/g' $inpsvg

echo "creating $"
rsvg-convert -f ps -o $ $inpsvg

pstoedit -f gnuplot $ $
clipcli -s $ -F -x $premul -T > ${bn}

sfx_slow="$frapid S$S"
sfx_rapid="$fslow S0"

echo gp2ngc -i ${bn} -s "$invmul" --sfx-rapid "$sfx_rapid" --sfx-slow "$sfx_slow" -o ${bn}.ngc
gp2ngc -i ${bn} --sfx-rapid "$sfx_rapid" --sfx-slow "$sfx_slow" | ngc_scale -s "$invmul" > ${bn}.ngc


In theory, pstoedit can be used to create GCode but pstoedit converts to the RS274 standard. Among other things, the RS274 includes variables so a substitution step needs to be involved in order to "normalize" to something that other GCode interpreters can understand (for example, the smoothieboard or grbl).

There's still the problem of polygon ordering but assuming that's not an issue, the following is a "hacky" script does the substitution (no nested expressions, no non-trivial functions, run at your own risk):

# regexp substitution of variables.
# Uses Python's "eval" to evaluate interior
# after variable substitution.
# AGPLv3 license
import sys
import re

var_map = {}

# variable decleration
var_decl_pat = re.compile( r'\s*#(\d+)\s*=\s*([^\s]+)\s*(\([^\)]*\))?\s*$' )

# not [], [], not []
expr_pat = re.compile( r'([^\[]*)\[([^\]]*)\]([^\[]*)' )

# not #*, #\d+, not #*
var_sub_pat = re.compile( r'([^#]*)(#\d+)([^#]*)' )

# consider comments separately to avoid matching '#' and
# other special characters
comment_pat = re.compile( r'\([^\)]*\)' )

line_no = 0
for line in sys.stdin:
  line_no += 1

  line = line.rstrip()
  comments = ""
  for (comment) in re.findall(comment_pat, line):
    comments = comments + comment

  line = re.sub(comment_pat, '', line)
  m = re.match(var_decl_pat, line)
  if m:
    var_map[ "#" + str( ] = str(

  varsub_line = ""
  for (pfx, var_subs, sfx) in re.findall(var_sub_pat, line):
    if var_subs in var_map:
      print " ERROR on line", line_no, ", no variable mapping for", var_subs

    varsub_line += pfx
    varsub_line += var_map[var_subs]
    varsub_line += sfx

  if varsub_line == "":
    varsub_line = line

  xpr_match =, varsub_line)
  if not xpr_match:
    print varsub_line + comments

  cur_line = ""
  for (pfx, xpr, sfx) in re.findall(expr_pat, varsub_line):
    xpr_val = eval(xpr)
    cur_line += pfx + str(xpr_val) + sfx

  print cur_line +  comments