diff --git a/CHANGES.txt b/CHANGES.txt
index 29048f478e20004f62c5f6856d7000f40e778528..147aa7ffff30cbf579dabad8038cffa8a989f42d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -18,6 +18,8 @@ Changes in FLTK 1.4.0					Released: ??? ?? 2020
   New Features and Extensions
 
   - (add new items here)
+  - New classes Fl_SVG_File_Surface and Fl_EPS_File_Surface to save any FLTK
+    graphics to SVG or EPS files, respectively.
   - New fl_putenv() is a cross-platform putenv() wrapper (see docs).
   - New Fl::keyboard_screen_scaling(0) call stops recognition of ctrl/+/-/0/
     keystrokes as scaling all windows of a screen.
@@ -87,9 +89,6 @@ Changes in FLTK 1.4.0					Released: ??? ?? 2020
     of Fl_Clock, Fl_Clock_Output, and derived widgets.
   - New method Fl_Tabs::tab_align() allows to set alignment of tab labels,
     particularly to support icons on tab labels (STR #3076).
-  - Added '--enable-print' option to configure effective under X11 platforms
-    and with 'yes' default value. Using '--enable-print=no' removes print
-    and PostScript support from the FLTK library, thus reducing its size.
   - Added Fl_Surface_Device::push_current(new_surface) and
     Fl_Surface_Device::pop_current() to set/unset the current surface
     receiving graphics commands.
@@ -101,8 +100,13 @@ Changes in FLTK 1.4.0					Released: ??? ?? 2020
 
   New Configuration Options (ABI Version)
 
-  - The library can be built without support for SVG images using the
-    --disable-nanosvg configure option or turning off OPTION_USE_NANOSVG in CMake.
+  - The library can be built without support for reading SVG images or writing
+    graphics in SVG format using the --disable-svg configure option
+    or turning off OPTION_USE_SVG in CMake.
+  - The library can be built without support for PostScript, thus reducing
+    its size, using the --disable-print configure option or turning off
+    OPTION_PRINT_SUPPORT in CMake. That makes classes Fl_PostScript_File_Device,
+    Fl_EPS_File_Surface and Fl_Printer (under X11 platform only) ineffective.
   - FLTK's ABI version can be configured with 'configure' and CMake.
     See documentation in README.abi-version.txt.
 
@@ -119,6 +123,7 @@ Changes in FLTK 1.4.0					Released: ??? ?? 2020
   Other Improvements
 
   - (add new items here)
+  - Support for building for the arm64 architecture used by macOS 11.0 "Big Sur".
   - Add optional argument to Fl_Printer::begin_job() to receive
     a string describing the error when an error occurs.
   - Fix Windows-specific bug when the program tries to enlarge a
diff --git a/CMake/options.cmake b/CMake/options.cmake
index 559b68683e34c1150a0ed911b0dd48bfc4dd7474..70128080684bb6534e9c6957d9d5c62fb7aef528 100644
--- a/CMake/options.cmake
+++ b/CMake/options.cmake
@@ -179,11 +179,11 @@ else ()
 endif (PKG_CAIRO_FOUND)
 
 #######################################################################
-option(OPTION_USE_NANOSVG "support SVG images" ON)
+option(OPTION_USE_SVG "read/write SVG files" ON)
 
-if(OPTION_USE_NANOSVG)
-  set(FLTK_USE_NANOSVG 1)
-endif(OPTION_USE_NANOSVG)
+if(OPTION_USE_SVG)
+  set(FLTK_USE_SVG 1)
+endif(OPTION_USE_SVG)
 
 #######################################################################
 set(HAVE_GL LIB_GL OR LIB_MesaGL)
diff --git a/FL/Fl_PostScript.H b/FL/Fl_PostScript.H
index 6d26490dccf559bd0aa033df15eccd6787fa5547..ad6a6cd4af9fb462847228de3712fc12da87fa6e 100644
--- a/FL/Fl_PostScript.H
+++ b/FL/Fl_PostScript.H
@@ -3,7 +3,7 @@
 //
 // Support for graphics output to PostScript file for the Fast Light Tool Kit (FLTK).
 //
-// Copyright 2010-2019 by Bill Spitzak and others.
+// Copyright 2010-2020 by Bill Spitzak and others.
 //
 // This library is free software. Distribution and use rights are outlined in
 // the file "COPYING" which should have been included with this file.  If this
@@ -206,6 +206,11 @@ public:
   // ---
   Fl_Bitmask create_bitmask(int w, int h, const uchar *array) { return 0L; }
   virtual int has_feature(driver_feature feature_mask) { return feature_mask & PRINTER; }
+  
+  int start_eps(int width, int height);
+  void ps_origin(int x, int y);
+  void ps_translate(int, int);
+  void ps_untranslate();
 };
 
 /**
@@ -310,6 +315,58 @@ public:
   static const char *file_chooser_title;
 };
 
+/** Encapsulated PostScript drawing surface.
+ This drawing surface allows to store any FLTK graphics in vectorial form in an "Encapsulated PostScript" file.
+ \n Usage example:
+ \code
+   Fl_Window *win = ...// Window to draw to an .eps file
+   int ww = win->decorated_w();
+   int wh = win->decorated_h();
+   FILE *eps = fl_fopen("/path/to/mywindow.eps", "w");
+   if (eps) {
+     Fl_EPS_File_Surface *surface = new Fl_EPS_File_Surface(ww, wh, eps, win->color());
+     Fl_Surface_Device::push_current(surface);
+     surface->draw_decorated_window(win);
+     Fl_Surface_Device::pop_current();
+     delete surface; // the .eps file is not complete until the destructor was run
+     fclose(eps);
+   }
+ \endcode
+ */
+class FL_EXPORT Fl_EPS_File_Surface : public Fl_Widget_Surface {
+private:
+  void complete_();
+protected:
+  /** Returns the PostScript driver of this drawing surface. */
+  inline Fl_PostScript_Graphics_Driver *driver() { return (Fl_PostScript_Graphics_Driver*)Fl_Surface_Device::driver(); }
+public:
+  /**
+  Constructor.
+  \param width,height Width and height of the EPS drawing area
+  \param eps A writable FILE pointer where the Encapsulated PostScript data will be sent
+  \param background Color expected to cover the background of the EPS drawing area.
+  This parameter affects only the drawing of transparent Fl_RGB_Image objects:
+  transparent areas of RGB images are blended with the \p background color.
+  */
+  Fl_EPS_File_Surface(int width, int height, FILE *eps, Fl_Color background = FL_WHITE);
+  /**
+   Destructor.
+   The underlying FILE pointer remains open after destruction of the Fl_EPS_File_Surface object
+   unless close() was called.
+   */
+  ~Fl_EPS_File_Surface();
+  virtual int printable_rect(int *w, int *h);
+  /** Returns the underlying FILE pointer */
+  FILE *file() { return driver()->output; }
+  virtual void origin(int x, int y);
+  virtual void origin(int *px, int *py);
+  virtual void translate(int x, int y);
+  virtual void untranslate();
+  /** Closes using fclose() the underlying FILE pointer.
+   The only operation possible with the Fl_EPS_File_Surface object after calling close() is its destruction. */
+  int close();
+};
+
 #endif // Fl_PostScript_H
 
 //
diff --git a/FL/Fl_SVG_File_Surface.H b/FL/Fl_SVG_File_Surface.H
new file mode 100644
index 0000000000000000000000000000000000000000..272f7db1f23bd565f2794de689ff7f591d09f837
--- /dev/null
+++ b/FL/Fl_SVG_File_Surface.H
@@ -0,0 +1,73 @@
+//
+// Declaration of Fl_SVG_File_Surface in the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2020 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file.  If this
+// file is missing or damaged, see the license at:
+//
+//     https://www.fltk.org/COPYING.php
+//
+// Please report all bugs and problems on the following page:
+//
+//     https://www.fltk.org/str.php
+//
+
+#ifndef Fl_SVG_File_Surface_H
+#define Fl_SVG_File_Surface_H
+
+#include <FL/Fl_Widget_Surface.H>
+#include <stdio.h>
+
+/** A drawing surface producing a Scalable Vector Graphics (SVG) file.
+ This drawing surface allows to store any FLTK graphics in vectorial form in a "Scalable Vector Graphics" file.
+ \n Usage example:
+ \code
+   Fl_Window *win = ...// Window to draw to a .svg file
+   int ww = win->decorated_w();
+   int wh = win->decorated_h();
+   FILE *svg = fl_fopen("/path/to/mywindow.svg", "w");
+   if (svg) {
+     Fl_SVG_File_Surface *surface = new Fl_SVG_File_Surface(ww, wh, svg);
+     Fl_Surface_Device::push_current(surface);
+     fl_color(FL_WHITE);
+     fl_rectf(0, 0, ww, wh);
+     surface->draw_decorated_window(win);
+     Fl_Surface_Device::pop_current();
+     delete surface; // the .svg file is not complete until the destructor was run
+     fclose(svg);
+   }
+ \endcode
+ \note FLTK uses the PNG and JPEG libraries to encode images to the SVG format. If JPEG is
+ not available at application build time, PNG is enough (but produces a quite larger output).
+ If PNG isn't available either, images don't appear in the SVG output.
+*/
+class FL_EXPORT Fl_SVG_File_Surface : public Fl_Widget_Surface {
+  int width_, height_;
+public:
+  /**
+  Constructor of the SVG drawing surface.
+  \param width,height Width and height of the graphics area in FLTK drawing units
+  \param svg A writable FILE pointer where the SVG data are to be sent. The resulting SVG data are not complete until after destruction of the Fl_SVG_File_Surface object or after calling close().
+  */
+  Fl_SVG_File_Surface(int width, int height, FILE *svg);
+  /**
+   Destructor.
+   The underlying FILE pointer remains open after destruction of the Fl_SVG_File_Surface object
+   unless close() was called.
+   */
+  ~Fl_SVG_File_Surface();
+  /** Returns the underlying FILE pointer */
+  FILE *file();
+  virtual void origin(int x, int y);
+  virtual void translate(int x, int y);
+  virtual void untranslate();
+  virtual int printable_rect(int *w, int *h);
+  /** Closes with function fclose() the FILE pointer where SVG data is output.
+  The only operation possible after this on the Fl_SVG_File_Surface object is its destruction.
+  \return The value returned by fclose(). */
+  int close();
+};
+
+#endif /* Fl_SVG_File_Surface_H */
diff --git a/configh.cmake.in b/configh.cmake.in
index f00bad328817783bbb2480b06417e83fb3758fff..8b6a86985171eb8452c1c254acb16a84db90afbc 100644
--- a/configh.cmake.in
+++ b/configh.cmake.in
@@ -320,12 +320,12 @@
 #cmakedefine HAVE_PNG_SET_TRNS_TO_ALPHA 1
 
 /*
-* FLTK_USE_NANOSVG
+* FLTK_USE_SVG
 *
-* Do we want FLTK to support SVG images with nanosvg ?
+* Do we want FLTK to read and write SVG-formatted files ?
 */
 
