diff --git a/src/com/t_oster/liblasercut/LaserCutter.java b/src/com/t_oster/liblasercut/LaserCutter.java
index 9fdbe43046a75881514cbdfbd6c46078172eed16..64cc8b26a24702d421e4cf85cdd05c1319563703 100644
--- a/src/com/t_oster/liblasercut/LaserCutter.java
+++ b/src/com/t_oster/liblasercut/LaserCutter.java
@@ -24,6 +24,8 @@ package com.t_oster.liblasercut;
 
 import com.t_oster.liblasercut.platform.Point;
 import com.t_oster.liblasercut.platform.Util;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
 import java.util.LinkedList;
 import java.util.List;
 
@@ -80,6 +82,11 @@ public abstract class LaserCutter implements Cloneable, Customizable {
      */
     public abstract void sendJob(LaserJob job, ProgressListener pl, List<String> warnings) throws IllegalJobException, Exception;
 
+	public void saveJob(java.io.PrintStream fileOutputStream, LaserJob job) throws NotImplementedException, IllegalJobException, Exception {
+		System.err.println("Your driver does not implement saveJob(LaserJob job)");
+		throw new NotImplementedException();
+	}
+
     /**
      * If you lasercutter supports autofocus, override this method,
      * to let programs like VisiCut know, that they don't need to focus.
diff --git a/src/com/t_oster/liblasercut/drivers/GenericGcodeDriver.java b/src/com/t_oster/liblasercut/drivers/GenericGcodeDriver.java
index a41cc9c9c3397de55eb2cb45a187a05a5fc5bb58..cc0f29d02aea419ccf3a059194f8f52aa0d193eb 100644
--- a/src/com/t_oster/liblasercut/drivers/GenericGcodeDriver.java
+++ b/src/com/t_oster/liblasercut/drivers/GenericGcodeDriver.java
@@ -23,19 +23,27 @@ import com.t_oster.liblasercut.platform.Util;
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.File;
 import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.io.UnsupportedEncodingException;
+import java.lang.Exception;
 import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import purejavacomm.*;
 import java.util.*;
 import net.sf.corn.httpclient.HttpClient;
 import net.sf.corn.httpclient.HttpResponse;
 
+
+
+
+
 /**
  * This class implements a driver for a generic GRBL GCode Lasercutter.
  * It should contain all possible options and is inteded to be the superclass
@@ -377,6 +385,7 @@ public class GenericGcodeDriver extends LaserCutter {
     }
   }
 
+
   private void writeShutdownCode() throws IOException {
     if (postJobGcode != null)
     {
@@ -408,7 +417,7 @@ public class GenericGcodeDriver extends LaserCutter {
       }
     }
   }
-  
+
   protected void http_upload(URI url, String data, String filename) throws IOException
   {
     HttpClient client = new HttpClient(url);
@@ -651,6 +660,43 @@ public class GenericGcodeDriver extends LaserCutter {
     pl.taskChanged(this, "sent.");
     pl.progressChanged(this, 100);
   }
+
+@Override
+public void saveJob(java.io.PrintStream fileOutputStream, LaserJob job) throws IllegalJobException, Exception {
+	checkJob(job);
+
+	this.out = fileOutputStream;
+
+	boolean wasSetWaitingForOk = isWaitForOKafterEachLine();
+	setWaitForOKafterEachLine( false );
+
+	writeInitializationCode();
+	int i = 0;
+	int max = job.getParts().size();
+	for (JobPart p : job.getParts())
+	{
+		if (p instanceof RasterPart)
+		{
+			RasterPart rp = (RasterPart) p;
+			LaserProperty black = rp.getLaserProperty();
+			LaserProperty white = black.clone();
+			white.setProperty("power", 0.0f);
+			p = convertRasterToVectorPart((RasterPart) p, black, white,  p.getDPI(), false);
+		}
+		if (p instanceof VectorPart)
+		{
+			//TODO: in direct mode use progress listener to indicate progress
+			//of individual job
+			writeVectorGCode((VectorPart) p, p.getDPI());
+		}
+		i++;
+	}
+	writeShutdownCode();
+	this.out.flush();
+
+	setWaitForOKafterEachLine(wasSetWaitingForOk);
+}
+
   private List<Double> resolutions;
 
   @Override