-#cmakedefine FLTK_USE_NANOSVG 1
+#cmakedefine FLTK_USE_SVG 1
 
 /*
  * Do we have POSIX threading?
diff --git a/configh.in b/configh.in
index dcb8b5b8c3fe6579d0164bb4b0c356f0dcfdba51..65d90dc6d0b3a941174bdee08c603fe4801b32f2 100644
--- a/configh.in
+++ b/configh.in
@@ -287,7 +287,7 @@
 #undef HAVE_LIBPNG
 #undef HAVE_LIBZ
 #undef HAVE_LIBJPEG
-#undef FLTK_USE_NANOSVG
+#undef FLTK_USE_SVG
 
 /*
  * FLTK_USE_CAIRO
diff --git a/configure.ac b/configure.ac
index fda55a21d73c84c4b7c0356b7b405a05dd3b5b9b..20fcbe05ad93268ab60d83d7ed36db2a62828587 100644
--- a/configure.ac
+++ b/configure.ac
@@ -789,10 +789,10 @@ AC_SUBST(PNGINC)
 AC_SUBST(ZLIB)
 AC_SUBST(ZLIBINC)
 
-# Control the usage of the nanosvg lib
-AC_ARG_ENABLE(nanosvg, [  --enable-nanosvg        use nanosvg to support SVG images  [[default=yes]]])
-if test x$enable_nanosvg != xno; then
-    AC_DEFINE(FLTK_USE_NANOSVG)
+# Control the usage of the nanosvg lib and SVG output
+AC_ARG_ENABLE(svg, [  --enable-svg            read/write SVG files  [[default=yes]]])
+if test x$enable_svg != xno; then
+    AC_DEFINE(FLTK_USE_SVG)
 fi
 
 dnl Restore original LIBS settings...
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0205cb9963b9dc5093cb35c6cf96534871a9b301..3c1f0f0d68fb12b2deeec89bef175222e1021268 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -161,6 +161,7 @@ set (CPPFILES
   fl_utf8.cxx
   fl_encoding_latin1.cxx
   fl_encoding_mac_roman.cxx
+  drivers/SVG/Fl_SVG_File_Surface.cxx
 )
 
 # find all header files in source directory <FL/...>
diff --git a/src/Fl_SVG_Image.cxx b/src/Fl_SVG_Image.cxx
index b0fb9dd9d47ad4426f29126e8790abb563bede17..c010aa2cf20f10a82d8436449c69b4b45ff25644 100644
--- a/src/Fl_SVG_Image.cxx
+++ b/src/Fl_SVG_Image.cxx
@@ -18,7 +18,7 @@
 
 #include <config.h>
 
-#if defined(FLTK_USE_NANOSVG) || defined(FL_DOXYGEN)
+#if defined(FLTK_USE_SVG) || defined(FL_DOXYGEN)
 
 #include <FL/Fl_SVG_Image.H>
 #include <FL/fl_utf8.h>
@@ -272,7 +272,7 @@ void Fl_SVG_Image::normalize() {
   if (!array) resize(w(), h());
 }
 
-#endif // FLTK_USE_NANOSVG
+#endif // FLTK_USE_SVG
 
 //
 // End of "$Id$".
diff --git a/src/Makefile b/src/Makefile
index 5274a6a6cdf7786b51dbba3e751e0c24f7ee2942..7fde7e8c967498e54ea66d247de3c34905df6e68 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -214,7 +214,6 @@ IMGCPPFILES = \
 	Fl_Image_Reader.cxx \
 	Fl_SVG_Image.cxx
 
-
 CFILES = fl_call_main.c flstring.c numericsort.c vsnprintf.c
 
 UTF8CFILES = \
@@ -306,7 +305,8 @@ GDICFILES = \
 
 PSCPPFILES = \
 	drivers/PostScript/Fl_PostScript.cxx \
-	drivers/PostScript/Fl_PostScript_image.cxx
+	drivers/PostScript/Fl_PostScript_image.cxx \
+	drivers/SVG/Fl_SVG_File_Surface.cxx
 
 ################################################################
 FLTKFLAGS = -DFL_LIBRARY
diff --git a/src/drivers/PostScript/Fl_PostScript.cxx b/src/drivers/PostScript/Fl_PostScript.cxx
index f182720b811ef2bfceb6e80cd71620d36bd06bf2..d04b27af3f1215cb9a9cbc0022b7f194a5e0abd7 100644
--- a/src/drivers/PostScript/Fl_PostScript.cxx
+++ b/src/drivers/PostScript/Fl_PostScript.cxx
@@ -26,6 +26,7 @@
 #include <FL/Fl_Native_File_Chooser.H>
 #include "../../Fl_System_Driver.H"
 #include <stdarg.h>
+#include <time.h>
 
 const char *Fl_PostScript_File_Device::file_chooser_title = "Select a .ps file";
 
@@ -137,7 +138,6 @@ int Fl_PostScript_Graphics_Driver::clocale_printf(const char *format, ...)
 //  Prolog string 
 
 static const char * prolog =
-"%%BeginProlog\n"
 "/L { /y2 exch def\n"
 "/x2 exch def\n"
 "/y1 exch def\n"
@@ -554,7 +554,7 @@ int Fl_PostScript_Graphics_Driver::start_postscript (int pagecount,
   if (lang_level_ == 3 && (layout & Fl_Paged_Device::LANDSCAPE) ) { x = w; w = h; h = x; }
   fprintf(output, "<</PageSize[%d %d]>>setpagedevice\n", w, h );
   fputs("%%EndFeature\n", output);
-  fputs("%%EndComments\n", output);
+  fputs("%%EndComments\n%%BeginProlog\n", output);
   fputs(prolog, output);
   if (lang_level_ > 1) {
     fputs(prolog_2, output);
@@ -583,6 +583,41 @@ int Fl_PostScript_Graphics_Driver::start_postscript (int pagecount,
   return 0;
 }
 
+int Fl_PostScript_Graphics_Driver::start_eps (int width, int height) {
+  width_ = width;
+  height_ = height;
+  fputs("%!PS-Adobe-3.0 EPSF-3.0\n", output);
+  fputs("%%Creator: (FLTK)\n", output);
+  fprintf(output,"%%%%BoundingBox: 1 1 %d %d\n", width, height);
+  if (ps_filename_) fprintf(output,"%%%%Title: (%s)\n", fl_filename_name(ps_filename_));
+  time_t lt = time(NULL);
+  fprintf(output,"%%%%CreationDate: %s", ctime(&lt)+4);
+  lang_level_= 2;
+  fprintf(output, "%%%%LanguageLevel: 2\n");
+  fputs("%%Pages: 1\n%%EndComments\n", output);
+  fputs("%%BeginProlog\n", output);
+  fputs("%%EndProlog\n",output);
+  fprintf(output, "save\n");
+  fputs("/FLTK 20 dict def FLTK begin\n"
+  "/x1 0 def /x2 0 def /y1 0 def /y2 0 def /x 0 def /y 0 def /dx 0 def /dy 0 def\n"
+        "/px 0 def /py 0 def /sx 0 def /sy 0 def /inter 0 def\n"
+        "/pixmap_sx 0 def  /pixmap_sy 0 def /pixmap_w 0 def /pixmap_h 0 def\n", output);
+  fputs(prolog, output);
+  fputs(prolog_2, output);
+  fputs(prolog_2_pixmap, output);
+  fputs("/CS { GS } bind def\n", output);
+  fputs("/CR { GR } bind def\n", output);
+  page_policy_ = 1;
+  reset();
+  nPages=0;
+  fprintf(output, "GS\n");
+  clocale_printf( "%g %g TR\n", (double)0, height_);
+  fprintf(output, "1 -1 SC\n");
+  line_style(0);
+  fprintf(output, "GS GS\n");
+  return 0;
+}
+
 void Fl_PostScript_Graphics_Driver::recover(){
   color(cr_,cg_,cb_);
   line_style(linestyle_,linewidth_,linedash_);
@@ -1386,6 +1421,22 @@ int Fl_PostScript_Graphics_Driver::not_clipped(int x, int y, int w, int h) {
   return 0;
 }
 
+void Fl_PostScript_Graphics_Driver::ps_origin(int x, int y)
+{
+  clocale_printf("GR GR GS %d %d TR  %f %f SC %d %d TR %f rotate GS\n",
+    left_margin, top_margin, scale_x, scale_y, x, y, angle);
+}
+
+void Fl_PostScript_Graphics_Driver::ps_translate(int x, int y)
+{
+  fprintf(output, "GS %d %d translate GS\n", x, y);
+}
+
+void Fl_PostScript_Graphics_Driver::ps_untranslate(void)
+{
+  fprintf(output, "GR GR\n");
+}
+
 void Fl_PostScript_File_Device::margins(int *left, int *top, int *right, int *bottom) // to implement
 {
   Fl_PostScript_Graphics_Driver *ps = driver();
@@ -1413,9 +1464,7 @@ void Fl_PostScript_File_Device::origin(int x, int y)
 {
   x_offset = x;
   y_offset = y;
-  Fl_PostScript_Graphics_Driver *ps = driver();
-  ps->clocale_printf("GR GR GS %d %d TR  %f %f SC %d %d TR %f rotate GS\n",
-	  ps->left_margin, ps->top_margin, ps->scale_x, ps->scale_y, x, y, ps->angle);
+  driver()->ps_origin(x, y);
 }
 
 void Fl_PostScript_File_Device::scale (float s_x, float s_y)
@@ -1438,12 +1487,12 @@ void Fl_PostScript_File_Device::rotate (float rot_angle)
 
 void Fl_PostScript_File_Device::translate(int x, int y)
 {
-  fprintf(driver()->output, "GS %d %d translate GS\n", x, y);
+  driver()->ps_translate(x, y);
 }
 
 void Fl_PostScript_File_Device::untranslate(void)
 {
-  fprintf(driver()->output, "GR GR\n");
+  driver()->ps_untranslate();
 }
 
 int Fl_PostScript_File_Device::begin_page (void)
@@ -1495,9 +1544,85 @@ void Fl_PostScript_File_Device::end_job (void)
 }
 
 /**
- \}
- \endcond
- */
+\}
+\endcond
+*/
+
+Fl_EPS_File_Surface::Fl_EPS_File_Surface(int width, int height, FILE *eps, Fl_Color background) :
+        Fl_Widget_Surface(new Fl_PostScript_Graphics_Driver()) {
+  Fl_PostScript_Graphics_Driver *ps = driver();
+  ps->output = eps;
+  if (ps->output) {
+    float s = Fl::screen_scale(0);
+    ps->start_eps(width*s, height*s);
+    if (s != 1) {
+      ps->clocale_printf("GR GR GS %f %f SC GS\n", s, s);
+      ps->scale_x = ps->scale_y = s;
+    }
+    Fl::get_color(background, ps->bg_r, ps->bg_g, ps->bg_b);
+  }
+}
+
+void Fl_EPS_File_Surface::complete_() {
+  Fl_PostScript_Graphics_Driver *ps = driver();
+  if(ps->output) {
+    fputs("GR\nend %matches begin of FLTK dict\n", ps->output);
+    fputs("restore\n", ps->output);
+    fputs("%%EOF\n", ps->output);
+    ps->reset();
+    fflush(ps->output);
+    if(ferror(ps->output)) {
+      fl_alert ("Error during PostScript data output.");
+    }
+  }
+  while (ps->clip_){
+    Fl_PostScript_Graphics_Driver::Clip * c= ps->clip_;
+    ps->clip_= ps->clip_->prev;
+    delete c;
+  }
+}
+
+Fl_EPS_File_Surface::~Fl_EPS_File_Surface() {
+  Fl_PostScript_Graphics_Driver *ps = driver();
+  if(ps->output) complete_();
+  delete ps;
+}
+
+int Fl_EPS_File_Surface::close() {
+  complete_();
+  Fl_PostScript_Graphics_Driver *ps = driver();
+  int retval = fclose(ps->output);
+  ps->output = NULL;
+  return retval;
+}
+
+int Fl_EPS_File_Surface::printable_rect(int *w, int *h) {
+  Fl_PostScript_Graphics_Driver *ps = driver();
+  *w = ps->width_;
+  *h = ps->height_;
+  return 0;
+}
+
+void Fl_EPS_File_Surface::origin(int x, int y)
+{
+  x_offset = x;
+  y_offset = y;
+  driver()->ps_origin(x, y);
+}
+
+void Fl_EPS_File_Surface::origin(int *px, int *py) {
+  Fl_Widget_Surface::origin(px, py);
+}
+
+void Fl_EPS_File_Surface::translate(int x, int y)
+{
+  driver()->ps_translate(x, y);
+}
+
+void Fl_EPS_File_Surface::untranslate()
+{
+  driver()->ps_untranslate();
+}
 
 #endif // !defined(FL_NO_PRINT_SUPPORT)
 
diff --git a/src/drivers/SVG/Fl_SVG_File_Surface.cxx b/src/drivers/SVG/Fl_SVG_File_Surface.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..d6cf7f4b711a4bfa8c32df0e0d109b83e39cf652
--- /dev/null
+++ b/src/drivers/SVG/Fl_SVG_File_Surface.cxx
@@ -0,0 +1,1023 @@
+//
+// Implementation of classes Fl_SVG_Graphics_Driver and Fl_SVG_File_Surface in the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2020 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file.  If this
+// file is missing or damaged, see the license at:
+//
+//     https://www.fltk.org/COPYING.php
+//
+// Please report all bugs and problems on the following page:
+//
+//     https://www.fltk.org/str.php
+//
+
+// Complete implementation to draw into an SVG file using the standard FLTK drawing API.
+
+#include <config.h>
+#include <FL/Fl_SVG_File_Surface.H>
+#if FLTK_USE_SVG
+#include <FL/fl_draw.H>
+#include <stdio.h>
+#include <FL/math.h>
+#include <FL/Fl_Widget_Surface.H>
+#include <FL/Fl_Graphics_Driver.H>
+#include <FL/Fl.H>
+#include <FL/Fl_RGB_Image.H>
+#include <FL/Fl_Pixmap.H>
+#include <FL/Fl_Bitmap.H>
+extern "C" {
+#if defined(HAVE_LIBPNG)
+#  ifdef HAVE_PNG_H
+#    include <png.h>
+#  else
+#    include <libpng/png.h>
+#  endif // HAVE_PNG_H
+#endif // HAVE_LIBPNG
+
+#ifdef HAVE_LIBJPEG
+#  include <jpeglib.h>
+#endif // HAVE_LIBJPEG
+}
+
+class Fl_SVG_Graphics_Driver : public Fl_Graphics_Driver {
+  FILE *out_;
+  int width_;
+  int line_style_;
+  const char *linecap_;
+  const char *linejoin_;
+  uchar red_, green_, blue_;
+  char *dasharray_; // the dash array as SVG needs it
+  char *user_dash_array_; // the dash array as FLTK needs it
+  int p_size;
+  typedef struct { float x; float y; } XPOINT;
+  XPOINT *p;
+  class Clip {
+  public:
+    int x, y, w, h; // the clip rectangle
+    char Id[12]; // "none" or SVG Id of this clip rectangle
+    Clip *prev; // previous in pile of clips
+  };
+  Clip * clip_; // top of pile of clips
+  int clip_count_; // to generate distinct SVG clip Ids
+  char *last_rgb_name_; // NULL or SVG Id of last defined RGB image
+  const char *family_;
+  const char *bold_;
+  const char *style_;
+public:
+  Fl_SVG_Graphics_Driver(FILE*);
+  ~Fl_SVG_Graphics_Driver();
+  FILE* file() {return out_;}
+protected:
+  void rect(int x, int y, int w, int h);
+  void rectf(int x, int y, int w, int h);
+  void compute_dasharray(float s, char *dashes=0);
+  void line_style(int style, int width, char *dashes=0);
+  void line(int x1, int y1, int x2, int y2);
+  void font_(int f, int s);
+  void font(int f, int s);
+  void draw(const char *str, int n, int x, int y);
+  void draw(const char*, int, float, float) ;
+  void draw(int, const char*, int, int, int) ;
+  void rtl_draw(const char *str, int n, int x, int y);
+  void color(uchar r, uchar g, uchar b);
+  void color(Fl_Color c);
+  double width(const char*, int) ;
+  void text_extents(const char*, int n, int& dx, int& dy, int& w, int& h);
+  int height() ;
+  int descent() ;
+  void draw_rgb(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP, int cx, int cy);
+  void define_rgb_png(Fl_RGB_Image *rgb, const char *name, int x, int y);
+  void define_rgb_jpeg(Fl_RGB_Image *rgb, const char *name, int x, int y);
+  void draw_pixmap(Fl_Pixmap *pxm,int XP, int YP, int WP, int HP, int cx, int cy);
+  void draw_bitmap(Fl_Bitmap *bm,int XP, int YP, int WP, int HP, int cx, int cy);
+  void draw_image(const uchar* buf, int x, int y, int w, int h, int d, int l);
+  void draw_image(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d);
+  void draw_image_mono(const uchar* buf, int x, int y, int w, int h, int d, int l);
+  void draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d);
+  void push_clip(int x, int y, int w, int h);
+  void push_no_clip();
+  void pop_clip();
+  int clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H);
+  int not_clipped(int x, int y, int w, int h);
+  void polygon(int x0, int y0, int x1, int y1, int x2, int y2);
+  void polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3);
+  void loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3);
+  void loop(int x0, int y0, int x1, int y1, int x2, int y2);
+  void point(int x, int y);
+  void transformed_vertex0(float x, float y);
+  void transformed_vertex(double xf, double yf);
+  void vertex(double x,double y);
+  void end_points();
+  void end_line();
+  void fixloop();
+  void end_loop();
+  void end_polygon();
+  void begin_complex_polygon();
+  void gap();
+  void end_complex_polygon();
+  void circle(double x, double y,double r);
+  void arc(int x,int y,int w,int h,double a1,double a2);
+  void pie(int x,int y,int w,int h,double a1,double a2);
+  void arc_pie(char AorP, int x, int y, int w, int h, double a1, double a2);
+};
+
+Fl_SVG_Graphics_Driver::Fl_SVG_Graphics_Driver(FILE *f) {
+  out_ = f;
+  width_ = 1;
+  line_style_ = 0;
+  linecap_ = "butt";
+  linejoin_ = "miter";
+  family_ = "";
+  bold_ = "";
+  style_ = "";
+  red_ = green_ = blue_ = 0;
+  clip_count_ = 0;
+  clip_ = NULL;
+  user_dash_array_ = 0;
+  dasharray_ = strdup("none");
+  p_size = 0;
+  p = NULL;
+  last_rgb_name_ = NULL;
+}
+
+Fl_SVG_Graphics_Driver::~Fl_SVG_Graphics_Driver()
+{
+  if (user_dash_array_) free(user_dash_array_);
+  if (dasharray_) free(dasharray_);
+  while (clip_){
+    Clip * c= clip_;
+    clip_= clip_->prev;
+    delete c;
+  }
+  if (last_rgb_name_) free(last_rgb_name_);
+}
+
+void Fl_SVG_Graphics_Driver::rect(int x, int y, int w, int h) {
+  fprintf(out_, "<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" "
+	  "fill=\"none\" stroke=\"rgb(%u,%u,%u)\" stroke-width=\"%d\" stroke-dasharray=\"%s\""
+          " stroke-linecap=\"%s\" stroke-linejoin=\"%s\"/>\n", x, y, w-1, h-1, red_, green_, blue_, width_, dasharray_, linecap_, linejoin_);
+}
+
+void Fl_SVG_Graphics_Driver::rectf(int x, int y, int w, int h) {
+  fprintf(out_, "<rect x=\"%.3f\" y=\"%.3f\" width=\"%d\" height=\"%d\" "
+	  "fill=\"rgb(%u,%u,%u)\" />\n", x-.5, y-.5, w, h, red_, green_, blue_);
+}
+
+void Fl_SVG_Graphics_Driver::point(int x, int y) {
+  rectf(x,y,1,1);
+}
+
+void Fl_SVG_Graphics_Driver::line(int x1, int y1, int x2, int y2) {
+  fprintf(out_, 
+	  "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" "
+	  "style=\"stroke:rgb(%u,%u,%u);stroke-width:%d;stroke-linecap:%s;stroke-linejoin:%s;stroke-dasharray:%s\" />\n",
+	  x1,y1,x2,y2, red_, green_, blue_, width_, linecap_, linejoin_, dasharray_);
+}
+
+void Fl_SVG_Graphics_Driver::font_(int ft, int s) {
+  Fl_Graphics_Driver::font(ft, s);
+  int famnum = ft/4;
+  if (famnum == 0) family_ = "Helvetica";
+  else if (famnum == 1) family_ = "Courier";
+  else family_ = "Times";
+  int modulo = ft % 4;
+  int use_bold = modulo == 1 || modulo == 3;
+  int use_italic = modulo >= 2;  
+  bold_ =  ( use_bold ? " font-weight=\"bold\"" : "" );
+  style_ =  ( use_italic ? " font-style=\"italic\"" : "" );
+  if (use_italic && famnum != 2) style_ = " font-style=\"oblique\"";
+}
+
+void Fl_SVG_Graphics_Driver::font(int ft, int s) {
+  Fl_Display_Device::display_device()->driver()->font(ft, s);
+  font_(ft, s);
+}
+
+void Fl_SVG_Graphics_Driver::compute_dasharray(float s, char *dashes) {
+  if (user_dash_array_ && user_dash_array_ != dashes) {free(user_dash_array_); user_dash_array_ = NULL;}
+  if (dashes && *dashes) {
+    if (dasharray_) free(dasharray_);
+    dasharray_ = (char*)calloc(10*strlen(dashes) + 1, 1);
+    for (char *p = dashes; *p; p++) {
+      sprintf(dasharray_+strlen(dasharray_), "%.3f,", (*p)/s);
+    }
+    dasharray_[strlen(dasharray_) - 1] = 0;
+    if (user_dash_array_ != dashes) user_dash_array_ = strdup(dashes);
+    return;
+  }
+  int dash_part = line_style_ & 0xFF;
+  if (dash_part == FL_SOLID)  {
+    if (dasharray_ && strcmp(dasharray_, "none")) free(dasharray_);
+    dasharray_ = strdup("none");
+  } else {
+    int cap_part = (line_style_ & 0xF00);
+    bool is_flat = (cap_part == FL_CAP_FLAT || cap_part == 0);
+    float dot = (is_flat ? width_/s : width_*0.6/s);
+    float gap = (is_flat ? width_/s : width_*1.5/s);
+    float big = (is_flat ? 3*width_/s : width_*2.5/s);
+    if (dasharray_) free(dasharray_);
+    dasharray_ = (char*)malloc(61);
+    if (dash_part == FL_DOT) sprintf(dasharray_, "%.3f,%.3f", dot, gap);
+    else if (dash_part == FL_DASH) sprintf(dasharray_, "%.3f,%.3f", big, gap);
+    else if (dash_part == FL_DASHDOT) sprintf(dasharray_, "%.3f,%.3f,%.3f,%.3f", big, gap, dot, gap);
+    else sprintf(dasharray_, "%.3f,%.3f,%.3f,%.3f,%.3f,%.3f", big, gap, dot, gap, dot, gap);
+  }
+}
+
+void Fl_SVG_Graphics_Driver::line_style(int style, int width, char *dashes) {
+  line_style_ = style;
+  if (width == 0) width = 1;
+  width_ = width;
+  int cap_part = style & 0xF00;
+  if (cap_part == FL_CAP_SQUARE) linecap_ = "square";
+  else if (cap_part == FL_CAP_ROUND) linecap_ = "round";
+  else linecap_ = "butt";
+  int join_part = style & 0xF000;
+  if (join_part == FL_JOIN_BEVEL) linejoin_ = "bevel";
+  else if (join_part == FL_JOIN_MITER) linejoin_ = "miter";
+  else if (join_part == FL_JOIN_ROUND) linejoin_ = "round";
+  else linejoin_ = "miter";
+  compute_dasharray(1., dashes);
+}
+
+void Fl_SVG_Graphics_Driver::draw(const char *str, int n, int x, int y) {
+  // Caution: Internet Explorer ignores the xml:space="preserve" attribute
+  // work-around: replace all spaces by no-break space = U+00A0 = 0xC2-0xA0 (UTF-8) before sending to IE
+  fprintf(out_, "<text x=\"%d\" y=\"%d\" font-family=\"%s\"%s%s font-size=\"%d\" "
+          "xml:space=\"preserve\" "
+          " fill=\"rgb(%u,%u,%u)\" textLength=\"%d\">", x, y, family_, bold_, style_, size(), red_, green_, blue_, (int)width(str, n));
+  for (int i = 0; i < n; i++) {
+    if (str[i] == '&') fputs("&amp;", out_);
+    else if (str[i] == '<') fputs("&lt;", out_);
+    else if (str[i] == '>') fputs("&gt;", out_);
+    else fputc(str[i], out_);
+  }
+  fputs("</text>\n", out_);
+}
+
+void Fl_SVG_Graphics_Driver::draw(const char* str, int n, float fx, float fy) {
+  return draw(str, n, (int)fx, (int)fy);
+}
+
+void Fl_SVG_Graphics_Driver::draw(int angle, const char* str, int n, int x, int y) {
+  fprintf(out_, "<g transform=\"translate(%d,%d) rotate(%d)\">", x, y, -angle);
+  draw(str, n, 0, 0);
+  fputs("</g>\n", out_);
+}
+
+void Fl_SVG_Graphics_Driver::rtl_draw(const char *str, int n, int x, int y) {
+  int w = (int)width(str, n);
+  draw(str, n, x - w, y);
+}
+
+void Fl_SVG_Graphics_Driver::color(Fl_Color c) {
+  Fl_Graphics_Driver::color(c);
+  Fl::get_color(c, red_, green_, blue_);
+}
+
+void Fl_SVG_Graphics_Driver::color(uchar r, uchar g, uchar b) {
+  red_ = r;
+  green_ = g;
+  blue_ = b;
+}
+
+double Fl_SVG_Graphics_Driver::width(const char* str, int l) {
+ return Fl_Display_Device::display_device()->driver()->width(str, l);
+}
+
+void Fl_SVG_Graphics_Driver::text_extents(const char *c, int n, int& dx, int& dy, int& w, int& h) {
+  Fl::first_window()->make_current(); // to get a valid drawing gc
+  Fl_Display_Device::display_device()->driver()->text_extents(c, n, dx, dy, w, h);
+}
+
+int Fl_SVG_Graphics_Driver::height() {
+  return Fl_Display_Device::display_device()->driver()->height();
+}
+
+int Fl_SVG_Graphics_Driver::descent() {
+  return Fl_Display_Device::display_device()->driver()->descent();
+}
+
+Fl_SVG_File_Surface::Fl_SVG_File_Surface(int w, int h, FILE *f) : Fl_Widget_Surface(new Fl_SVG_Graphics_Driver(f)) {
+  Fl_Window *win = Fl::first_window();
+  float s = (win ? Fl::screen_scale(win->screen_num()) : 1);
+  int sw = w * s, sh = h * s;
+  fprintf(f,
+	  "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n"
+	  "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \n"
+	  "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
+	  "<svg width=\"%dpx\" height=\"%dpx\" viewBox=\"0 0 %d %d\"\n"
+	  "xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n", sw, sh, sw, sh);
+  width_ = w; height_ = h;
+  fprintf(f, "<g transform=\"scale(%f)\">\n", s);
+  fputs("<g transform=\"translate(0,0)\">\n", f);
+}
+
+Fl_SVG_File_Surface::~Fl_SVG_File_Surface() {
+  Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver();
+  if (driver) {
+    fputs("</g></g></svg>\n", driver->file());
+    fflush(driver->file());
+    delete driver;
+  }
+}
+
+FILE *Fl_SVG_File_Surface::file() {
+  Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver();
+  return driver->file();
+}
+
+int Fl_SVG_File_Surface::close() {
+  Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver();
+  fputs("</g></g></svg>\n", driver->file());
+  int retval = fclose(driver->file());
+  delete driver;
+  this->driver(NULL);
+  return retval;
+}
+
+void Fl_SVG_File_Surface::translate(int x, int y) {
+  Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver();
+  fprintf(driver->file(), "<g transform=\"translate(%d,%d) \">\n", x, y);
+}
+
+void Fl_SVG_File_Surface::untranslate() {
+  Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver();
+  fputs("</g>\n", driver->file());
+}
+
+void Fl_SVG_File_Surface::origin(int x, int y) {
+  Fl_SVG_Graphics_Driver *driver = (Fl_SVG_Graphics_Driver*)this->driver();
+  fprintf(driver->file(), "</g><g transform=\"translate(%d,%d) \">\n", x, y);
+  Fl_Widget_Surface::origin(x, y);
+}
+
+int Fl_SVG_File_Surface::printable_rect(int *w, int *h) {
+  *w = width_;
+  *h = height_;
+  return 0;
+}
+
+struct svg_base64_t { // holds data useful to perform base64-encoding of a stream of bytes
+  FILE *svg; // where base64-encoded data is output
+  int lline; // follows length of current line in svg file
+  uchar buff[3]; // holds up to 3 bytes that still need encoding
+  int lbuf; // # of valid bytes in buff
+};
+
+// Performs base64 encoding of up to 3 bytes.
+// To be called successively with 3 consecutive bytes (l=3),
+// and possibly with l=1 or l=2 only at the end of the byte stream.
+// Always writes 4 printable characters to the output FILE.
+static void to_base64(uchar *p, int l, svg_base64_t *svg_base64) {
+  static char base64_table[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  uchar B0 = *p++;
+  uchar B1 = (l == 1 ? 0 : *p++);
+  uchar B2 = (l <= 2 ? 0 : *p);
+  fputc(base64_table[ B0 >> 2 ], svg_base64->svg);
+  fputc(base64_table[ ((B0 & 0x3) << 4) + (B1 >> 4) ], svg_base64->svg);
+  fputc( (l == 1 ? '=' : base64_table[ ((B1 & 0xF) << 2) + (B2 >> 6) ]), svg_base64->svg );
+  fputc( (l < 3 ? '=' : base64_table[ B2 & 0x3F ]), svg_base64->svg );
+  svg_base64->lline += 4;
+  if (svg_base64->lline >= 80) {
+    fputc('\n', svg_base64->svg);
+    svg_base64->lline = 0;
+  }
+}
+
+// Writes to the svg file, in base64-encoded form, a block of length bytes.
+// 1 or 2 bytes may remain unprocessed after return.
+// Returns the number of remaining unprocessed bytes.
+static size_t write_by_3(uchar *data, size_t length, svg_base64_t *svg_base64) {
+  while (length >= 3) {
+    to_base64(data, 3, svg_base64);
+    data += 3;
+    length -= 3;
+  }
+  return length;
+}
+
+#ifdef HAVE_LIBPNG
+
+// processes length bytes of the png stream under construction
+static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
+  svg_base64_t *svg_base64_data = (svg_base64_t*)png_get_io_ptr(png_ptr);
+  if (svg_base64_data->lbuf == 1 && length >= 2) {
+    svg_base64_data->buff[1] = *data++; length--;
+    svg_base64_data->buff[2] = *data++; length--;
+    write_by_3(svg_base64_data->buff, 3, svg_base64_data);
+  }  else if (svg_base64_data->lbuf == 2 && length >= 1) {
+    svg_base64_data->buff[2] = *data++; length--;
+    write_by_3(svg_base64_data->buff, 3, svg_base64_data);
+  }
+  size_t new_l = length;
+  if (length >= 3) {
+    new_l = write_by_3(data, length, svg_base64_data);
+  }
+  svg_base64_data->lbuf = new_l;
+  if (new_l) {
+    memcpy(svg_base64_data->buff, data + length - new_l, new_l);
+  }
+}
+
+// processes last bytes to be base64 encoded
+static void user_flush_data(png_structp png_ptr) {
+  svg_base64_t *svg_base64_data = (svg_base64_t*)png_get_io_ptr(png_ptr);
+  if (svg_base64_data->lbuf) to_base64(svg_base64_data->buff, svg_base64_data->lbuf, svg_base64_data);
+}
+
+/* How to define first the image data and next use it, possibly several times:
+<defs><image id="myimage"  width="64" height="64" href="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAnElEQVR4nO3UsQ3EIADAwEQgsf+S7ECB
+/sdwkfMEru7de/+eDzfvvfVD2hxj1A9pc61VP6TNc079kMYABjCAAfVDGgMYwAAG1A9pDGAAAxhQP6Qx
+gAEMYED9kMYABjCAAfVDGgMYwAAG1A9pDGAAAxhQP6QxgAEMYED9kMYABjCAAfVDGgMYwAAG1A9pDGAA
+AxhQP6QxgAEM+LYBf9sdYcTRmp6pAAAAAElFTkSuQmCCAAAAAElFTkSuQmCC"/>
+</defs>
+<use href="#myimage" x="xxx" y="yyy"/>
+<use href="#myimage" x="xxx2" y="yyy2"/>
+*/
+/* Specify image data and draw it in one go:
+ <image x="xxx" y="yyy"  width="64" height="64" href="data:image/png;base64,
+ iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAnElEQVR4nO3UsQ3EIADAwEQgsf+S7ECB
+ /sdwkfMEru7de/+eDzfvvfVD2hxj1A9pc61VP6TNc079kMYABjCAAfVDGgMYwAAG1A9pDGAAAxhQP6Qx
+ gAEMYED9kMYABjCAAfVDGgMYwAAG1A9pDGAAAxhQP6QxgAEMYED9kMYABjCAAfVDGgMYwAAG1A9pDGAA
+ AxhQP6QxgAEM+LYBf9sdYcTRmp6pAAAAAElFTkSuQmCCAAAAAElFTkSuQmCC"/>
+ */
+
+void Fl_SVG_Graphics_Driver::define_rgb_png(Fl_RGB_Image *rgb, const char *name, int x, int y) {
+  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+  if (!png_ptr) return;
+  png_infop info_ptr = png_create_info_struct(png_ptr);
+  if (!info_ptr) {
+    png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+    return;
+  }
+  if (name) {
+    if (last_rgb_name_) free(last_rgb_name_);
+    last_rgb_name_ = strdup(name);
+  }
+  float f = rgb->data_w() > rgb->data_h() ? float(rgb->w()) / rgb->data_w(): float(rgb->h()) / rgb->data_h();
+  if (name) fprintf(out_, "<defs><image id=\"%s\" ", name);
+  else fprintf(out_, "<image x=\"%d\" y=\"%d\" ", x, y);
+  fprintf(out_, "width=\"%f\" height=\"%f\" href=\"data:image/png;base64,\n", f*rgb->data_w(), f*rgb->data_h());
+  // Transforms the image into a stream of bytes in PNG format,
+  // base64-encode this byte stream, and outputs the result to the svg FILE.
+  svg_base64_t svg_base64_data;
+  svg_base64_data.svg = out_;
+  svg_base64_data.lline = 0;
+  svg_base64_data.lbuf = 0;
+  // user_write_data is a function repetitively called by libpng which receives blocks of bytes.
+  png_set_write_fn(png_ptr, &svg_base64_data, user_write_data, user_flush_data);
+  int color_type;
+  switch (rgb->d()) {
+    case 1:
+      color_type = PNG_COLOR_TYPE_GRAY;
+      break;
+    case 2:
+      color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+      break;
+    case 3:
+      color_type = PNG_COLOR_TYPE_RGB;
+      break;
+    case 4:
+    default:
+      color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+  }
+  png_set_IHDR(png_ptr, info_ptr, rgb->data_w(), rgb->data_h(), 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+  const uchar **row_pointers = new const uchar*[rgb->data_h()];
+  int ld = rgb->ld() ? rgb->ld() : rgb->d() * rgb->data_w();
+  for (int i=0; i < rgb->data_h(); i++) row_pointers[i] = (rgb->array + i*ld);
+  png_set_rows(png_ptr, info_ptr, (png_bytepp)row_pointers);
+  png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+  png_write_end(png_ptr, NULL);
+  user_flush_data(png_ptr);
+  png_destroy_write_struct(&png_ptr, &info_ptr);
+  delete[] row_pointers;
+  if (name) fputs("\"/></defs>\n", out_);
+  else fputs("\"/>\n", out_);
+}
+
+#endif // HAVE_LIBPNG
+
+#ifdef HAVE_LIBJPEG
+
+struct jpeg_client_data_struct {
+  JOCTET JPEG_BUFFER[50000];
+  size_t size;
+  svg_base64_t base64_data;
+};
+
+static void init_destination(jpeg_compress_struct *cinfo) {
+  jpeg_client_data_struct *client_data = (jpeg_client_data_struct*)(cinfo->client_data);
+  cinfo->dest->next_output_byte = client_data->JPEG_BUFFER;
+  cinfo->dest->free_in_buffer = client_data->size;
+}
+
+static size_t process_jpeg_chunk(jpeg_compress_struct *cinfo, size_t length) {
+  jpeg_client_data_struct *client_data = (jpeg_client_data_struct*)(cinfo->client_data);
+  JOCTET *data = client_data->JPEG_BUFFER;
+  size_t new_l = length;
+  if (length >= 3) {
+    new_l = write_by_3(data, length, &client_data->base64_data);
+    if (new_l) memmove(client_data->JPEG_BUFFER, data + length - new_l, new_l);
+  }
+  cinfo->dest->next_output_byte = client_data->JPEG_BUFFER + new_l;
+  cinfo->dest->free_in_buffer = client_data->size - new_l;
+  return new_l;
+}
+
+static boolean empty_output_buffer(jpeg_compress_struct *cinfo) {
+  jpeg_client_data_struct *client_data = (jpeg_client_data_struct*)(cinfo->client_data);
+  process_jpeg_chunk(cinfo, client_data->size);
+  return TRUE;
+}
+
+static void term_destination(jpeg_compress_struct *cinfo) {
+  jpeg_client_data_struct *client_data = (jpeg_client_data_struct*)(cinfo->client_data);
+  size_t new_l = process_jpeg_chunk(cinfo, client_data->size - cinfo->dest->free_in_buffer);
+  if (new_l) {
+    to_base64(client_data->JPEG_BUFFER, new_l, &client_data->base64_data);
+  }
+}
+
+void Fl_SVG_Graphics_Driver::define_rgb_jpeg(Fl_RGB_Image *rgb, const char *name, int x, int y) {
+  if (name) {
+    if (last_rgb_name_) free(last_rgb_name_);
+    last_rgb_name_ = strdup(name);
+  }
+  float f = rgb->data_w() > rgb->data_h() ? float(rgb->w()) / rgb->data_w(): float(rgb->h()) / rgb->data_h();
+  if (name) fprintf(out_, "<defs><image id=\"%s\" ", name);
+  else fprintf(out_, "<image x=\"%d\" y=\"%d\" ", x, y);
+  fprintf(out_, "width=\"%f\" height=\"%f\" href=\"data:image/jpeg;base64,\n", f*rgb->data_w(), f*rgb->data_h());
+  // Transforms the image into a stream of bytes in JPEG format,
+  // base64-encode this byte stream, and outputs the result to the svg FILE.
+  jpeg_compress_struct cinfo;
+  jpeg_error_mgr jerr;
+  jpeg_client_data_struct jpeg_client_data;
+  jpeg_client_data.size = sizeof(jpeg_client_data.JPEG_BUFFER);
+  cinfo.client_data = &jpeg_client_data;
+  cinfo.err = jpeg_std_error(&jerr);
+  jpeg_create_compress(&cinfo);
+  jpeg_destination_mgr jpeg_mgr;
+  jpeg_mgr.init_destination = init_destination;
+  jpeg_mgr.empty_output_buffer = empty_output_buffer;
+  jpeg_mgr.term_destination = term_destination;
+  cinfo.dest = &jpeg_mgr;
+  cinfo.image_width = rgb->data_w();
+  cinfo.image_height = rgb->data_h();
+  cinfo.input_components = rgb->d();  // 1 or 3
+  cinfo.in_color_space = rgb->d() == 3 ? JCS_RGB : JCS_GRAYSCALE;
+  jpeg_set_defaults(&cinfo);
+  jpeg_client_data.base64_data.svg = out_;
+  jpeg_client_data.base64_data.lline = 0;
+  jpeg_client_data.base64_data.lbuf = 0;
+  jpeg_start_compress(&cinfo, TRUE);
+  int ld = rgb->ld() ? rgb->ld() : rgb->data_w() * rgb->d();
+  JSAMPROW row_pointer[1];
+  while (cinfo.next_scanline < cinfo.image_height) {
+    row_pointer[0] = (uchar*)rgb->array + ld*cinfo.next_scanline;
+    jpeg_write_scanlines(&cinfo, row_pointer, 1);
+  }
+  jpeg_finish_compress(&cinfo);
+  jpeg_destroy_compress(&cinfo);
+  if (name) fputs("\"/></defs>\n", out_);
+  else fputs("\"/>\n", out_);
+}
+#endif // HAVE_LIBJPEG
+
+void Fl_SVG_Graphics_Driver::draw_rgb(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP, int cx, int cy) {
+#if defined(HAVE_LIBPNG)
+  char name[24];
+  bool need_clip = (cx || cy || WP != rgb->w() || HP != rgb->h());
+  void *p = (void*)*Fl_Graphics_Driver::id(rgb);
+  if (p) sprintf(name, "FLrgb%p", p); else name[0] = 0;
+  if (!p || !last_rgb_name_ || strcmp(name, last_rgb_name_) != 0) {
+    if (*name==0 && need_clip) push_clip(XP, YP, WP, HP);
+#if defined(HAVE_LIBJPEG)
+    if (rgb->d() == 3 || rgb->d() == 1) define_rgb_jpeg(rgb, *name ? name : NULL, XP-cx, YP-cy);
+    else
+#endif // HAVE_LIBJPEG
+      define_rgb_png(rgb, *name ? name : NULL, XP-cx, YP-cy);
+    if (*name==0 && need_clip) pop_clip();
+  }
+  if (*name) {
+    if (need_clip) push_clip(XP, YP, WP, HP);
+    fprintf(out_, "<use href=\"#%s\" x=\"%d\" y=\"%d\"/>\n", last_rgb_name_, XP-cx, YP-cy);
+    if (need_clip) pop_clip();
+  }
+#endif // HAVE_LIBPNG
+}
+
+void Fl_SVG_Graphics_Driver::draw_pixmap(Fl_Pixmap *pxm, int XP, int YP, int WP, int HP, int cx, int cy) {
+#if defined(HAVE_LIBPNG)
+  char name[24];
+  bool need_clip = (cx || cy || WP != pxm->w() || HP != pxm->h());
+  void *p = (void*)*Fl_Graphics_Driver::id(pxm);
+  if (p) sprintf(name, "FLpx%p", p); else name[0] = 0;
+  if (!p || !last_rgb_name_ || strcmp(name, last_rgb_name_) != 0) {
+    Fl_RGB_Image *rgb = new Fl_RGB_Image(pxm);
+    if (*name==0 && need_clip) push_clip(XP, YP, WP, HP);
+    define_rgb_png(rgb, *name ? name : NULL, XP-cx, YP-cy);
+    if (*name==0 && need_clip) pop_clip();
+    delete rgb;
+  }
+  if (*name) {
+    if (need_clip) push_clip(XP, YP, WP, HP);
+    fprintf(out_, "<use href=\"#%s\" x=\"%d\" y=\"%d\"/>\n", last_rgb_name_, XP-cx, YP-cy);
+    if (need_clip) pop_clip();
+  }
+#endif // HAVE_LIBPNG
+}
+
+void Fl_SVG_Graphics_Driver::draw_bitmap(Fl_Bitmap *bm, int XP, int YP, int WP, int HP, int cx, int cy) {
+#if defined(HAVE_LIBPNG)
+  char name[45];
+  bool need_clip = (cx || cy || WP != bm->w() || HP != bm->h());
+  void *p = (void*)*Fl_Graphics_Driver::id(bm);
+  if (p) sprintf(name, "FLbm%p%X", p, fl_color()); else name[0] = 0;
+  if (!p || !last_rgb_name_ || strcmp(name, last_rgb_name_) != 0) {
+    uchar R, G, B;
+    Fl::get_color(fl_color(), R, G, B);
+    uchar *data = new uchar[bm->data_w() * bm->data_h() * 4];
+    memset(data, 0, bm->data_w() * bm->data_h() * 4);
+    Fl_RGB_Image *rgb = new Fl_RGB_Image(data, bm->data_w(), bm->data_h(), 4);
+    rgb->alloc_array = 1;
+    int rowBytes = (bm->data_w()+7)>>3 ;
+    for (int j = 0; j < bm->data_h(); j++) {
+      const uchar *p = bm->array + j*rowBytes;
+      for (int i = 0; i < rowBytes; i++) {
+        uchar q = *p;
+        int last = bm->data_w() - 8*i; if (last > 8) last = 8;
+        for (int k=0; k < last; k++) {
+          if (q&1) {
+            uchar *r = (uchar*)rgb->array + j*bm->data_w()*4 + i*8*4 + k*4;
+            *r++ = R; *r++ = G; *r++ = B; *r = ~0;
+          }
+          q >>= 1;
+        }
+        p++;
+      }
+    }
+    if (*name==0 && need_clip) push_clip(XP, YP, WP, HP);
+    define_rgb_png(rgb, *name ? name : NULL, XP-cx, YP-cy);
+    if (*name==0 && need_clip) pop_clip();
+    delete rgb;
+  }
+  if (*name) {
+    if (need_clip) push_clip(XP, YP, WP, HP);
+    fprintf(out_, "<use href=\"#%s\" x=\"%d\" y=\"%d\"/>\n", last_rgb_name_, XP-cx, YP-cy);
+    if (need_clip) pop_clip();
+  }
+#endif // HAVE_LIBPNG
+}
+
+void Fl_SVG_Graphics_Driver::draw_image(const uchar* buf, int x, int y, int w, int h, int d, int l) {
+  if (d < 0) {
+    fprintf(out_, "<g transform=\"translate(%d,%d) scale(-1,1)\">\n", x, y);
+    x = -w; y = 0; buf -= (w-1)*abs(d);
+  }
+  if (l < 0) {
+    fprintf(out_, "<g transform=\"translate(%d,%d) scale(1,-1)\">\n", x, y);
+    x = 0; y = -h; buf -= (h-1)*abs(l);
+  }
+  Fl_RGB_Image *rgb = new Fl_RGB_Image(buf, w, h, abs(d), abs(l));
+  rgb->draw(x, y);
+  delete rgb;
+  if (d < 0) fprintf(out_, "</g>\n");
+  if (l < 0) fprintf(out_, "</g>\n");
+}
+
+void Fl_SVG_Graphics_Driver::draw_image(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d) {
+  uchar *buf = new uchar[w*h*d];
+  for (int j = 0; j < h; j++) {
+    cb(data, 0, j, w, buf + j*w*d);
+  }
+  draw_image(buf, x, y, w, h, d, 0);
+  delete [] buf;
+}
+
+struct mono_image_data {
+  const uchar *buf;
+  int d;
+  int l;
+};
+
+static void mono_image_cb(mono_image_data* data, int x, int y, int w, uchar* buf) {
+  for (int i = 0; i < w; i++)
+    *buf++ = *(data->buf + y*data->l + (x++)*data->d);
+}
+
+void Fl_SVG_Graphics_Driver::draw_image_mono(const uchar* buf, int x, int y, int w, int h, int d, int l) {
+  mono_image_data data;
+  data.buf = buf; data.d = d; data.l = (l?l:w*d);
+  draw_image((Fl_Draw_Image_Cb)mono_image_cb, (void*)&data, x, y, w, h, 1);
+}
+
+void Fl_SVG_Graphics_Driver::draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int x, int y, int w, int h, int d) {
+  uchar *buf = new uchar[w*h*d];
+  for (int j = 0; j < h; j++) {
+    cb(data, 0, j, w, buf + j*w*d);
+  }
+  draw_image_mono(buf, x, y, w, h, d, 0);
+  delete[] buf;
+}
+
+void Fl_SVG_Graphics_Driver::push_clip(int x, int y, int w, int h) {
+  Clip * c=new Clip();
+  clip_box(x,y,w,h,c->x,c->y,c->w,c->h);
+  c->prev=clip_;
+  sprintf(c->Id, "FLclip%d", clip_count_++);
+  clip_=c;
+  fprintf(out_, "<clipPath id=\"%s\"><rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"/></clipPath><g clip-path=\"url(#%s)\">\n",
+          c->Id, clip_->x , clip_->y , clip_->w, clip_->h, c->Id);
+}
+
+void Fl_SVG_Graphics_Driver::push_no_clip() {
+  Clip * c=clip_;
+  while (c) {
+    fprintf(out_, "</g>");
+    c = c->prev;
+  }
+  c=new Clip();
+  c->prev=clip_;
+  strcpy(c->Id, "none"); // mark of no_clip
+  clip_=c;
+  fprintf(out_, "<g clip-path=\"none\">\n");
+}
+
+void Fl_SVG_Graphics_Driver::pop_clip() {
+  Clip *c;
+  bool was_no_clip = clip_ && (strcmp(clip_->Id, "none") == 0);
+  fprintf(out_, "</g>");
+  if (clip_) {
+    c = clip_;
+    clip_ = clip_->prev;
+    delete c;
+  }
+  if (was_no_clip) {
+    Clip *next = NULL;
+    c=clip_;
+    while (c) {
+      Clip *c2 = new Clip(*c);
+      c2->prev = next;
+      next = c2;
+      c = c->prev;
+    }
+    while (next) {
+      fprintf(out_, "<g clip-path=\"url(#%s)\">", next->Id);
+      c = next->prev;
+      delete next;
+      next = c;
+    }
+  }
+  fprintf(out_, "\n");
+}
+
+int Fl_SVG_Graphics_Driver::clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H) {
+  if (!clip_) {
+    X = x; Y = y; W = w; H = h;
+    return 0;
+  }
+  if (clip_->w < 0) {
+    X = x; Y = y; W = w; H = h;
+    return 1;
+  }
+  int ret = 0;
+  if (x > (X=clip_->x)) {X=x; ret=1;}
+  if (y > (Y=clip_->y)) {Y=y; ret=1;}
+  if ((x+w) < (clip_->x+clip_->w)) {
+    W=x+w-X;
+    ret=1;
+  }else
+    W = clip_->x + clip_->w - X;
+  if(W<0){
+    W=0;
+    return 1;
+  }
+  if ((y+h) < (clip_->y+clip_->h)) {
+    H=y+h-Y;
+    ret=1;
+  }else
+    H = clip_->y + clip_->h - Y;
+  if(H<0){
+    W=0;
+    H=0;
+    return 1;
+  }
+  return ret;
+}
+
+int Fl_SVG_Graphics_Driver::not_clipped(int x, int y, int w, int h) {
+  if (!clip_) return 1;
+  if (clip_->w < 0) return 1;
+  int X = 0, Y = 0, W = 0, H = 0;
+  clip_box(x, y, w, h, X, Y, W, H);
+  if (W) return 1;
+  return 0;
+}
+
+void Fl_SVG_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2) {
+  fprintf(out_, "<path d=\"M %d %d L %d %d L %d %d z\" fill=\"rgb(%u,%u,%u)\" />\n",
+          x0, y0, x1, y1, x2, y2, red_, green_, blue_);
+}
+
+void Fl_SVG_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
+  fprintf(out_, "<path d=\"M %d %d L %d %d L %d %d L %d %d z\" fill=\"rgb(%u,%u,%u)\" />\n",
+          x0, y0, x1, y1, x2, y2, x3, y3, red_, green_, blue_);
+}
+
+void Fl_SVG_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
+  fprintf(out_, "<path d=\"M %d %d L %d %d L %d %d L %d %d z\" fill=\"none\" stroke=\"rgb(%u,%u,%u)\" "
+          "stroke-width=\"%d\" stroke-linejoin=\"%s\" stroke-linecap=\"%s\" stroke-dasharray=\"%s\"/>\n",
+          x0, y0, x1, y1, x2, y2, x3, y3, red_, green_, blue_, width_, linejoin_, linecap_, dasharray_);
+}
+
+void Fl_SVG_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2) {
+  fprintf(out_, "<path d=\"M %d %d L %d %d L %d %d z\" fill=\"none\" stroke=\"rgb(%u,%u,%u)\" "
+          "stroke-width=\"%d\" stroke-linejoin=\"%s\" stroke-linecap=\"%s\" stroke-dasharray=\"%s\"/>\n",
+          x0, y0, x1, y1, x2, y2, red_, green_, blue_, width_, linejoin_, linecap_, dasharray_);
+}
+
+void Fl_SVG_Graphics_Driver::transformed_vertex0(float x, float y) {
+  if (!n || x != p[n-1].x || y != p[n-1].y) {
+    if (n >= p_size) {
+      p_size = p ? 2*p_size : 16;
+      p = (XPOINT*)realloc((void*)p, p_size*sizeof(*p));
+    }
+   p[n].x = x;
+   p[n].y = y;
+   n++;
+   }
+}
+
+void Fl_SVG_Graphics_Driver::transformed_vertex(double xf, double yf) {
+  transformed_vertex0(float(xf), float(yf));
+}
+
+void Fl_SVG_Graphics_Driver::vertex(double x,double y) {
+  transformed_vertex0(float(x*m.a + y*m.c + m.x), float(x*m.b + y*m.d + m.y));
+}
+
+void Fl_SVG_Graphics_Driver::end_points() {
+  for (int i=0; i<n; i++) {
+    fprintf(out_, "<path d=\"M %f %f L %f %f\" fill=\"none\" stroke=\"rgb(%u,%u,%u)\" stroke-width=\"%d\" />\n",
+        p[i].x, p[i].y, p[i].x, p[i].y, red_, green_, blue_, width_);
+  }
+}
+
+void Fl_SVG_Graphics_Driver::end_line() {
+  if (n < 2) {
+    end_points();
+    return;
+  }
+  if (n<=1) return;
+  fprintf(out_, "<path d=\"M %f %f", p[0].x, p[0].y);
+  for (int i=1; i<n; i++)
+    fprintf(out_, " L %f %f", p[i].x, p[i].y);
+  fprintf(out_, "\" fill=\"none\" stroke=\"rgb(%u,%u,%u)\" stroke-width=\"%d\" stroke-dasharray=\"%s\" stroke-linecap=\"%s\" stroke-linejoin=\"%s\" />\n",
+          red_, green_, blue_, width_, dasharray_, linecap_, linejoin_);
+}
+
+void Fl_SVG_Graphics_Driver::fixloop() {  // remove equal points from closed path
+  while (n>2 && p[n-1].x == p[0].x && p[n-1].y == p[0].y) n--;
+}
+
+void Fl_SVG_Graphics_Driver::end_loop() {
+  fixloop();
+  if (n>2) transformed_vertex((float)p[0].x, (float)p[0].y);
+  end_line();
+}
+
+void Fl_SVG_Graphics_Driver::end_polygon() {
+  fixloop();
+  if (n < 3) {
+    end_line();
+    return;
+  }
+  if (n<=1) return;
+  fprintf(out_, "<path d=\"M %f %f", p[0].x, p[0].y);
+  for (int i=1; i<n; i++)
+    fprintf(out_, " L %f %f", p[i].x, p[i].y);
+  fprintf(out_, " z\" fill=\"rgb(%u,%u,%u)\" />\n", red_, green_, blue_);
+}
+
+void Fl_SVG_Graphics_Driver::circle(double x, double y, double r) {
+  double xt = transform_x(x,y);
+  double yt = transform_y(x,y);
+  double rx = r * (m.c ? sqrt(m.a*m.a+m.c*m.c) : fabs(m.a));
+  double ry = r * (m.b ? sqrt(m.b*m.b+m.d*m.d) : fabs(m.d));
+  int llx = (int)rint(xt-rx);
+  int w = (int)rint(xt+rx)-llx;
+  int lly = (int)rint(yt-ry);
+  int h = (int)rint(yt+ry)-lly;
+  fprintf(out_, "<circle cx=\"%g\" cy=\"%g\" r=\"%g\"", xt, yt, (w+h)*0.25f);
+  if (what == POLYGON)
+    fprintf(out_, " fill");
+  else
+    fprintf(out_, " fill=\"none\" stroke-width=\"%d\" stroke-dasharray=\"%s\" stroke-linecap=\"%s\" stroke", width_, dasharray_,linecap_);
+  fprintf(out_, "=\"rgb(%u,%u,%u)\" />\n", red_, green_, blue_);
+}
+
+void Fl_SVG_Graphics_Driver::begin_complex_polygon() {
+  begin_polygon();
+  gap_ = 0;
+}
+
+void Fl_SVG_Graphics_Driver::gap() {
+  while (n>gap_+2 && p[n-1].x == p[gap_].x && p[n-1].y == p[gap_].y) n--;
+  if (n > gap_+2) {
+    transformed_vertex((float)p[gap_].x, (float)p[gap_].y);
+    gap_ = n;
+  } else {
+    n = gap_;
+  }
+}
+
+void Fl_SVG_Graphics_Driver::end_complex_polygon() {
+  gap();
+  if (n < 3) {
+    end_line();
+    return;
+  }
+  if (n<=1) return;
+  fprintf(out_, "<path d=\"M %f %f", p[0].x, p[0].y);
+  for (int i=1; i<n; i++)
+    fprintf(out_, " L %f %f", p[i].x, p[i].y);
+  fprintf(out_, " z\" fill=\"rgb(%u,%u,%u)\" />\n", red_, green_, blue_);
+}
+
+void Fl_SVG_Graphics_Driver::arc(int x, int y, int w, int h, double a1, double a2) {
+  arc_pie('A', x, y, w, h, a1, a2);
+}
+
+void Fl_SVG_Graphics_Driver::pie(int x, int y, int w, int h, double a1, double a2) {
+  arc_pie('P', x, y, w, h, a1, a2);
+}
+
+void Fl_SVG_Graphics_Driver::arc_pie(char AorP, int x, int y, int w, int h, double a1, double a2) {
+  // This implementation was constructed as follows:
+  // - follow Fl_Quartz_Graphics_Driver::arc(int x,...).
+  // which applies a translation, a scaling, and then calls
+  //     CGContextAddArc(gc_, 0, 0, 0.5, a1, a2, 1);
+  // to draw an arc of a circle given its center, its radius, and starting and ending angles
+  // - consider https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+  // which gives the equations that transform the center parameterization used by
+  // CGContextAddArc() to the endpoint parameterization used by the "elliptical arc curve" command of SVG (A).
+  if (w <= 0 || h <= 0) return;
+  bool full = fabs(a1-a2) == 360; // case of full circle/disk
+  a1 = (-a1)/180.0f*M_PI; a2 = (-a2)/180.0f*M_PI;
+  float cx = x + 0.5f*w /*- 0.5f*/, cy = y + 0.5f*h - 0.5f;
+  double r = (w!=h ? 0.5 : (w+h)*0.25f-0.5f);
+  float stroke_width = width_;
+  float sx, sy;
+  if (w != h) {
+    sx = w-1; sy = h-1;
+    stroke_width /= ((sx+sy)/2);
+  } else {
+    sx = sy = 2*r;
+    stroke_width /= sx;
+  }
+  fprintf(out_, "<g transform=\"translate(%f,%f) scale(%f,%f)\">\n", cx, cy, sx, sy);
+  if (AorP == 'A') compute_dasharray((sx+sy)/2, user_dash_array_);
+  if (full) {
+    fprintf(out_, "<circle cx=\"0\" cy=\"0\" r=\"0.5\" style=\"fill");
+    if (AorP == 'A')
+      fprintf(out_, ":none;stroke-width:%f;stroke-linecap:%s;stroke-dasharray:%s;stroke", stroke_width, linecap_, dasharray_);
+  } else {
+    double x1 = 0.5*cos(a1), y1 = 0.5 * sin(a1);
+    double x2 = 0.5*cos(a2), y2 = 0.5 * sin(a2);
+    int fA = fabs(a2-a1) > M_PI ? 1 : 0;
+    if (AorP == 'A')
+      fprintf(out_, "<path d=\"M %f,%f A 0.5,0.5 0 %d,0 %f,%f\" "
+              "style=\"fill:none;stroke-width:%f;stroke-linecap:%s;stroke-dasharray:%s;stroke",
+              x1, y1, fA, x2, y2, stroke_width, linecap_, dasharray_);
+    else
+      fprintf(out_, "<path d=\"M 0,0 L %f,%f A 0.5,0.5 0 %d,0 %f,%f z\" style=\"fill",
+              x1, y1, fA, x2, y2);
+  }
+  fprintf(out_, ":rgb(%u,%u,%u)\"/>\n</g>\n", red_, green_, blue_);
+  if (AorP == 'A') compute_dasharray(1., user_dash_array_);
+}
+
+#else
+
+Fl_SVG_File_Surface::Fl_SVG_File_Surface(int w, int h, FILE *f) : Fl_Widget_Surface(NULL) {
+  width_ = height_ = 0;
+}
+Fl_SVG_File_Surface::~Fl_SVG_File_Surface() {}
+int Fl_SVG_File_Surface::close() {return 0;}
+FILE *Fl_SVG_File_Surface::file() {return NULL;}
+void Fl_SVG_File_Surface::origin(int x, int y) {}
+void Fl_SVG_File_Surface::translate(int x, int y) {}
+void Fl_SVG_File_Surface::untranslate() {}
+int Fl_SVG_File_Surface::printable_rect(int *w, int *h) {return 0;}
+
+#endif // FLTK_USE_SVG
+
+//
+// End of "$Id$".
+//
diff --git a/src/fl_images_core.cxx b/src/fl_images_core.cxx
index 624b6f4c37270ae2fabc1c41242f59d6c5bab36a..3df7fcd27fc17c77d5de4a759b2727c85e82dec5 100644
--- a/src/fl_images_core.cxx
+++ b/src/fl_images_core.cxx
@@ -91,7 +91,7 @@ fl_check_images(const char *name,		// I - Filename
     return new Fl_JPEG_Image(name);
 #endif // HAVE_LIBJPEG
 
-#ifdef FLTK_USE_NANOSVG
+#ifdef FLTK_USE_SVG
 #  if defined(HAVE_LIBZ)
   if (header[0] == 0x1f && header[1] == 0x8b) { // denotes gzip'ed data
     int fd = fl_open_ext(name, 1, 0);
@@ -106,7 +106,7 @@ fl_check_images(const char *name,		// I - Filename
   if ( (headerlen > 5 && memcmp(header, "<?xml", 5) == 0) ||
       memcmp(header, "<svg", 4) == 0)
     return new Fl_SVG_Image(name);
-#endif // FLTK_USE_NANOSVG
+#endif // FLTK_USE_SVG
 
   return 0;
 }
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 886d18813d93d853f33cbeae38732824375f1dad..6e996a5a3ecea51da357e1c5eb096e949c80476d 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -59,7 +59,7 @@ CREATE_EXAMPLE(color_chooser color_chooser.cxx fltk ANDROID_OK)
 CREATE_EXAMPLE(cursor cursor.cxx fltk ANDROID_OK)
 CREATE_EXAMPLE(curve curve.cxx fltk ANDROID_OK)
 CREATE_EXAMPLE(demo demo.cxx fltk)
-CREATE_EXAMPLE(device device.cxx fltk)
+CREATE_EXAMPLE(device device.cxx  "fltk;fltk_images")
 CREATE_EXAMPLE(doublebuffer doublebuffer.cxx fltk ANDROID_OK)
 CREATE_EXAMPLE(editor editor.cxx fltk ANDROID_OK)
 CREATE_EXAMPLE(fast_slow fast_slow.fl fltk ANDROID_OK)
diff --git a/test/Makefile b/test/Makefile
index 66a25825c126258d9a50d8ea2ed456439091ca1f..4dceb9d45c4d23efa82b503a0d59af8fa22cc094 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -370,9 +370,9 @@ demo$(EXEEXT): demo.o
 	$(OSX_ONLY) mkdir -p demo.app/Contents/Resources
 	$(OSX_ONLY) cp -f demo.menu demo.app/Contents/Resources/
 
-device$(EXEEXT): device.o $(IMGLIBNAME)
+device$(EXEEXT): device.o
 	echo Linking $@...
-	$(CXX) $(ARCHFLAGS) $(CXXFLAGS) $(LDFLAGS) device.o -o $@ $(LINKFLTKIMG) $(LDLIBS)
+	$(CXX) $(ARCHFLAGS) $(CXXFLAGS) $(LDFLAGS) device.o -o $@ $(LINKFLTK) $(IMAGELIBS) $(LDLIBS)
 	$(OSX_ONLY) ../fltk-config --post $@
 
 doublebuffer$(EXEEXT): doublebuffer.o
diff --git a/test/device.cxx b/test/device.cxx
index 6ddda876127e436943dcb7473eb5c558ba535a42..e242f6ae687b37c914a0a4f60540f1cf3bd93302 100644
--- a/test/device.cxx
+++ b/test/device.cxx
@@ -3,7 +3,7 @@
 //
 // Device test program for the Fast Light Tool Kit (FLTK).
 //
-// Copyright 1998-2016 by Roman Kantor and others.
+// Copyright 1998-2020 by Roman Kantor and others.
 //
 // This library is free software. Distribution and use rights are outlined in
 // the file "COPYING" which should have been included with this file.  If this
@@ -18,7 +18,6 @@
 
 #include <math.h>
 #include <FL/Fl.H>
-
 #include <FL/Fl_Overlay_Window.H>
 #include <FL/Fl_Light_Button.H>
 #include <FL/Fl_Radio_Round_Button.H>
@@ -28,15 +27,12 @@
 #include <FL/Fl_Pixmap.H>
 #include <FL/Fl_Bitmap.H>
 #include <FL/Fl_Round_Button.H>
-
-
 #include <FL/Fl_Printer.H>
 #include <FL/Fl_PostScript.H>
 #include <FL/Fl_Copy_Surface.H>
 #include <FL/Fl_Image_Surface.H>
-
-#include <FL/Fl_File_Chooser.H>
-#include <FL/fl_draw.H>
+#include <FL/Fl_Native_File_Chooser.H>
+#include <FL/Fl_SVG_File_Surface.H>
 
 
 #define sorceress_width 75
@@ -630,6 +626,59 @@ void copy(Fl_Widget *, void *data) {
     } else if (err > 1 && err_message) {fl_alert("%s", err_message); delete[] err_message;}
     delete p;
   }
+  
+  if (strcmp(operation, "Fl_EPS_File_Surface") == 0) {
+    Fl_Native_File_Chooser fnfc;
+    fnfc.title("Save a .eps file");
+    fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
+    fnfc.filter("EPS\t*.eps\n");
+    fnfc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT);
+    if (!fnfc.show() ) {
+      FILE *eps = fl_fopen(fnfc.filename(), "w");
+      if (eps) {
+        int ww, wh;
+        if (target->as_window())  {
+          ww = target->as_window()->decorated_w();
+          wh = target->as_window()->decorated_h();
+        } else {
+          ww = target->w();
+          wh = target->h();
+        }
+        Fl_EPS_File_Surface p(ww, wh, eps);
+        if (target->as_window()) p.draw_decorated_window(target->as_window());
+        else p.draw(target);
+        //p.close();
+      }
+      fclose(eps);
+    }
+  }
+
+  if (strcmp(operation, "Fl_SVG_File_Surface") == 0) {
+    Fl_Native_File_Chooser fnfc;
+    fnfc.title("Save a .svg file");
+    fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
+    fnfc.filter("SVG\t*.svg\n");
+    fnfc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT);
+    if (!fnfc.show() ) {
+      FILE *svg = fl_fopen(fnfc.filename(), "w");
+      if (svg) {
+        int ww, wh;
+        if (target->as_window())  {
+          ww = target->as_window()->decorated_w();
+          wh = target->as_window()->decorated_h();
+        } else {
+          ww = target->w();
+          wh = target->h();
+        }
+        Fl_SVG_File_Surface surface(ww, wh, svg);
+        if (surface.file()) {
+          if (target->as_window()) surface.draw_decorated_window(target->as_window());
+          else surface.draw(target);
+          surface.close();
+        }
+      }
+    }
+  }
 }
 
 
@@ -664,59 +713,59 @@ void operation_cb(Fl_Widget* wid, void *data)
 
 int main(int argc, char ** argv) {
   
-  //Fl::scheme("plastic");  
+  Fl::scheme("plastic");  
   
-  Fl_Window * w2 = new Fl_Window(500,560,"Graphics test");
+  Fl_Window * w2 = new Fl_Window(500,568,"Graphics test");
   
-  Fl_Group *c2 =new Fl_Group(3, 43, 494, 514 );
+  Fl_Group *c2 =new Fl_Group(3, 56, 494, 514 );
   
-  new MyWidget(10,140);
-  new MyWidget2(110,80);
-  new MyWidget3(220,140);
-  new MyWidget4(330,70);
-  new MyWidget5(140,270);
+  new MyWidget(10,140+16);
+  new MyWidget2(110,80+16);
+  new MyWidget3(220,140+16);
+  new MyWidget4(330,70+16);
+  new MyWidget5(140,270+16);
   
   make_image();
   Fl_RGB_Image *rgb = new Fl_RGB_Image(image, width, height, 4);
-  My_Button b_rgb(10,245,100,100,"RGB with alpha");
+  My_Button b_rgb(10,245+16,100,100,"RGB with alpha");
   b_rgb.image(rgb);
   
-  My_Button b_pixmap(10,345,100,100,"Pixmap");
+  My_Button b_pixmap(10,345+16,100,100,"Pixmap");
   Fl_Pixmap *pixmap = new Fl_Pixmap(porsche_xpm);
   b_pixmap.image(pixmap);
   
-  My_Button b_bitmap(10,445,100,100,"Bitmap");
+  My_Button b_bitmap(10,445+16,100,100,"Bitmap");
   b_bitmap.labelcolor(FL_GREEN);
   b_bitmap.image(new Fl_Bitmap(sorceress_bits,sorceress_width,sorceress_height));
   
-  new Fl_Clock(360,230,120,120);
+  new Fl_Clock(360,230+16,120,120);
   Fl_Return_Button * ret = new Fl_Return_Button (360, 360, 120,30, "Return");
   ret->deactivate();
-  Fl_Button but1(360, 390, 30, 30, "@->|");
+  Fl_Button but1(360, 390+16, 30, 30, "@->|");
   but1.labelcolor(FL_DARK3);
-  Fl_Button but2(390, 390, 30, 30, "@UpArrow");
+  Fl_Button but2(390, 390+16, 30, 30, "@UpArrow");
   but2.labelcolor(FL_DARK3);
-  Fl_Button but3(420, 390, 30, 30, "@DnArrow");
+  Fl_Button but3(420, 390+16, 30, 30, "@DnArrow");
   but3.labelcolor(FL_DARK3);
-  Fl_Button but4(450, 390, 30, 30, "@+");
+  Fl_Button but4(450, 390+16, 30, 30, "@+");
   but4.labelcolor(FL_DARK3);
-  Fl_Button but5(360, 425, 120, 30, "Hello, World");
+  Fl_Button but5(360, 425+16, 120, 30, "Hello, World");
   but5.labelfont(FL_BOLD|FL_ITALIC);
   but5.labeltype(FL_SHADOW_LABEL);
   but5.box(FL_ROUND_UP_BOX);
   
-  Fl_Button but6(360, 460, 120, 30, "Plastic");
+  Fl_Button but6(360, 460+16, 120, 30, "Plastic");
   but6.box(FL_PLASTIC_UP_BOX);
   
   Fl_Group *group;
-  { Fl_Group* o = new Fl_Group(360, 495, 120, 40); group=o;
+  { Fl_Group* o = new Fl_Group(360, 495+16, 120, 40); group=o;
     o->box(FL_UP_BOX);
-    { Fl_Group* o = new Fl_Group(365, 500, 110, 30);
+    { Fl_Group* o = new Fl_Group(365, 500+16, 110, 30);
       o->box(FL_THIN_UP_FRAME);
-      { Fl_Round_Button* o = new Fl_Round_Button(365, 500, 40, 30, "rad");
+      { Fl_Round_Button* o = new Fl_Round_Button(365, 500+16, 40, 30, "rad");
         o->value(1);
       }
-      { Fl_Check_Button* o = new Fl_Check_Button(410, 500, 60, 30, "check");
+      { Fl_Check_Button* o = new Fl_Check_Button(410, 500+16, 60, 30, "check");
         o->value(1);
         
       }
@@ -725,7 +774,7 @@ int main(int argc, char ** argv) {
     o->end();
     o->deactivate();
   }
-  Fl_Box tx(120,492,230,50,"Background is not printed because\nencapsulating group, which we are\n printing, has not set the box type");
+  Fl_Box tx(120,492+16,230,50,"Background is not printed because\nencapsulating group, which we are\n printing, has not set the box type");
   tx.box(FL_SHADOW_BOX);
   tx.labelsize(12);
   
@@ -734,7 +783,7 @@ int main(int argc, char ** argv) {
   c2->end();
   
   Fl_Radio_Round_Button *rb;
-  Fl_Window *w3 = new Fl_Window(2,5,w2->w()-10,60);
+  Fl_Window *w3 = new Fl_Window(2,5,w2->w()-10,73);
   w3->box(FL_DOWN_BOX);
   Fl_Group *g1 = new Fl_Group(w3->x(),w3->y(),w3->w(),w3->h());
   rb = new Fl_Radio_Round_Button(5,5,150,12, "Fl_Image_Surface"); 
@@ -742,6 +791,8 @@ int main(int argc, char ** argv) {
   rb = new Fl_Radio_Round_Button(5,18,150,12, "Fl_Copy_Surface"); rb->callback(operation_cb, NULL); rb->labelsize(12);
   rb = new Fl_Radio_Round_Button(5,31,150,12, "Fl_Printer"); rb->callback(operation_cb, NULL); rb->labelsize(12);
   rb = new Fl_Radio_Round_Button(5,44,150,12, "Fl_PostScript_File_Device"); rb->callback(operation_cb, NULL); rb->labelsize(12);
+  rb = new Fl_Radio_Round_Button(5,57,150,12, "Fl_EPS_File_Surface"); rb->callback(operation_cb, NULL); rb->labelsize(12);
+  rb = new Fl_Radio_Round_Button(170,57,150,12, "Fl_SVG_File_Surface"); rb->callback(operation_cb, NULL); rb->labelsize(12);
   g1->end();
   
   Fl_Group *g2 = new Fl_Group(w3->x(),w3->y(),w3->w(),w3->h());
diff --git a/test/pixmap_browser.cxx b/test/pixmap_browser.cxx
index 7d2ea3c3c1a01982e747f8ddbefe34ccef53329c..b2611703b461bb53948dc5f7d4ab9ebc89509aea 100644
--- a/test/pixmap_browser.cxx
+++ b/test/pixmap_browser.cxx
@@ -27,7 +27,8 @@
 #include <errno.h>
 #include <FL/Fl_File_Chooser.H>
 #include <FL/fl_message.H>
-
+#include <FL/Fl_SVG_File_Surface.H>
+#include <FL/Fl_Native_File_Chooser.H>
 Fl_Box *b;
 Fl_Double_Window *w;
 Fl_Shared_Image *img;
@@ -77,16 +78,17 @@ void file_cb(const char *n) {
 void button_cb(Fl_Widget *,void *) {
   fl_file_chooser_callback(file_cb);
   const char *fname = fl_file_chooser("Image file?","*.{bm,bmp,gif,jpg,pbm,pgm,png,ppm,xbm,xpm"
-#ifdef FLTK_USE_NANOSVG
+#ifdef FLTK_USE_SVG
                                       ",svg"
 #ifdef HAVE_LIBZ
                                       ",svgz"
-#endif
-#endif
+#endif // HAVE_LIBZ
+#endif // FLTK_USE_SVG
                                       "}", name);
   puts(fname ? fname : "(null)"); fflush(stdout);
   fl_file_chooser_callback(0);
 }
+
 void print_cb(Fl_Widget *widget, void *) {
   Fl_Printer printer;
   int width, height;
@@ -101,6 +103,19 @@ void print_cb(Fl_Widget *widget, void *) {
   printer.end_page();
   printer.end_job();
 }
+   
+void svg_cb(Fl_Widget *widget, void *) {
+  Fl_Native_File_Chooser fnfc;
+  fnfc.title("Pick a .svg file");
+  fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
+  fnfc.filter("SVG\t*.svg\n");
+  fnfc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT);
+  if (fnfc.show() ) return;
+  FILE *svg = fl_fopen(fnfc.filename(), "w");
+  Fl_SVG_File_Surface surf(widget->window()->decorated_w(), widget->window()->decorated_h(), svg);
+  surf.draw_decorated_window(widget->window());
+  surf.close();
+}
 
 int dvisual = 0;
 int arg(int, char **argv, int &i) {
@@ -126,6 +141,8 @@ int main(int argc, char **argv) {
   window.resizable(b);
   Fl_Button print(300,425,50,25,"Print");
   print.callback(print_cb);
+  Fl_Button svg(190,425,100,25,"save as SVG");
+  svg.callback(svg_cb);
 
   window.show(argc,argv);
   return Fl::run();